From 4fe5688d29e4e72d9acdd519413b12852d6daca2 Mon Sep 17 00:00:00 2001 From: Shirait Date: Wed, 30 May 2007 10:22:28 +0000 Subject: [PATCH] add SD driver and FATFS library (tentative) git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/twl_wrapsdk/trunk@96 4ee2a332-4b2b-5046-8439-1ba90f034370 --- .../buildsetup/ioreg_sp/io_register_list.csv | 42 + build/libraries/devices/Makefile | 34 + build/libraries/devices/sdmc/ARM7/Makefile | 66 + build/libraries/devices/sdmc/ARM7/drsdmc.c | 686 +++ build/libraries/devices/sdmc/ARM7/sdif.c | 1188 +++++ build/libraries/devices/sdmc/ARM7/sdif_ip.h | 349 ++ build/libraries/devices/sdmc/ARM7/sdif_reg.h | 109 + build/libraries/devices/sdmc/ARM7/sdmc.c | 2748 ++++++++++++ .../libraries/devices/sdmc/ARM7/sdmc_config.h | 85 + build/libraries/devices/sdmc/Makefile | 34 + build/libraries/fatfs/ARM7/Makefile | 74 + build/libraries/fatfs/ARM7/apickdsk.c | 1346 ++++++ build/libraries/fatfs/ARM7/apicnfig.c | 450 ++ build/libraries/fatfs/ARM7/apideltr.c | 249 ++ build/libraries/fatfs/ARM7/apienum.c | 500 +++ build/libraries/fatfs/ARM7/apifilio.c | 1411 ++++++ build/libraries/fatfs/ARM7/apifilmv.c | 292 ++ build/libraries/fatfs/ARM7/apifrmat.c | 783 ++++ build/libraries/fatfs/ARM7/apigetwd.c | 188 + build/libraries/fatfs/ARM7/apigfrst.c | 307 ++ build/libraries/fatfs/ARM7/apiinfo.c | 286 ++ build/libraries/fatfs/ARM7/apiinit.c | 510 +++ build/libraries/fatfs/ARM7/apimkdir.c | 307 ++ build/libraries/fatfs/ARM7/apirealt.c | 1090 +++++ build/libraries/fatfs/ARM7/apiregrs.c | 1139 +++++ build/libraries/fatfs/ARM7/apisetwd.c | 146 + build/libraries/fatfs/ARM7/apistat.c | 194 + build/libraries/fatfs/ARM7/apiwrite.c | 828 ++++ build/libraries/fatfs/ARM7/appcmdsh.c | 2473 ++++++++++ build/libraries/fatfs/ARM7/appdemo.c | 26 + build/libraries/fatfs/ARM7/attach.c | 274 ++ build/libraries/fatfs/ARM7/csascii.c | 525 +++ build/libraries/fatfs/ARM7/csjis.c | 910 ++++ build/libraries/fatfs/ARM7/csjistab.c | 3970 +++++++++++++++++ build/libraries/fatfs/ARM7/csstrtab.c | 991 ++++ build/libraries/fatfs/ARM7/csunicod.c | 723 +++ build/libraries/fatfs/ARM7/drdefault.c | 158 + build/libraries/fatfs/ARM7/drdefault.h | 11 + build/libraries/fatfs/ARM7/drfile.c | 295 ++ build/libraries/fatfs/ARM7/portconf.h | 50 + build/libraries/fatfs/ARM7/portio.c | 418 ++ build/libraries/fatfs/ARM7/portkern.c | 636 +++ build/libraries/fatfs/ARM7/portmain.c | 52 + build/libraries/fatfs/ARM7/prapipro.c | 309 ++ build/libraries/fatfs/ARM7/prblock.c | 1188 +++++ build/libraries/fatfs/ARM7/prfsapi.c | 484 ++ build/libraries/fatfs/ARM7/prfscore.c | 1263 ++++++ build/libraries/fatfs/ARM7/prfsnvio.c | 406 ++ build/libraries/fatfs/ARM7/prfstest.c | 1786 ++++++++ build/libraries/fatfs/ARM7/readme.txt | 14 + build/libraries/fatfs/ARM7/revhst40.txt | 2063 +++++++++ build/libraries/fatfs/ARM7/rtdevio.c | 533 +++ build/libraries/fatfs/ARM7/rtdrobj.c | 1424 ++++++ build/libraries/fatfs/ARM7/rtfat16.c | 360 ++ build/libraries/fatfs/ARM7/rtfat32.c | 502 +++ build/libraries/fatfs/ARM7/rtfatxx.c | 1071 +++++ build/libraries/fatfs/ARM7/rtkernfn.c | 522 +++ build/libraries/fatfs/ARM7/rtlowl.c | 711 +++ build/libraries/fatfs/ARM7/rtnvfat.c | 441 ++ build/libraries/fatfs/ARM7/rttermin.c | 235 + build/libraries/fatfs/ARM7/rtutbyte.c | 121 + build/libraries/fatfs/ARM7/rtutil.c | 304 ++ build/libraries/fatfs/ARM7/rtvfat.c | 1449 ++++++ build/libraries/fatfs/ARM7/winhdisk.c | 452 ++ build/libraries/fatfs/ARM7/winmkrom.c | 661 +++ build/libraries/fatfs/ARM7/winsplsh.c | 378 ++ build/libraries/fatfs/Makefile | 34 + include/twl/devices/sdmc/ARM7/sdmc.h | 108 + include/twl/fatfs/ARM7/attach.h | 25 + include/twl/fatfs/ARM7/csstrtab.h | 446 ++ include/twl/fatfs/ARM7/drfile.h | 40 + include/twl/fatfs/ARM7/portconf.h | 50 + include/twl/fatfs/ARM7/prfs.h | 190 + include/twl/fatfs/ARM7/rtfs.h | 1402 ++++++ .../twl/fatfs/ARM7/rtfs_naming_convention.h | 57 + include/twl/fatfs/ARM7/rtfs_target_os.h | 74 + include/twl/fatfs/ARM7/rtfsapi.h | 25 + include/twl/fatfs/ARM7/rtfsconf.h | 153 + include/twl/fatfs/ARM7/rtfspro.h | 53 + 79 files changed, 46287 insertions(+) create mode 100644 build/libraries/devices/Makefile create mode 100644 build/libraries/devices/sdmc/ARM7/Makefile create mode 100644 build/libraries/devices/sdmc/ARM7/drsdmc.c create mode 100644 build/libraries/devices/sdmc/ARM7/sdif.c create mode 100644 build/libraries/devices/sdmc/ARM7/sdif_ip.h create mode 100644 build/libraries/devices/sdmc/ARM7/sdif_reg.h create mode 100644 build/libraries/devices/sdmc/ARM7/sdmc.c create mode 100644 build/libraries/devices/sdmc/ARM7/sdmc_config.h create mode 100644 build/libraries/devices/sdmc/Makefile create mode 100644 build/libraries/fatfs/ARM7/Makefile create mode 100644 build/libraries/fatfs/ARM7/apickdsk.c create mode 100644 build/libraries/fatfs/ARM7/apicnfig.c create mode 100644 build/libraries/fatfs/ARM7/apideltr.c create mode 100644 build/libraries/fatfs/ARM7/apienum.c create mode 100644 build/libraries/fatfs/ARM7/apifilio.c create mode 100644 build/libraries/fatfs/ARM7/apifilmv.c create mode 100644 build/libraries/fatfs/ARM7/apifrmat.c create mode 100644 build/libraries/fatfs/ARM7/apigetwd.c create mode 100644 build/libraries/fatfs/ARM7/apigfrst.c create mode 100644 build/libraries/fatfs/ARM7/apiinfo.c create mode 100644 build/libraries/fatfs/ARM7/apiinit.c create mode 100644 build/libraries/fatfs/ARM7/apimkdir.c create mode 100644 build/libraries/fatfs/ARM7/apirealt.c create mode 100644 build/libraries/fatfs/ARM7/apiregrs.c create mode 100644 build/libraries/fatfs/ARM7/apisetwd.c create mode 100644 build/libraries/fatfs/ARM7/apistat.c create mode 100644 build/libraries/fatfs/ARM7/apiwrite.c create mode 100644 build/libraries/fatfs/ARM7/appcmdsh.c create mode 100644 build/libraries/fatfs/ARM7/appdemo.c create mode 100644 build/libraries/fatfs/ARM7/attach.c create mode 100644 build/libraries/fatfs/ARM7/csascii.c create mode 100644 build/libraries/fatfs/ARM7/csjis.c create mode 100644 build/libraries/fatfs/ARM7/csjistab.c create mode 100644 build/libraries/fatfs/ARM7/csstrtab.c create mode 100644 build/libraries/fatfs/ARM7/csunicod.c create mode 100644 build/libraries/fatfs/ARM7/drdefault.c create mode 100644 build/libraries/fatfs/ARM7/drdefault.h create mode 100644 build/libraries/fatfs/ARM7/drfile.c create mode 100644 build/libraries/fatfs/ARM7/portconf.h create mode 100644 build/libraries/fatfs/ARM7/portio.c create mode 100644 build/libraries/fatfs/ARM7/portkern.c create mode 100644 build/libraries/fatfs/ARM7/portmain.c create mode 100644 build/libraries/fatfs/ARM7/prapipro.c create mode 100644 build/libraries/fatfs/ARM7/prblock.c create mode 100644 build/libraries/fatfs/ARM7/prfsapi.c create mode 100644 build/libraries/fatfs/ARM7/prfscore.c create mode 100644 build/libraries/fatfs/ARM7/prfsnvio.c create mode 100644 build/libraries/fatfs/ARM7/prfstest.c create mode 100644 build/libraries/fatfs/ARM7/readme.txt create mode 100644 build/libraries/fatfs/ARM7/revhst40.txt create mode 100644 build/libraries/fatfs/ARM7/rtdevio.c create mode 100644 build/libraries/fatfs/ARM7/rtdrobj.c create mode 100644 build/libraries/fatfs/ARM7/rtfat16.c create mode 100644 build/libraries/fatfs/ARM7/rtfat32.c create mode 100644 build/libraries/fatfs/ARM7/rtfatxx.c create mode 100644 build/libraries/fatfs/ARM7/rtkernfn.c create mode 100644 build/libraries/fatfs/ARM7/rtlowl.c create mode 100644 build/libraries/fatfs/ARM7/rtnvfat.c create mode 100644 build/libraries/fatfs/ARM7/rttermin.c create mode 100644 build/libraries/fatfs/ARM7/rtutbyte.c create mode 100644 build/libraries/fatfs/ARM7/rtutil.c create mode 100644 build/libraries/fatfs/ARM7/rtvfat.c create mode 100644 build/libraries/fatfs/ARM7/winhdisk.c create mode 100644 build/libraries/fatfs/ARM7/winmkrom.c create mode 100644 build/libraries/fatfs/ARM7/winsplsh.c create mode 100644 build/libraries/fatfs/Makefile create mode 100644 include/twl/devices/sdmc/ARM7/sdmc.h create mode 100644 include/twl/fatfs/ARM7/attach.h create mode 100644 include/twl/fatfs/ARM7/csstrtab.h create mode 100644 include/twl/fatfs/ARM7/drfile.h create mode 100644 include/twl/fatfs/ARM7/portconf.h create mode 100644 include/twl/fatfs/ARM7/prfs.h create mode 100644 include/twl/fatfs/ARM7/rtfs.h create mode 100644 include/twl/fatfs/ARM7/rtfs_naming_convention.h create mode 100644 include/twl/fatfs/ARM7/rtfs_target_os.h create mode 100644 include/twl/fatfs/ARM7/rtfsapi.h create mode 100644 include/twl/fatfs/ARM7/rtfsconf.h create mode 100644 include/twl/fatfs/ARM7/rtfspro.h diff --git a/build/buildsetup/ioreg_sp/io_register_list.csv b/build/buildsetup/ioreg_sp/io_register_list.csv index 3341ecd..eed481b 100644 --- a/build/buildsetup/ioreg_sp/io_register_list.csv +++ b/build/buildsetup/ioreg_sp/io_register_list.csv @@ -230,3 +230,45 @@ 0x514,,SNDCAP0LEN,16,rw,SND,volatile,LEN,0,16,,,,,,,,,,,,,,,,,,,,,,,,,,, 0x518,,SNDCAP1DAD,32,w,SND,volatile,DEST,0,27,,,,,,,,,,,,,,,,,,,,,,,,,,, 0x51c,,SNDCAP1LEN,16,rw,SND,volatile,LEN,0,16,,,,,,,,,,,,,,,,,,,,,,,,,,, +#SDインタフェース twl p195,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0x800,,SD_CMD,16,rw,SD,volatile,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0x802,,SD_PORTSEL,16,rw,SD,volatile,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0x804,,SD_ARG0,16,rw,SD,volatile,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0x806,,SD_ARG1,16,rw,SD,volatile,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0x808,,SD_STOP,16,rw,SD,volatile,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0x80A,,SD_SECCNT,16,rw,SD,volatile,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0x80C,,SD_RSP0,16,r,SD,volatile,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0x80E,,SD_RSP1,16,r,SD,volatile +0x810,,SD_RSP2,16,r,SD,volatile +0x812,,SD_RSP3,16,r,SD,volatile +0x814,,SD_RSP4,16,r,SD,volatile +0x816,,SD_RSP5,16,r,SD,volatile +0x818,,SD_RSP6,16,r,SD,volatile +0x81A,,SD_RSP7,16,r,SD,volatile +0x81C,,SD_INFO1,16,rw,SD,volatile +0x81E,,SD_INFO2,16,rw,SD,volatile +0x820,,SD_INFO1_MASK,16,rw,SD,volatile +0x822,,SD_INFO2_MASK,16,rw,SD,volatile +0x824,,SD_CLK_CTRL,16,rw,SD,volatile +0x826,,SD_SIZE,16,rw,SD,volatile +0x828,,SD_OPTION,16,rw,SD,volatile +0x82C,,SD_ERR_STS1,16,r,SD,volatile +0x82E,,SD_ERR_STS2,16,r,SD,volatile +0x830,,SD_BUF0,16,rw,SD,volatile +0x834,,SDIO_MODE,16,rw,SD,volatile +0x836,,SDIO_INFO1,16,rw,SD,volatile +0x838,,SDIO_INF1_MASK,16,rw,SD,volatile +0x8D8,,CC_EXT_MODE,16,rw,SD,volatile +0x8E0,,SOFT_RST,16,rw,SD,volatile +0x8E2,,VERSITON,16,r,SD,permanent +0x8F2,,SD_PWR,16,rw,SD,volatile +0x8F4,,EXT_SDIO,16,rw,SD,volatile +0x8F6,,EXT_WP,16,r,SD,volatile +0x8F8,,EXT_CD,16,rw,SD,volatile +0x8FA,,EXT_CD_DAT3,16,rw,SD,volatile +0x8FC,,EXT_CD_MASK,16,rw,SD,volatile +0x8FE,,EXT_CD_DAT3_MASK,16,rw,SD,volatile +0x900,,SDIF_CNT,16,rw,SD,volatile +0x904,,SDIF_FDS,16,rw,SD,volatile +0x908,,SDIF_FSC,16,rw,SD,volatile +0x90C,,SDIF_FI,32,rw,SD,volatile diff --git a/build/libraries/devices/Makefile b/build/libraries/devices/Makefile new file mode 100644 index 0000000..73a8cd3 --- /dev/null +++ b/build/libraries/devices/Makefile @@ -0,0 +1,34 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - libraries +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + + +#---------------------------------------------------------------------------- + +SUBDIRS = \ + sdmc \ + +#---------------------------------------------------------------------------- +export NITRO_BLXCHECKED = yes + +#---------------------------------------------------------------------------- + +include $(TWLSDK_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/build/libraries/devices/sdmc/ARM7/Makefile b/build/libraries/devices/sdmc/ARM7/Makefile new file mode 100644 index 0000000..1d09af5 --- /dev/null +++ b/build/libraries/devices/sdmc/ARM7/Makefile @@ -0,0 +1,66 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - libraries - mi/ARM7 +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +SUBDIRS = + + +#---------------------------------------------------------------------------- + +# build ARM & THUMB libraries +TWL_CODEGEN_ALL ?= True + +# Codegen for sub processer +TWL_PROC = ARM7 + +SRCDIR = ../common/src src + +INCDIR = ../common +INCDIR += $(TWLSDK_ROOT)/include/twl \ + $(TWLSDK_ROOT)/include/twl/devices/sdmc/ARM7 \ + $(TWLSDK_ROOT)/include/twl/fatfs/ARM7 \ + +SRCS = \ + sdmc.c \ + sdif.c \ + drsdmc.c + +TARGET_LIB = libsd_sp$(TWL_LIBSUFFIX).a + + +#---------------------------------------------------------------------------- + +# DEBUG版ビルドの場合、RELEASE版でビルドして +# DEBUG版のライブラリを装います。 + +ifdef NITRO_DEBUG +NITRO_BUILD_TYPE = RELEASE +endif + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + +INSTALL_TARGETS = $(TARGETS) +INSTALL_DIR = $(TWL_INSTALL_LIBDIR) + + +#---------------------------------------------------------------------------- + +do-build: $(TARGETS) + +include $(TWLSDK_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/build/libraries/devices/sdmc/ARM7/drsdmc.c b/build/libraries/devices/sdmc/ARM7/drsdmc.c new file mode 100644 index 0000000..81910fa --- /dev/null +++ b/build/libraries/devices/sdmc/ARM7/drsdmc.c @@ -0,0 +1,686 @@ +/*---------------------------------------------------------------------------* + Project: CTR - rtfs interface for SD Memory Card + File: drsdmc.h + + Copyright 2006,2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#include +#include +//#if (INCLUDE_SD) + +#include "sdmc_config.h" +#include "sdmc.h" +#include "sdif_ip.h" +#include "sdif_reg.h" + +#if (SD_DEBUG_PRINT_ON == 1) + #if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + #define PRINTDEBUG osTPrintf + #else + #include + #define PRINTDEBUG vlink_dos_printf + #endif +#else + #define PRINTDEBUG( ...) ((void)0) +#endif + + +#define NUM_SD_PAGES +#define SD_PAGE_SIZE + + +/*---------------------------------------------------------------------------* + extern変数 + *---------------------------------------------------------------------------*/ +//extern ER_ID sdmc_dtq_id; +//extern ER_ID sdmc_result_dtq_id; +extern void (*func_SDCARD_In)(void); /* カード挿入イベント用コールバック保存用 */ +extern void (*func_SDCARD_Out)(void); /* カード排出イベント用コールバック保存用 */ +extern volatile s16 SDCARD_OutFlag; /* カード排出発生判定フラグ */ + +extern int rtfs_first_stat_flag[26]; + +/*SDメモリカードのスペック構造体*/ +extern SdmcSpec sdmc_current_spec; + + +/*---------------------------------------------------------------------------* + extern関数 + *---------------------------------------------------------------------------*/ +extern SDMC_ERR_CODE sdmcGoIdle(void (*func1)(),void (*func2)()); + + +/*---------------------------------------------------------------------------* + static変数 + *---------------------------------------------------------------------------*/ +static int sdmc_drive_no; +void (*func_usr_sdmc_out)(void) = NULL; /* カード排出イベントのユーザコールバック */ + + +/*---------------------------------------------------------------------------* + static関数 + *---------------------------------------------------------------------------*/ +void i_sdmcRemovedIntr( void); +static void sdi_get_CHS_params( void); +static u32 sdi_get_ceil( u32 cval, u32 mval); +static void sdi_get_nom( void); +static void sdi_get_fatparams( void); +static void sdi_build_partition_table( void); + + +/*---------------------------------------------------------------------------* + Name: sdmcCheckMedia + + Description: MBRのシグネチャおよび + パーティションのフォーマット種別をチェックする + + Arguments: + + Returns: TRUE/FALSE + (FALSEなら pc_format_media が必要) + *---------------------------------------------------------------------------*/ +BOOL sdmcCheckMedia( void) +{ + u16 i; + SdmcResultInfo SdResult; + u8* bufp; + u32 buffer[512/4]; + u8 systemid; + + /**/ + if( sdmcReadFifo( buffer, 1, 0, NULL, &SdResult)) { + return( FALSE); + } + + bufp = (u8*)buffer; + + /* Check the Signature Word. */ + if( (bufp[510]!=0x55) || (bufp[511]!=0xAA)) { + return( FALSE); + } + /* Check the System ID of partition. */ + systemid = bufp[450]; + if( (systemid!=0x01) && (systemid!=0x04) && (systemid!=0x06) && + (systemid!=0x0B) && (systemid!=0x0C)) { + return( FALSE); + } + /* Check the System ID of unuse partitions. */ + for( i=1; i<4; i++) { + systemid = bufp[450+(16*i)]; + if( systemid != 0x00) { + return( FALSE); + } + } + /**/ + /*もっと厳密にチェックするならパーティション0開始位置の + 正当性も調べる*/ + return( TRUE); +} + + +/*---------------------------------------------------------------------------* + Name: sdmcRtfsIo + + Description: 上位層からのセクタリード/ライト要求を受ける + + Arguments: driveno : ドライブ番号 + block : 開始ブロック番号 + buffer : + count : ブロック数 + reading : リード要求時にTRUE + + Returns: TRUE/FALSE + *---------------------------------------------------------------------------*/ +BOOL sdmcRtfsIo( int driveno, dword block, void* buffer, word count, BOOLEAN reading) +{ + u16 result; + SdmcResultInfo SdResult; + + if( reading) { + PRINTDEBUG( "DEVCTL_IO_READ ... block:%x, count:%x -> buf:%x\n", block, count, buffer); + result = sdmcReadFifo( buffer, count, block, NULL, &SdResult); +// result = sdmcRead( buffer, count, block, NULL, &SdResult); + }else{ + PRINTDEBUG( "DEVCTL_IO_WRITE ... block:%x, count:%x <- buf:%x\n", block, count, buffer); + result = sdmcWriteFifo( buffer, count, block, NULL, &SdResult); +// result = sdmcWrite( buffer, count, block, NULL, &SdResult); + } + if( result) { + return FALSE; + } + + return TRUE; +} + +/*---------------------------------------------------------------------------* + Name: sdmcRtfsCtrl + + Description: 上位層からのコントロール要求を受ける + + Arguments: driveno : ドライブ番号 + opcode : 要求の種類 + pargs : + + Returns: + Memo : DRIVE_FLAGS_REMOVABLEの場合、ドライバのIO関数を呼ぶ前に + CTRL関数のDEVCTL_CHECK_STATUSが呼ばれる。 + DEVTEST_CHANGED→DEVTEST_NOCHANGEが確認されるとRTFSはセクタ0を + 読みに行き、FATパラメータを取得した上で目的のセクタを読みに行く。 + CTRL関数の前にはDEVCTL_CHECK_STATUSが呼ばれないので、DEVCTL_ + GET_GEOMETRYでは自前で再挿入をチェックする必要がある。 + *---------------------------------------------------------------------------*/ +int sdmcRtfsCtrl( int driveno, int opcode, void* pargs) +{ + DDRIVE *pdr; + DEV_GEOMETRY gc; + int heads, secptrack; + + pdr = pc_drno_to_drive_struct( driveno); + + switch( opcode) { + case DEVCTL_GET_GEOMETRY: //formatまたはpartirionするときにRTFSが使うパラメータ + PRINTDEBUG( "DEVCTL_GET_GEOMETRY\n"); + if( (pdr->drive_flags & DRIVE_FLAGS_INSERTED) == 0) { /* 抜かれていた場合 */ + if( SDCARD_OutFlag == TRUE) { /* 今現在も抜かれているとき */ + return( -1); + }else{ /* 今現在は再挿入されているとき */ + /*-- GoIdleセット --*/ + if( func_SDCARD_Out != i_sdmcRemovedIntr) { + func_usr_sdmc_out = func_SDCARD_Out; //ユーザコールバック取得 + } + sdmcGoIdle( func_SDCARD_In, i_sdmcRemovedIntr); //カード初期化シーケンス + /*------------------*/ + } + } + + rtfs_memset( &gc, (byte)0, sizeof(gc)); + + sdi_get_CHS_params(); //最初に呼ぶこと + sdi_get_fatparams(); + sdi_get_nom(); + + PRINTDEBUG( "heads : 0x%x\n", sdmc_current_spec.heads); + PRINTDEBUG( "secptrack : 0x%x\n", sdmc_current_spec.secptrack); + PRINTDEBUG( "cylinders : 0x%x\n", sdmc_current_spec.cylinders); + PRINTDEBUG( "SC : 0x%x\n", sdmc_current_spec.SC); + PRINTDEBUG( "BU : 0x%x\n", sdmc_current_spec.BU); + PRINTDEBUG( "RDE : 0x%x\n", sdmc_current_spec.RDE); + PRINTDEBUG( "SS : 0x%x\n", sdmc_current_spec.SS); + PRINTDEBUG( "RSC : 0x%x\n", sdmc_current_spec.RSC); + PRINTDEBUG( "FATBITS : 0x%x\n", sdmc_current_spec.FATBITS); + PRINTDEBUG( "SF : 0x%x\n", sdmc_current_spec.SF); + PRINTDEBUG( "SSA : 0x%x\n", sdmc_current_spec.SSA); + PRINTDEBUG( "NOM : 0x%x\n", sdmc_current_spec.NOM); + + gc.dev_geometry_lbas = (sdmc_current_spec.adjusted_memory_capacity);// - sdmc_current_spec.NOM); + gc.dev_geometry_heads = sdmc_current_spec.heads; + gc.dev_geometry_cylinders = sdmc_current_spec.cylinders; + gc.dev_geometry_secptrack = sdmc_current_spec.secptrack; + /**/ + gc.fmt_parms_valid = TRUE; + gc.fmt.oemname[0] = 'C'; + gc.fmt.oemname[1] = 'T'; + gc.fmt.oemname[2] = 'R'; + gc.fmt.oemname[3] = '\0'; + gc.fmt.secpalloc = sdmc_current_spec.SC; /*sectors per cluster(FIX by capacity)*/ + gc.fmt.secreserved = sdmc_current_spec.RSC;//sdmc_current_spec.RSC;/*reserved sectors(FIX 1 at FAT12,16)*/ + gc.fmt.numfats = 2; + gc.fmt.secpfat = sdmc_current_spec.SF; + gc.fmt.numhide = sdmc_current_spec.NOM; /**/ + gc.fmt.numroot = sdmc_current_spec.RDE; /*FIX*/ + gc.fmt.mediadesc = 0xF8; + gc.fmt.secptrk = sdmc_current_spec.secptrack; //CHS Recommendation + gc.fmt.numhead = sdmc_current_spec.heads; + gc.fmt.numcyl = sdmc_current_spec.cylinders; + gc.fmt.physical_drive_no = driveno; + gc.fmt.binary_volume_label = BIN_VOL_LABEL; + gc.fmt.text_volume_label[0] = '\0'; + //TODO:dev_geometry_lbasもセットする必要あるか調べること + PRINTDEBUG( "heads : 0x%x, secptrack : 0x%x, cylinders : 0x%x\n", gc.dev_geometry_heads, gc.dev_geometry_secptrack, gc.dev_geometry_cylinders); + +#if (TARGET_OS_CTR == 1) + miCpuCopy8( &gc, pargs, sizeof(gc)); +// copybuff( pargs, &gc, sizeof(gc)); +#else + MI_CpuCopy8( &gc, pargs, sizeof(gc)); +#endif + return( 0); + + case DEVCTL_FORMAT: + PRINTDEBUG( "DEVCTL_FORMAT\n"); + sdi_build_partition_table(); //MBRセクタ(パーティションテーブル含む)書き込み + return( 0); + + case DEVCTL_REPORT_REMOVE: //抜かれたとき + PRINTDEBUG( "DEVCTL_REPORT_REMOVE\n"); + pdr->drive_flags &= ~DRIVE_FLAGS_INSERTED; + return( 0); + + case DEVCTL_CHECKSTATUS: //REMOVABLEの場合、毎回R/W前に呼ばれる + PRINTDEBUG( "DEVCTL_CHECKSTATUS\n"); + if (!(pdr->drive_flags & DRIVE_FLAGS_REMOVABLE)) { //リムーバブルでない場合 + return(DEVTEST_NOCHANGE); + } + if (pdr->drive_flags & DRIVE_FLAGS_INSERTED) { /* 抜かれてない場合 */ + if( rtfs_first_stat_flag[driveno]) { //初回のCHECKSTATUS時 + rtfs_first_stat_flag[driveno] = 0; + PRINTDEBUG( "CHANGED!\n"); + return(DEVTEST_CHANGED); + }else{ + PRINTDEBUG( "DEVTEST_NOCHANGE\n"); + return( DEVTEST_NOCHANGE); + } + }else{ /* 抜かれていた場合 */ + if( SDCARD_OutFlag == FALSE) { /* 排出フラグ参照 */ + pdr->drive_flags |= DRIVE_FLAGS_INSERTED; + /*-- GoIdleセット --*/ + if( func_SDCARD_Out != i_sdmcRemovedIntr) { + func_usr_sdmc_out = func_SDCARD_Out; //ユーザコールバック取得 + } + sdmcGoIdle( func_SDCARD_In, i_sdmcRemovedIntr); //カード初期化シーケンス + /*------------------*/ + PRINTDEBUG( "DEVTEST_CHANGED\n"); + return( DEVTEST_CHANGED); //挿さっている + }else{ + PRINTDEBUG( "DEVTEST_NOMEDIA\n"); + return( DEVTEST_NOMEDIA); //挿さってない + } + } + + case DEVCTL_WARMSTART: //attachのときしか呼ばれない + PRINTDEBUG( "DEVCTL_WARMSTART\n"); + /*-- GoIdleセット --*/ + if( func_SDCARD_Out != i_sdmcRemovedIntr) { + func_usr_sdmc_out = func_SDCARD_Out; //ユーザコールバック取得 + } + sdmcGoIdle( func_SDCARD_In, i_sdmcRemovedIntr); //カード初期化シーケンス + /*------------------*/ + pdr->drive_flags |= (DRIVE_FLAGS_VALID | DRIVE_FLAGS_REMOVABLE | DRIVE_FLAGS_PARTITIONED); + pdr->partition_number = 0; + + if( SDCARD_OutFlag == FALSE) { /* 排出フラグ参照 */ + pdr->drive_flags |= DRIVE_FLAGS_INSERTED; + }else{ /* カード未挿入のとき */ + pdr->drive_flags &= (~(DRIVE_FLAGS_INSERTED)); //抜かれているだけではVALIDフラグは落とさないらしい + } + return( 0); + + case DEVCTL_POWER_RESTORE: + PRINTDEBUG( "DEVCTL_POWER_RESTORE\n"); + break; + + case DEVCTL_POWER_LOSS: + PRINTDEBUG( "DEVCTL_POWER_LOSS\n"); + break; + + default: + PRINTDEBUG( "DEVCTL_unknown\n"); + break; + } + return( 0); +} + +/*---------------------------------------------------------------------------* + 抜取コールバック関数 + + RTFSが次回"DEVCTL_GET_GEOMETRY"を行うとき、ここでセットしたパラメータを知る + *---------------------------------------------------------------------------*/ +void i_sdmcRemovedIntr( void) +{ + DDRIVE *pdr; + + pdr = pc_drno_to_drive_struct( sdmc_drive_no); + if( pdr) { + pdr->dev_table_perform_device_ioctl( pdr->driveno, + DEVCTL_REPORT_REMOVE, + (void*)0); + } + + if( func_usr_sdmc_out) { + func_usr_sdmc_out(); //ユーザコールバック + } +} + + +/*---------------------------------------------------------------------------* + Name: sdmcRtfsAttach + + Description: sdmcドライバをドライブに割り当てる + + Arguments: driveno : ドライブ番号 + + Returns: + *---------------------------------------------------------------------------*/ +BOOL sdmcRtfsAttach( int driveno) +{ + BOOLEAN result; + DDRIVE pdr; + + pdr.dev_table_drive_io = sdmcRtfsIo; + pdr.dev_table_perform_device_ioctl = sdmcRtfsCtrl; + pdr.register_file_address = (dword) 0; /* Not used */ + pdr.interrupt_number = 0; /* Not used */ + pdr.drive_flags = 0;//DRIVE_FLAGS_FAILSAFE; + pdr.partition_number = 0; /* Not used */ + pdr.pcmcia_slot_number = 0; /* Not used */ + pdr.controller_number = 0; + pdr.logical_unit_number = 0; + + result = rtfs_attach( driveno, &pdr, "SD"); //構造体がFSライブラリ側にコピーされる + + /*drivenoをグローバル変数に記憶*/ + sdmc_drive_no = driveno; + + return( result); +} + + + +/*SD File System Specification(仕様書)に基づいた値を出す*/ +static void sdi_get_CHS_params( void) +{ + int mbytes; + +// mbytes = (sdmc_current_spec.card_capacity / (1024 * 1024)) * 512; + mbytes = (sdmc_current_spec.card_capacity >> 11); + + while( 1) { + if( mbytes <= 2) { + sdmc_current_spec.heads = 2; + sdmc_current_spec.secptrack = 16; + break; + } + if( mbytes <= 16) { + sdmc_current_spec.heads = 2; + sdmc_current_spec.secptrack = 32; + break; + } + if( mbytes <= 32) { + sdmc_current_spec.heads = 4; + sdmc_current_spec.secptrack = 32; + break; + } + if( mbytes <= 128) { + sdmc_current_spec.heads = 8; + sdmc_current_spec.secptrack = 32; + break; + } + if( mbytes <= 256) { + sdmc_current_spec.heads = 16; + sdmc_current_spec.secptrack = 32; + break; + } + if( mbytes <= 504) { + sdmc_current_spec.heads = 16; + sdmc_current_spec.secptrack = 63; + break; + } + if( mbytes <= 1008) { + sdmc_current_spec.heads = 32; + sdmc_current_spec.secptrack = 63; + break; + } + if( mbytes <= 2016) { + sdmc_current_spec.heads = 64; + sdmc_current_spec.secptrack = 63; + break; + } + if( mbytes <= 2048) { + sdmc_current_spec.heads = 128; + sdmc_current_spec.secptrack = 63; + break; + } + if( mbytes <= 4032) { + sdmc_current_spec.heads = 128; + sdmc_current_spec.secptrack = 63; + break; + } + if( mbytes <= 32768) { + sdmc_current_spec.heads = 255; + sdmc_current_spec.secptrack = 63; + break; + } + } + + /*シリンダ数を計算*/ + sdmc_current_spec.cylinders = (sdmc_current_spec.memory_capacity / + (sdmc_current_spec.heads * sdmc_current_spec.secptrack)); + + /*memory_capacityを再計算してadjusted_memory_capacityに格納*/ + sdmc_current_spec.adjusted_memory_capacity = sdmc_current_spec.cylinders * + (sdmc_current_spec.heads * sdmc_current_spec.secptrack); +} + + +/*引数を超える最も小さい整数を算出する*/ +static u32 sdi_get_ceil( u32 cval, u32 mval) +{ + return( (cval / mval) + (1 * (cval % mval != 0))); +} + + +/*マスターブートセクタのセクタ数を返す*/ +static void sdi_get_nom( void) +{ + u32 RSC = 1; //FAT12,16では1 + u32 RDE = 512; //ルートディレクトリエントリ。FIX + u32 SS = 512; //セクタサイズ。FIX + u32 TS, SC, n; + u32 MAX, SFdash; + + TS = sdmc_current_spec.adjusted_memory_capacity; + SC = sdmc_current_spec.SC; + + sdmc_current_spec.SF = sdi_get_ceil( TS/SC * sdmc_current_spec.FATBITS, SS*8); + + /*-----------------------SDHCのとき----------------------------*/ + if( sdmc_current_spec.csd_ver2_flag) { + sdmc_current_spec.NOM = sdmc_current_spec.BU; + do { + n = sdi_get_ceil( 2*sdmc_current_spec.SF, sdmc_current_spec.BU); + sdmc_current_spec.RSC = (sdmc_current_spec.BU * n) - ( 2 * sdmc_current_spec.SF); + if( sdmc_current_spec.RSC < 9) { + sdmc_current_spec.RSC += sdmc_current_spec.BU; + } + sdmc_current_spec.SSA = sdmc_current_spec.RSC + (2 * sdmc_current_spec.SF); + do { + MAX = ((TS - sdmc_current_spec.NOM - sdmc_current_spec.SSA) / SC) + 1; + SFdash = sdi_get_ceil( (2+(MAX-1)) * sdmc_current_spec.FATBITS, SS*8); + if( SFdash > sdmc_current_spec.SF) { + sdmc_current_spec.SSA += sdmc_current_spec.BU; + sdmc_current_spec.RSC += sdmc_current_spec.BU; + }else{ + break; + } + }while( 1); + if( SFdash != sdmc_current_spec.SF) { + sdmc_current_spec.SF -= 1; + }else{ + break; + } + }while( 1); + }else{ /*-------------------------SDのとき-------------------------------*/ + do { + sdmc_current_spec.SSA = RSC + ( 2 * sdmc_current_spec.SF) + sdi_get_ceil( 32*RDE, SS); + n = sdi_get_ceil( sdmc_current_spec.SSA, sdmc_current_spec.BU); + sdmc_current_spec.NOM = (sdmc_current_spec.BU * n) - sdmc_current_spec.SSA; + if( sdmc_current_spec.NOM != sdmc_current_spec.BU) { + sdmc_current_spec.NOM += sdmc_current_spec.BU; + } + do { + MAX = ((TS - sdmc_current_spec.NOM - sdmc_current_spec.SSA) / SC) + 1; + SFdash = sdi_get_ceil( (2+(MAX-1)) * sdmc_current_spec.FATBITS, SS*8); + if( SFdash > sdmc_current_spec.SF) { + sdmc_current_spec.NOM += sdmc_current_spec.BU; + }else{ + break; + } + }while( 1); + if( SFdash != sdmc_current_spec.SF) { + sdmc_current_spec.SF = SFdash; + }else{ + break; //complete + } + }while( 1); + } + + return; +} + +/*FATのビット数を返す*/ +static void sdi_get_fatparams( void) +{ + int mbytes; + +// mbytes = (sdmc_current_spec.card_capacity / (1024 * 1024)) * 512; + mbytes = (sdmc_current_spec.card_capacity >> 11); + + if( mbytes <= 64) { + sdmc_current_spec.FATBITS = 12; + sdmc_current_spec.RDE = 512; + sdmc_current_spec.RSC = 1; + }else{ + if( mbytes <= 2048) { + sdmc_current_spec.FATBITS = 16; + sdmc_current_spec.RDE = 512; + sdmc_current_spec.RSC = 1; + }else{ + sdmc_current_spec.FATBITS = 32; + sdmc_current_spec.RDE = 0; //FAT32のときは未使用。0にしておかないとRTFSが BAD FORMAT を返す。 + sdmc_current_spec.RSC = 1; + } + } + + if( mbytes <= 8) { + sdmc_current_spec.SC = 16; + sdmc_current_spec.BU = 16; + return; + } + if( mbytes <= 64) { + sdmc_current_spec.SC = 32; + sdmc_current_spec.BU = 32; + return; + } + if( mbytes <= 256) { + sdmc_current_spec.SC = 32; + sdmc_current_spec.BU = 64; + return; + } + if( mbytes <= 1024) { + sdmc_current_spec.SC = 32; + sdmc_current_spec.BU = 128; + return; + } + if( mbytes <= 2048) { + sdmc_current_spec.SC = 64; + sdmc_current_spec.BU = 128; + return; + } + if( mbytes <= 32768) { + sdmc_current_spec.SC = 64; + sdmc_current_spec.BU = 8192; + return; + } +} + +/*MBRセクタ(パーティションセクタ含む)を生成して書き込む*/ +static void sdi_build_partition_table( void) +{ + u16 MbrSectDat[512/2]; + u32 starting_head, starting_sect, starting_cyl; + u32 ending_head, ending_sect, ending_cyl; + u32 total_sect; +#if (SD_DEBUG_PRINT_ON == 1) + u32 starting_data, ending_data; +#endif + u32 systemid; + SdmcResultInfo SdResult; + + /**/ + starting_head = sdmc_current_spec.NOM % (sdmc_current_spec.heads * + sdmc_current_spec.secptrack); + starting_head /= sdmc_current_spec.secptrack; + + /**/ + starting_sect = (sdmc_current_spec.NOM % sdmc_current_spec.secptrack) + 1; + + /**/ + starting_cyl = sdmc_current_spec.NOM / (sdmc_current_spec.heads * + sdmc_current_spec.secptrack); + + /**/ + total_sect = (sdmc_current_spec.adjusted_memory_capacity - sdmc_current_spec.NOM); + ending_head = (sdmc_current_spec.NOM + total_sect - 1) % + (sdmc_current_spec.heads * sdmc_current_spec.secptrack); + ending_head /= sdmc_current_spec.secptrack; + + /**/ + ending_sect = ((sdmc_current_spec.NOM + total_sect - 1) % + sdmc_current_spec.secptrack) + 1; + + /**/ + ending_cyl = (sdmc_current_spec.NOM + total_sect - 1) / + (sdmc_current_spec.heads * sdmc_current_spec.secptrack); + + /**/ + if( sdmc_current_spec.FATBITS == 32) { //FAT32のとき + if( total_sect < 0xFB0400) { //8032.5MBが閾値(SD FileSystemSpec2.00参照) + systemid = 0x0B; /* FAT32 */ + }else{ + systemid = 0x0C; /* FAT32(拡張INT13対応) */ + } + }else{ //FAT12,FAT16のとき + if( total_sect < 32680) { + systemid = 0x01; /* FAT12 */ + }else if( total_sect < 65536) { + systemid = 0x04; /* FAT16(16MB〜32MB未満) */ + }else{ + systemid = 0x06; /* FAT16(32MB〜4GB) */ + } + } + + /*MBRセクタ(パーティションテーブル含む)作成*/ +#if (TARGET_OS_CTR == 1) + miCpuFill8( MbrSectDat, 0, 512); +#else + MI_CpuFill8( MbrSectDat, 0, 512); +#endif + MbrSectDat[446/2] = (starting_head<<8); + //上位8bit:starting_cylの下位8bit, 下位8bit:starting_cylの上位2bit + starting_sect 6bit. + MbrSectDat[448/2] = (starting_cyl<<8) + ((starting_cyl>>2) & 0xC0) + starting_sect; + MbrSectDat[450/2] = (ending_head<<8) + systemid; + //上位8bit:ending_cylの下位8bit, 下位8bit:ending_cylの上位2bit + ending_sect 6bit. + MbrSectDat[452/2] = (ending_cyl<<8) + ((ending_cyl>>2) & 0xC0) + ending_sect; + MbrSectDat[454/2] = sdmc_current_spec.NOM; + MbrSectDat[456/2] = (sdmc_current_spec.NOM>>16); + MbrSectDat[458/2] = total_sect; + MbrSectDat[460/2] = (total_sect>>16); + MbrSectDat[510/2] = 0xAA55; + /*セクタ0に書き込み*/ + sdmcWriteFifo( MbrSectDat, 1, 0, NULL, &SdResult); + + /**/ + PRINTDEBUG( "total sect : 0x%x\n", total_sect); + PRINTDEBUG( "starting head : 0x%x\n", starting_head); + PRINTDEBUG( "starting sect : 0x%x\n", starting_sect); + PRINTDEBUG( "starting cyl : 0x%x\n", starting_cyl); + PRINTDEBUG( "ending head : 0x%x\n", ending_head); + PRINTDEBUG( "ending sect : 0x%x\n", ending_sect); + PRINTDEBUG( "ending cyl : 0x%x\n", ending_cyl); + PRINTDEBUG( "\n"); +#if (SD_DEBUG_PRINT_ON == 1) + starting_data = (starting_cyl<<8) + ((starting_cyl>>2) & 0xC0) + starting_sect; + PRINTDEBUG( "starting data : 0x%x\n", starting_data); + ending_data = (ending_cyl<<8) + ((ending_cyl>>2) & 0xC0) + ending_sect; + PRINTDEBUG( "endign data : 0x%x\n", ending_data); +#endif +} + +//#endif /*(INCLUDE_SD)*/ diff --git a/build/libraries/devices/sdmc/ARM7/sdif.c b/build/libraries/devices/sdmc/ARM7/sdif.c new file mode 100644 index 0000000..5ee0104 --- /dev/null +++ b/build/libraries/devices/sdmc/ARM7/sdif.c @@ -0,0 +1,1188 @@ +/* + Project: CTR SD Card driver + File: sd_card.c + + 2006, Research and Development Department, Nintendo. +*/ + +#include +#include "sdmc_config.h" +#include "sdif_reg.h" /* IP 対応レジスタ定義 */ +#include "sdmc.h" +#include "sdif_ip.h" /* IP 対応フラグ定義 */ + +#if (SD_DEBUG_PRINT_ON == 1) + #if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + #define PRINTDEBUG osTPrintf + #else + #include + #define PRINTDEBUG vlink_dos_printf + #endif +#else + #define PRINTDEBUG( ...) ((void)0) +#endif + + +#define ADD_CHECK 1 + +/*********************************************************************** + グローバル +***********************************************************************/ +u16 SD_CID[8]; /* CID保存用 (Card IDentification register) : ID*/ +u16 SD_CSD[8]; /* CSD保存用 (Card Specific Data register) : spec*/ +u16 SD_OCR[2]; /* OCR保存用 (Operation Condition Register) : voltage and status*/ +u16 SD_SCR[4]; /* SCR保存用 (Sd card Configulation Register) : bus-width, card-ver, etc*/ +u16 SD_RCA; /* RCA保存用 (Relative Card Address register) : address*/ + +s16 SDCARD_MMCFlag; /* MMCカードフラグ */ +s16 SDCARD_SDHCFlag; /* SDHCカードフラグ(ここではPhysicalLayer2.0の意) */ +u16 SD_port_number; /* 現在のポート番号 */ + +/*********************************************************************** + 外部参照変数 +***********************************************************************/ +extern volatile u16 SDCARD_ErrStatus; /* エラーステータス */ +extern volatile u32 SDCARD_Status; /* カードステータス */ +//extern volatile s16 SDCARD_OutFlag; /* カード排出発生判定フラグ */ +//extern void (*func_SDCARD_Out)(void); /* カード排出イベント用コールバック保存用 */ + + + + + +void SD_DisableInfo( void); + + + +/*---------------------------------------------------------------------------* + Name: SD_Init + + Description: reset and initialize SD card interface + SDカードIPのリセットと初期設定 + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_Init(void) +{ + SD_AndFPGA( SOFT_RST,(~(SOFT_RST_SDIF_RST))); /* SD I/F モジュールをリセット */ + SD_OrFPGA( SOFT_RST,((SOFT_RST_SDIF_RST))); /* SD I/F モジュールをリセット復帰 */ + + SD_AndFPGA( SD_STOP,(~SD_STOP_STP)); /* データ転送終了クリア */ +} + +/*---------------------------------------------------------------------------* + Name: SD_EnableInfo + + Description: enable SD card insert and remove interrupts. + SDカードの挿入/抜取 割り込みを許可する + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_EnableInfo( void) +{ + if(SD_port_number == SDCARD_PORT0) + { + SD_AndFPGA(SD_INFO1,(~(SD_INFO1_INSERT | SD_INFO1_REMOVE))); /* SD_INFO1レジスタの card inserted removedをクリア */ + SD_AndFPGA(SD_INFO1_MASK,(~(SD_INFO1_MASK_INSERT | SD_INFO1_MASK_REMOVE))); /* 挿/抜 割り込み許可 */ + } + else if(SD_port_number == SDCARD_PORT1) + { /* ポート1はCD端子が繋がっていないので実質無効 */ + SD_AndFPGA(EXT_CD,(~(EXT_CD_PORT1_INSERT | EXT_CD_PORT1_REMOVE))); /* EXT_CD レジスタの card inserted removedをクリア */ + SD_AndFPGA(EXT_CD_MASK,(~(EXT_CD_MASK_PORT1INSERT | EXT_CD_MASK_PORT1REMOVE))); /* 挿抜 割り込み許可 */ + } +} +/*---------------------------------------------------------------------------* + Name: SD_DisableInfo + + Description: disable SD card insert and remove interrupts. + SDカードの挿入/抜取 割り込みを禁止する + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_DisableInfo( void) +{ + if(SD_port_number == SDCARD_PORT0) + { + SD_AndFPGA(SD_INFO1,(~(SD_INFO1_INSERT | SD_INFO1_REMOVE))); /* SD_INFO1レジスタの card inserted removedをクリア */ + SD_OrFPGA(SD_INFO1_MASK,(SD_INFO1_MASK_INSERT | SD_INFO1_MASK_REMOVE)); /* 挿/抜 割り込み禁止 */ + } + else if(SD_port_number == SDCARD_PORT1) + { /* ポート1はCD端子が繋がっていないので実質無効 */ + SD_AndFPGA(EXT_CD,(~(EXT_CD_PORT1_INSERT | EXT_CD_PORT1_REMOVE))); /* EXT_CD レジスタの card inserted removedをクリア */ + SD_OrFPGA(EXT_CD_MASK,(EXT_CD_MASK_PORT1INSERT | EXT_CD_MASK_PORT1REMOVE)); /* 挿抜 割り込み禁止 */ + } +} + +/*---------------------------------------------------------------------------* + Name: SD_Command + + Description: send command that the card will response only. + コマンドを送出する(レスポンスが返ってくるだけのコマンド用) + + Arguments: ucCommand : command number + + Returns: 0 : success + >0 : error + *---------------------------------------------------------------------------*/ +u16 SD_Command(u16 ucCommand) +{ + SD_AndFPGA(SD_INFO2,(~SD_INFO2_ERR_ALLCLR)); /* SD Card I/F の 全エラーをクリア */ + SD_AndFPGA(SD_INFO1,(~SD_INFO1_RES_END)); /* SD_INFO1レジスタの Response end クリア */ + SD_AndFPGA(SD_INFO2_MASK,(~SD_INFO2_MASK_ALLERRMASK)); /* SD Card I/F の 全エラー割込み許可 */ + + SD_SetFPGA(SD_CMD,(ucCommand)); /* コマンド発行 */ + + while(!SD_CheckFPGAReg(SD_INFO1,SD_INFO1_RES_END)){ /* Response end 待ち */ + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトチェック */ + break; + } + } + + SD_OrFPGA(SD_INFO2_MASK,(SD_INFO2_MASK_ALLERRMASK)); /* SD Card I/F の全エラー割込み禁止 */ + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_AppCommand + + Description: send apprication command. + アプリケーションコマンド(CMD55)を送出する + + Arguments: None + + Returns: 0 : success + >0 : error + *---------------------------------------------------------------------------*/ +u16 SD_AppCommand(void) +{ + PRINTDEBUG( " CMD55 (APP_CMD)\n"); + + /* argumentをセット */ + SD_SetFPGA(SD_ARG0,(0x0000)); /* Argument(15:0) = stuff bits */ + SD_SetFPGA(SD_ARG1,(SD_RCA)); /* Argument(31:16) = RCA */ + + SD_Command(SD_CMD_CMD | APP_CMD); /* CMD55発行、レスポンス(R1)待ち */ + +#if ADD_CHECK + /*SD_CheckStatusを行うと、直前にCMD8を発行していた場合、SDHC以外はCardStatusの + IllegalCommandエラーフラグが立ってしまうので、ここで引っかかることになる + (このフラグがクリアされるのは1コマンドぶん遅れるため) + SD Physical Layer 仕様書の Card Status参照*/ +// SD_CheckStatus(FALSE); /* コマンドレスポンス(R1) の Card Status チェック */ +#endif + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_AppOpCond + + Description: get operating condition register data of SD card. + SDカードのACMD41を発行してOCRを取得する + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_AppOpCond(void) +{ + SD_ClrErr((u16)(~SDMC_ERR_FPGA_TIMEOUT)); /* タイムアウト以外のエラーをクリア */ + + while(!SDCARD_ErrStatus){ /* エラーが発生しない間は繰り返し */ + /* Argument(31:0) = OCR without busy (0x00100000 = 3.2-3.3V) */ + /*ホスト側で電圧を選択できる場合などは、初回にSD_ARG1を0にすることにより + カードが対応している電圧を問い合わせることができる。CTRは電圧3.3V決め打ち + なので問い合わせせず、いきなり3.3V対応をカードに要求する。対応できない + カードは Inactive Mode に移行する。*/ + SD_SetFPGA(SD_ARG0,(0x0000)); + if( SDCARD_SDHCFlag) { + SD_SetFPGA(SD_ARG1,(0x4010)); + }else{ + SD_SetFPGA(SD_ARG1,(0x0010)); + } + + PRINTDEBUG( " ACMD41 (SD_SEND_OP_COND)\n"); + SD_Command(SD_CMD_ACMD | SD_APP_OP_COND); /* ACMD41発行、レスポンス(R3)待ち */ + + if(!SDCARD_ErrStatus){ /* エラーステータスの確認(エラー無し?)*/ + SD_GetFPGA(SD_OCR[0],SD_RSP0); /* レスポンス(R3)からOCR取得 */ + SD_GetFPGA(SD_OCR[1],SD_RSP1); /* レスポンス(R3)からOCR取得 */ + if(SD_RSP1 & RSP_R3_OCR31){ /* OCR(レジスタ)の31bit目チェック(busy?) */ + break; + } + } + SD_AppCommand(); /* 決定済みのRCAを設定しCMD55発行 */ + } + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_SendOpCond + + Description: send CMD1 without busy for MMC + MMC の CMD1を発行する(SDのACMD41に位置するコマンド) + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SendOpCond(void) +{ + PRINTDEBUG( " CMD1 (SEND_OP_COND)\n"); + + SD_ClrErr((u16)(~SDMC_ERR_FPGA_TIMEOUT)); /* タイムアウトエラーをクリア */ + + while(!SDCARD_ErrStatus){ /* エラーが発生しない間は繰り返し */ + SD_SetFPGA(SD_ARG0,(0x0000)); /* Argument(15:0) for MMC (None for SD) */ + SD_SetFPGA(SD_ARG1,(0x0010)); /* Argument(31:16) for MMC (None for SD) */ + SD_Command(SD_CMD_CMD | SEND_OP_COND); /* CMD1発行、レスポンス(R1)待ち */ + if(!SDCARD_ErrStatus){ /* エラーステータスの確認(エラー無し?) */ + SD_GetFPGA(SD_OCR[0],SD_RSP0); + SD_GetFPGA(SD_OCR[1],SD_RSP1); + if(SD_RSP1 & RSP_R3_OCR31){ /* OCR(レジスタ)の31bit目チェック(busy?) */ + break; + } + } + } + return SDCARD_ErrStatus; +} + + +/*---------------------------------------------------------------------------* + Name: SD_SendIfCond + + Description: send CMD8 + CMD8を発行する(Physical Layer 2.00で追加されたコマンド) + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SendIfCond(void) +{ + PRINTDEBUG( " CMD8 (SEND_IF_COND)\n"); + + SD_ClrErr((u16)(~SDMC_ERR_FPGA_TIMEOUT)); /* タイムアウトエラーをクリア */ + + /* (31:12) Reserved bits, (11:8) supply voltage(VHS), (7:0) check pattern */ + SD_SetFPGA(SD_ARG0,(0x01AA)); /* Argument */ + SD_SetFPGA(SD_ARG1,(0x0000)); /* Argument */ + + SD_Command(SD_CMD_CMD | SEND_IF_COND_EXT); /* CMD8発行、レスポンス(R7)待ち */ + + if(!SDCARD_ErrStatus){ /* エラーステータスの確認(エラー無し?) */ + SDCARD_SDHCFlag = TRUE; /* SDHC */ +// SD_GetFPGA(SD_R7[0],SD_RSP0); +// SD_GetFPGA(SD_R7[1],SD_RSP1); + }else{ + SDCARD_SDHCFlag = FALSE; + } + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_SendRelativeAddr + + Description: send relative addr + CMD3を発行し、SDカードの場合はレスポンスのRCAを取得する + (RCAは他のコマンド発行時にArgumentとして必要) + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SendRelativeAddr(void) +{ + PRINTDEBUG( " CMD3 (SEND_RELATIVE_ADDR)\n"); + + if(SDCARD_MMCFlag){ /* MMCカードフラグ ON ? */ + SD_SetFPGA(SD_ARG0,(0x0000)); /* Argument(15:0) */ + SD_SetFPGA(SD_ARG1,(0x0001)); /* Argument(31:16) = 0x0001 (松下drvはなぜか0x0100にしていた) */ + } /* SDカードのときはArgument(31:0) = stuff bits */ + + SD_Command(SD_CMD_CMD | SEND_RELATIVE_ADDR); /* CMD3発行、レスポンス(R6)待ち */ + + if(!SDCARD_ErrStatus){ /* エラーステータスの確認(エラー無し?) */ + if(SDCARD_MMCFlag){ /* MMCカードフラグ ON ? */ + SD_RCA = 0x0001; /* RCA <- 1 (松下drvはなぜか0x0100にしていた) */ + }else{ + SD_GetFPGA(SD_RCA,SD_RSP1); /* レスポンスレジスタからRCAを取得 */ + } + } + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_SelectCard + + Description: toggle card between the stand-by and transfer states. + CMD7を発行してスタンバイモードと転送モードを切り替える + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SelectCard(void) +{ + PRINTDEBUG( " CMD7 (SELECT#/DESELECT_CARD)\n"); + + SD_SetFPGA(SD_ARG0,(0x0000)); /* Argument(15:0) = stuff bits*/ + SD_SetFPGA(SD_ARG1,(SD_RCA)); /* Argument(31:16) = RCA */ + + SD_Command(SD_CMD_CMD | SELECT_CARD); /* CMD7発行、レスポンス(R1b)待ち */ + +#if ADD_CHECK + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1) の Card Status チェック */ +#endif + + return SDCARD_ErrStatus; + +} + +/*---------------------------------------------------------------------------* + Name: SD_SetBlockLength + + Description: set of block length. + SDカードのブロックレングスを設定する。 + 注意:multiple read のときに SD_SIZEレジスタに512以外の値を + 設定すると正しく動作しない(IP reg spec資料より)。 + + Arguments: ulBlockLength : bytes of a block ( must be multiplier of 2) + (memo : the default block length is as specified in the CSD.) + 1ブロックの長さ:2の乗数を指定すること + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SetBlockLength(u32 ulBlockLength) +{ + u16 usValue; + + PRINTDEBUG( " CMD16 (SET_BLOCKLEN)\n"); + + /*------- IPの transfer length を設定 -------*/ + if(ulBlockLength == 512){ + SD_SetFPGA(SD_SIZE,(SD_SIZE_DATA_LENGTH_512B)); /* SDカードデータ転送サイズ 512Bytes 設定 */ + }else{ + usValue = (u16)ulBlockLength; /* 16bitに変換 */ + SD_SetFPGA(SD_SIZE,(usValue)); /* IPにSDカードデータ転送サイズを設定 */ + }/*------------------------------------------*/ + + /*------- カードの設定 -------*/ + /* argument(31:0) = block length */ + SD_SetFPGA(SD_ARG0,(((LELONG *)&ulBlockLength)->dt2word.low)); + SD_SetFPGA(SD_ARG1,(((LELONG *)&ulBlockLength)->dt2word.high)); + + SD_Command(SD_CMD_CMD | SET_BLOCKLEN); /* CMD16発行、レスポンス(R1)待ち */ +#if ADD_CHECK + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1) の Card Status チェック */ +#endif + /*----------------------------*/ + return SDCARD_ErrStatus; +} + + +/*---------------------------------------------------------------------------* + Name: SD_SendCID + + Description: get card identification data (128bits). + SDカードのCID値を取得する + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SendCID(void) +{ + PRINTDEBUG( " CMD2 (ALL_SEND_CID)\n"); + /* Argument(31:0) = stuff bits */ + SD_Command(SD_CMD_CMD | ALL_SEND_CID); /* CMD2発行、レスポンス(R2)待ち */ + + if(!SDCARD_ErrStatus){ /* エラーステータスの確認(エラー無し?) */ + SD_GetFPGA(SD_CID[0],SD_RSP0); + SD_GetFPGA(SD_CID[1],SD_RSP1); + SD_GetFPGA(SD_CID[2],SD_RSP2); + SD_GetFPGA(SD_CID[3],SD_RSP3); + SD_GetFPGA(SD_CID[4],SD_RSP4); + SD_GetFPGA(SD_CID[5],SD_RSP5); + SD_GetFPGA(SD_CID[6],SD_RSP6); + SD_GetFPGA(SD_CID[7],SD_RSP7); + } + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_SendCSD + + Description: get card specific data + SDカードのCSD値を取得する + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SendCSD(void) +{ + PRINTDEBUG( " CMD9 (SEND_CSD)\n"); + + SD_SetFPGA(SD_ARG0,(0x0000)); /* Argument(15:0) = stuff bits */ + SD_SetFPGA(SD_ARG1,(SD_RCA)); /* Argument(31:16) = RCA */ + + SD_Command(SD_CMD_CMD | SEND_CSD); /* CMD9発行、レスポンス(R2)待ち */ + + if(!SDCARD_ErrStatus){ /* エラーステータスの確認(エラー無し?) */ + SD_GetFPGA(SD_CSD[0],SD_RSP0); + SD_GetFPGA(SD_CSD[1],SD_RSP1); + SD_GetFPGA(SD_CSD[2],SD_RSP2); + SD_GetFPGA(SD_CSD[3],SD_RSP3); + SD_GetFPGA(SD_CSD[4],SD_RSP4); + SD_GetFPGA(SD_CSD[5],SD_RSP5); + SD_GetFPGA(SD_CSD[6],SD_RSP6); + SD_GetFPGA(SD_CSD[7],SD_RSP7); + } + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_SendStatus + + Description: send status read command (compatible to the MMC protocol). + マルチメディアカード互換方式でのstatus取得コマンド発行 + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SendStatus(void) +{ + PRINTDEBUG( " CMD13 (Send STATUS)\n"); + + SD_SetFPGA(SD_ARG0,(0x0000)); /* Argument(15:0) = stuff bits */ + SD_SetFPGA(SD_ARG1,(SD_RCA)); /* Argument(31:16) = RCA */ + + /* カード排出の重複を防ぐため */ + SD_AndFPGA(SD_INFO2,(~SD_INFO2_ERR_ALLCLR)); /* SD Card I/F の 全てのエラーをクリア */ + SD_AndFPGA(SD_INFO1,(~SD_INFO1_RES_END)); /* SD_INFO1レジスタの Response end クリア */ + SD_AndFPGA(SD_INFO2_MASK,(~SD_INFO2_MASK_ALLERRMASK)); /* SD Card I/F の 全エラー割込み許可 */ + + SD_SetFPGA(SD_CMD,(SD_CMD_CMD | SD_SEND_STATUS)); /* CMD13発行 */ + + while(!SD_CheckFPGAReg(SD_INFO1,SD_INFO1_RES_END)){ /* Response end (R1)待ち */ + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ + break; + } + } + + SD_OrFPGA(SD_INFO2_MASK,(SD_INFO2_MASK_ALLERRMASK)); /* SD Card I/F の 全エラー割込み禁止 */ + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_SendSCR + + Description: get condition data. + SDカード内のSCRレジスタ値取得コマンド発行。この後カードは + DATライン経由で1ブロック(8Bytesに設定しておくこと)送信してくる。 + MultiBlock R/W と異なり、DATライン経由で転送されてくるSDカードの + レジスタは、MSBから先に送られてくることに注意。 + (Physical Layer Specification 2.00 p12-13参照) + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SendSCR(void) +{ + PRINTDEBUG( " ACMD51 (Send SCR)\n"); + /* Argument(31:0) = stuff bits */ +#if ADD_CHECK + SD_TransReadyFPGA(); /* 関連レジスタ初期化 */ + SD_AndFPGA(SD_INFO2_MASK,(~SD_INFO2_MASK_BRE)); /* SDカードからのデータ読出し要求割込み許可*/ + + SD_TransCommand((SD_CMD_ACMD | SEND_SCR)); /* SCR取得コマンド発行、レスポンス(R1)待ち */ +#endif + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_SDStatus + + Description: send status read command (for SD card only). + SDカード専用方式でのstatus取得コマンド発行。この後カードは + DATライン経由で1ブロック(64Bytesに設定しておくこと)送信してくる。 + MultiBlock R/W と異なり、DATライン経由で転送されてくるSDカードの + レジスタは、MSBから先に送られてくることに注意。 + (Physical Layer Specification 2.00 p12-13参照) + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SDStatus(void) +{ + PRINTDEBUG( " ACMD13 (SD_SD STATUS)\n"); + /* Argument(31:0) = stuff bits */ + SD_TransReadyFPGA(); /* 関連レジスタ初期化 */ + SD_AndFPGA(SD_INFO2_MASK,(~SD_INFO2_MASK_BRE)); /* SDカードからのデータ読出し要求割込み許可*/ + + SD_TransCommand((SD_CMD_ACMD | SD_STATUS)); /* ACMD13 Send the SD_CARD status コマンド発行、レスポンス(R1)待ち */ + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_MultiReadBlock + + Description: send multiple block read command. + マルチブロックリードコマンド発行。この後カードは + DATライン経由でデータを送信してくる。 + + Arguments: ulOffset : offset address to read(BYTE). + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_MultiReadBlock(u32 ulOffset) +{ + PRINTDEBUG( " CMD18 (READ_MULTIPLE_BLOCK)\n"); + + SD_TransReadyFPGA(); /* INFOレジスタ初期化 */ + if( !SDCARD_UseFifoFlag) { /* FIFOを使わないとき */ + SD_AndFPGA(SD_INFO2_MASK,(~SD_INFO2_MASK_BRE)); /* SDカードからのデータ読出し要求割込み許可*/ + } + + /* 読み込み開始アドレス(オフセット)設定 */ + SD_SetFPGA(SD_ARG0,(((LELONG *)&ulOffset)->dt2word.low)); + SD_SetFPGA(SD_ARG1,(((LELONG *)&ulOffset)->dt2word.high)); + + SD_TransCommand((READ_MULTIPLE_BLOCK)); /* CMD18(マルチセクタリードコマンド)発行、レスポンス(R1)待ち */ + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_ClockDivSet + + Description: set clock speed into the SD card. + (notice : clock into IP is 33.51MHz.) + (memo : default clock is 262KHz.) + SDカードへのクロック速度を設定する。CTRからIMCLK端子でIPに供給 + される基本クロックは33.51MHz。 + 起動直後のデフォルトでは128分周(262KHz)が設定されている。 + + Arguments: CSD responce include "TRAN_SPEED". + 予めCSDをSDカードから読み出しておいた、TRAN_SPEEDが入っている + 部分のCSD(CSD[5])。SDカード側で対応可能な周波数を参照するため。 + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +u16 SD_ClockDivSet(u16 usTranSpeed) +{ + u16 usTranTime; + + //CTRでは、IMCLKに入力されるクロックは 33.51Mhz + //SD_CLK_CTRL の default値は 0x0020( 128分周 = 262kHz) + /*------*/ + usTranTime = (u16)((usTranSpeed >> 11) & 0x000F); /* CSD[103:96] の time value(=4bit) 取得 */ + + usTranSpeed &= CSD_TRANSFER_RATE; /* CSD[103:96] の transfer rate unit を取得 */ + usTranSpeed = (u16)(usTranSpeed >> 8); /* transfer rate unit を下位8ビットに設定する */ + /*------*/ + + + switch( usTranSpeed) { + /*--- 100kbit/s(one dat line) = 100KHz, の倍数のとき ---*/ + case CSD_TRAN_SPEED_100K: + if(usTranTime > 0x000C){ /* time value が 5.5 より大きい? */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_64)); /* 523KHz */ + } + else{ + if(usTranTime > 0x0006){ /* time value が 2.5 より大きい? */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_128)); /* 262KHz */ + }else{ + if( usTranTime == 1) { /* time value が 1.0 ? */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_512)); /* 65KHz */ + }else{ /* time value が 1.2〜2.5 のとき */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_256)); /* 131KHz */ + } + } + } + break; + /*--- 1Mbit/s(one dat line) = 1MHz, の倍数のとき ---*/ + case CSD_TRAN_SPEED_1M: + if(usTranTime == 0x0001){ /* time value が 1.0 ? */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_64)); /* 523KHz */ + } + else{ + if(usTranTime <= 0x0005){ /* time value が 2.0 以下? */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_32)); /* 1.05MHz */ + } + else{ + if(usTranTime <= 0x0009){ /* time value が 4.0 以下? */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_16)); /* 2.095MHz */ + }else{ /* time value が 4.5 以上のとき */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_8)); /* 4.18MHz */ + } + } + } + break; + /*--- 10Mbit/s(one dat line) = 10MHz, の倍数のとき ---*/ + case CSD_TRAN_SPEED_10M: + if(usTranTime > 0x0004){ /* time value が 1.5 より大きい? */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_2)); /* 16.76MHz */ + }else{ /* time value が 1.5 以下のとき */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_4)); /* 8.38MHz */ + } + break; + /*--- 100Mbit/s(one dat line) = 100MHz, の倍数のとき ---*/ + case CSD_TRAN_SPEED_100M: /* time value がどんな値であっても */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_2)); /* 16.76MHz */ + break; + default: + if( usTranSpeed != 7) { /* reserved値(=7)以外か? */ + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_2)); /* 16.76MHz */ + } /* (松下drvでは、4以上なら288KHzにしている) */ + break; /* transfer rate unit が 7 のときは何もしない */ + } + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_EnableClock + + Description: enable clock + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_EnableClock( void) +{ + SD_OrFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_SDCLKEN)); +} + +/*---------------------------------------------------------------------------* + Name: SD_DisableClock + + Description: disable clock (for power save). + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_DisableClock( void) +{ + SD_AndFPGA(SD_CLK_CTRL,(~SD_CLK_CTRL_SDCLKEN)); +} + + +/*---------------------------------------------------------------------------* + Name: SD_SelectBitWidth + + Description: set bus width for SD Card ( Not for MMC Card). + (The allowed data bus widths are given in SCR register.) + SDカードのDATラインバス幅を1bitまたは4bitに設定する。 + SDカードが4bitに対応していないか、MMCカードの場合はバス幅を4bitに + 設定することはできない。SDカードの許容するバス幅は、予め取得済みの + SCRレジスタによって参照している。 + + Arguments: b4bit : TRUE = 4bit, FALSE = 1bit + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +u16 SD_SelectBitWidth(s16 b4bit) +{ + /*--- カードの仕様(SCR)を参照 ---*/ +#if ADD_CHECK + if( b4bit){ /* SCRレジスタがリード済であること! */ + if( !(SD_SCR[0] & SCR_DAT_BUS_WIDTH_4BIT)) { /* カードが4bit幅に対応していないか? */ + b4bit = FALSE; /* 対応していない場合は1bit幅を指定しておく */ + } + }/*------------------------------*/ +#endif + /*--- 4bit(TRUE) 指定のとき ---*/ + if(b4bit){ + if(!SDCARD_MMCFlag){ /* MMCカードフラグ OFF?(ONなら何もしない) */ + if(SD_AppCommand()){ /* RCA設定後 CMD55発行 */ + return SDCARD_ErrStatus; /* CMD55が正常終了しなかったらエラー終了 */ + } + SD_SetFPGA(SD_ARG0,(0x0002)); /* Argument(1:0) = (10) to 4bit,(00) to 1bit */ + SD_SetFPGA(SD_ARG1,(0x0000)); /* Argument(31:2) = stuff bits */ + PRINTDEBUG( " ACMD6 (SET_BUS_WIDTH:4bit)\n"); + SD_Command(SD_CMD_ACMD | SET_BUS_WIDTH); /* ACMD6(ビット幅選択コマンド)発行、レスポンス(R1)待ち */ +#if ADD_CHECK + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1)の Card Status チェック */ +#endif + if(!SDCARD_ErrStatus){ /* エラーなし? */ + SD_AndFPGA(SD_OPTION,(~SD_OPTION_WIDTH_1BIT)); /* IPにビット幅の設定(4bit幅) */ + } + } + }/*--- 1bit(FALSE) 指定のとき ---*/ + else{ + if(!SDCARD_MMCFlag){ /* MMCカードフラグ OFF?(ONなら何もしない) */ + if(SD_AppCommand()){ /* RCA設定後 CMD55発行処理 */ + return SDCARD_ErrStatus; /* CMD55が正常終了しなかったらエラー終了 */ + } + SD_SetFPGA(SD_ARG0,(0x0000)); /* Argument(1:0) = bus width : 1bit */ + SD_SetFPGA(SD_ARG1,(0x0000)); /* Argument(31:2) = stuff bits */ + PRINTDEBUG( " ACMD6 (SET_BUS_WIDTH:1bit)\n"); + SD_Command(SD_CMD_ACMD | SET_BUS_WIDTH); /* ACMD6(ビット幅選択コマンド)発行、レスポンス(R1)待ち */ +#if ADD_CHECK + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1)の Card Status チェック */ +#endif + } + if(!SDCARD_ErrStatus){ /* エラーステータスの確認(エラー無し?) */ + SD_OrFPGA(SD_OPTION,(SD_OPTION_WIDTH_1BIT)); /* IPにビット幅の設定(1bit幅) */ + } + }/*------------------------------*/ + + return SDCARD_ErrStatus; +} + + +/*---------------------------------------------------------------------------* + Name: MMCP_WriteBusWidth + + Description: set bus width for SD Card ( Not for MMC Card). + (The allowed data bus widths are given in SCR register.) + MMCplusカードのDATラインバス幅を1bitまたは4bitに設定する。 + SDまたはMMCカードの場合はバス幅を4bitに設定することはできない。 + + Arguments: b4bit : TRUE = 4bit, FALSE = 1bit + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +u16 MMCP_WriteBusWidth(s16 b4bit) +{ + if( !SDCARD_MMCFlag) { + return 1; + } + + /* 書き込み開始オフセット設定 */ + if( b4bit) { + SD_SetFPGA(SD_ARG0,(0x0100)); /* Argument(15:8)=(0x1) to 4bit,(0x0) to 1bit */ + }else{ + SD_SetFPGA(SD_ARG0,(0x0000)); /* Argument(15:8)=(0x1) to 4bit,(0x0) to 1bit */ + } + SD_SetFPGA(SD_ARG1,(0x03B7)); /* Argument(25:24)=(0x3)Write, (23:16)=(183)Index */ + + /* CMD6(ビット幅選択コマンド)発行、レスポンス(R1)待ち */ + SD_Command(SD_CMD_CMD | EXT_CSD_ACCESS); + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: MMCP_BusTest + + Description: バスのテストを行う。 + この後カードにDATライン経由でデータを送信する必要がある。 + + Arguments: readflag : リード時TRUE, ライト時FALSE + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +u16 MMCP_BusTest( BOOL readflag) +{ + /**/ + SD_TransReadyFPGA(); /* INFOレジスタ初期化 */ + SD_AndFPGA(SD_INFO2_MASK,(~SD_INFO2_MASK_BWE)); /* SDカードからのデータ書込み要求割込み許可 */ + + /**/ + SD_SetFPGA( SD_ARG0, 0); + SD_SetFPGA( SD_ARG1, 0); + + if( readflag) { + SD_TransCommand( 14); + }else{ + SD_TransCommand( 19); + } + + return( SDCARD_ErrStatus); +} + + +/*---------------------------------------------------------------------------* + Name: SD_FPGA_irq + + Description: handler of data transfer request interrupt from card, + check and clear interrupt register of IP. + カードからの転送要求(BREまたはBWE)割り込み発生時に + どちらかを調べて返し、INFO2の当該割り込み要求フラグをクリアする。 + + Arguments: None + + Returns: TRUE : BRE割り込み発生 + FALSE : BWE割り込み発生 + *---------------------------------------------------------------------------*/ +s16 SD_FPGA_irq(void) +{ + /*--- FIFOを使うとき ---*/ + if( SDCARD_UseFifoFlag) { + if( SD_CheckFPGAReg( *SDIF_CNT_L, (u16)SDIF_CNT_FFIE)) { /* FULL割り込み許可のとき */ + return TRUE; + }else{ + if(!SD_CheckFPGAReg(SD_INFO2_MASK,SD_INFO2_MASK_BWE)){ /* SDカードからのデータ書込み要求あり? */ + SD_AndFPGA(SD_INFO2,(~SD_INFO2_BWE)); /* SD用バッファ制御 Write Enable リセット*/ + } + return FALSE; + } + }else{ /*--- FIFOを使わないとき ---*/ + if(!SD_CheckFPGAReg(SD_INFO2_MASK,SD_INFO2_MASK_BRE)){ /* SDカードからのデータ読出し要求あり? */ + SD_AndFPGA(SD_INFO2,(~SD_INFO2_BRE)); /* SD用バッファ制御 Read Enable リセット*/ + return TRUE; + } + if(!SD_CheckFPGAReg(SD_INFO2_MASK,SD_INFO2_MASK_BWE)){ /* SDカードからのデータ書込み要求あり? */ + SD_AndFPGA(SD_INFO2,(~SD_INFO2_BWE)); /* SD用バッファ制御 Write Enable リセット*/ + return FALSE; + } + } + return FALSE; +} + +/*---------------------------------------------------------------------------* + Name: SD_StopTransmission + + Description: manual send CMD12 to terminate translate. + 手動でCMD12を発行し、転送終了をFPGAに通知する + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_StopTransmission(void) +{ + SD_OrFPGA(SD_STOP,(SD_STOP_STP)); /* データ転送終了設定 */ +} + +/*---------------------------------------------------------------------------* + Name: SD_TransEndFPGA + + Description: clear the transfer interrupt. + カード転送終了時に転送関連の割り込みを禁止に戻しておく + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_TransEndFPGA(void) +{ + SD_OrFPGA(SD_INFO2_MASK,(SD_INFO2_MASK_ALLERRMASK)); /* SD Card I/F の 全エラー割込み禁止 */ + SD_OrFPGA(SD_INFO1_MASK,(SD_INFO1_MASK_ALL_END)); /* R/W アクセス終了割込み禁止 */ + + if(!SD_CheckFPGAReg(SD_INFO2_MASK,SD_INFO2_MASK_BRE)){ /* SDカードからのデータ読込み要求割込み許可か? */ + SD_OrFPGA(SD_INFO2_MASK,(SD_INFO2_MASK_BRE)); /* SDカードからのデータ読込み要求割込み禁止 */ + } + if(!SD_CheckFPGAReg(SD_INFO2_MASK,SD_INFO2_MASK_BWE)){ /* SDカードからのデータ書込み要求割込み許可か? */ + SD_OrFPGA(SD_INFO2_MASK,(SD_INFO2_MASK_BWE)); /* SDカードからのデータ書込み要求割込み禁止 */ + } +} + +/*---------------------------------------------------------------------------* + Name: SD_CheckStatus + + Description: check the card status in the R1. + R1レスポンスのカードステータスをチェックする + + Arguments: bRead : 読み込み処理時 = TRUE + + Returns: 0 : success + >0 : error + *---------------------------------------------------------------------------*/ +u16 SD_CheckStatus(BOOL bRead) +{ + /* コマンドレスポンス(R1)の[39:8] から Card status を取得 */ + SD_GetFPGA((((LELONG *)&SDCARD_Status)->dt2word.low),SD_RSP0); + SD_GetFPGA((((LELONG *)&SDCARD_Status)->dt2word.high),SD_RSP1); + + /*--- 松下サンプルドライバでやっている処理 ---*/ + if(bRead){ /* リード時か? */ + if(!(SDCARD_ErrStatus & SDMC_ERR_TIMEOUT)){ /* タイムアウト発生していないか? */ + SDCARD_Status &= ~SDCARD_STATUS_OUT_OF_RANGE; /* OUT_OF_RANGEフラグを落とす */ + } + }/*-------------------------------------------*/ + + PRINTDEBUG( " SD_CheckStatus ======== 0x%x\n", SDCARD_Status); + + if(SDCARD_Status & RSP_R1_STATUS_ERR){ /* コマンドレスポンス(R1)のカードステータスがエラーか確認 */ + SD_SetErr(SDMC_ERR_R1_STATUS); /* コマンドレスポンス(R1)のカードステータス エラー */ + } + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_SwapByte + + Description: swap bytes in a 16bit data. + 16bitデータの上位と下位を入れ換える + + Arguments: data : 16bit data + + Returns: swapped data + *---------------------------------------------------------------------------*/ +u16 SD_SwapByte(u16 *data) +{ + u16 usDATA; + + usDATA = *data; + usDATA = (u16)(((usDATA & 0x00FF) << 8) | ((usDATA & 0xFF00) >> 8)); + + return usDATA; +} + +/*---------------------------------------------------------------------------* + Name: SD_EnableSeccnt + + Description: enable SD_SECCNT register. + SD_SECCNT を有効化して値をセットする。 + + Arguments: ulSDCARD_SectorCount : セクタカウント値(1セクタ=512byte) + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_EnableSeccnt( u32 ulSDCARD_SectorCount) +{ + u16 usSector; + + usSector = (u16)ulSDCARD_SectorCount; + SD_OrFPGA(SD_STOP,SD_STOP_SEC_ENABLE); /* SD_SECCNTレジスタを有効にする */ + SD_SetFPGA(SD_SECCNT,usSector); /* SD_SECCNTレジスタに転送セクタカウントを設定 */ +} + +/*---------------------------------------------------------------------------* + Name: SD_DisableSeccnt<現在は未使用関数> + + Description: disable SD_SECCNT register. + SD_SECCNT を無効化する。 + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_DisableSeccnt( void) +{ + u16 i; + + /*--- 松下ドライバでやっていること ---*/ + for ( i=0; i<50000; i++) /* カウント値50000では足りないことあり */ + { /* SD_INFO1レジスタの R/W access all end 待ち */ + if(SD_CheckFPGAReg(SD_INFO1,SD_INFO1_ALL_END)) { + break; + } + }/*-----------------------------------*/ + SD_AndFPGA(SD_STOP,(~SD_STOP_SEC_ENABLE)); /* SD_SECCNTレジスタを無効にする */ +} + +/*---------------------------------------------------------------------------* + Name: SD_SetErr + + Description: set error flag + 指定のエラーフラグをセットする + + Arguments: Error : error flag to set + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_SetErr(u16 Error) +{ + OSIntrMode irq_core_flag; + +#if (TARGET_OS_CTR == 1) + irq_core_flag = osDisableInterrupts(); /* 割込み禁止 */ + SDCARD_ErrStatus |= Error; /* エラーステータスを設定 */ + osRestoreInterrupts( irq_core_flag); /* 割り込み設定を元に戻す */ +#else + irq_core_flag = OS_DisableInterrupts(); + SDCARD_ErrStatus |= Error; /* エラーステータスを設定 */ + OS_RestoreInterrupts( irq_core_flag); +#endif +} + +/*---------------------------------------------------------------------------* + Name: SD_ClrErr + + Description: clear error flag + 指定のエラーフラグをクリアする + + Arguments: Error : error flag to clear + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_ClrErr(u16 Error) +{ + OSIntrMode irq_core_flag; + +#if (TARGET_OS_CTR == 1) + irq_core_flag = osDisableInterrupts(); /* 割込み禁止 */ + SDCARD_ErrStatus &= ~(Error); /* エラーステータスをクリア */ + osRestoreInterrupts( irq_core_flag); /* 割り込み設定を元に戻す */ +#else + irq_core_flag = OS_DisableInterrupts(); /* 割込み禁止 */ + SDCARD_ErrStatus &= ~(Error); /* エラーステータスをクリア */ + OS_RestoreInterrupts( irq_core_flag); /* 割り込み設定を元に戻す */ +#endif +} + + +/*---------------------------------------------------------------------------* + Name: SD_TransReadyFPGA + + Description: setup for the command that the card will response and request data transfer. + DATラインでのデータ転送が発生するコマンド発行前の転送準備 + (BRE, BWE割り込みの許可は別途行うこと) + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SD_TransReadyFPGA(void) +{ + /* 関連レジスタ初期化 */ + SD_AndFPGA(SD_INFO2,(~SD_INFO2_ERR_ALLCLR)); /* SD Card I/F の 全てのエラーをクリア */ + SD_AndFPGA(SD_INFO1,(~(SD_INFO1_RES_END | SD_INFO1_ALL_END))); /* SD_INFO1レジスタの Response end と access all end クリア */ + SD_AndFPGA(SD_INFO2,(~SD_INFO2_BRE)); /* SD用バッファ制御 Read Enable クリア*/ + SD_AndFPGA(SD_INFO2,(~SD_INFO2_BWE)); /* SD用バッファ制御 Write Enable クリア*/ + SD_AndFPGA(SD_STOP,(~SD_STOP_STP)); /* データ転送終了クリア */ + + /* 割り込み関連許可 */ + SD_AndFPGA(SD_INFO2_MASK,(~SD_INFO2_MASK_ALLERRMASK)); /* SD Card I/F の 全エラー割込み許可 */ + SD_AndFPGA(SD_INFO1_MASK,(~SD_INFO1_MASK_ALL_END)); /* R/W access all end 割込み許可(Responseはポーリングで見るため許可しない) */ +} + + +/*---------------------------------------------------------------------------* + Name: SD_TransCommand + + Description: send command that the card will response and request data transfer. + コマンドを送出する(DATラインでのデータ転送が発生するコマンド用) + + Arguments: ucCommand : command number + + Returns: 0 : success + >0 : error + *---------------------------------------------------------------------------*/ +u16 SD_TransCommand(u16 ucCommand) +{ + SD_SetFPGA(SD_CMD,(ucCommand)); /* コマンド発行 */ + + while(!SD_CheckFPGAReg(SD_INFO1,SD_INFO1_RES_END)){ /* Response end 待ち */ + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ + break; + } + } + SD_CheckStatus(FALSE); /* R1レスポンスのcard statusチェック*/ + + if(SDCARD_ErrStatus & SDMC_ERR_R1_STATUS){ + SD_StopTransmission(); /* カード転送終了をFPGAに通知 */ + } + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_CheckFPGAReg + + Description: check register bits. + レジスタの状態をチェックする + + Arguments: reg : 16bit register data + value : mask data + + Returns: TRUE : some of mask data bits are 1 on the register. + FALSE : no mask data bits is 1 on the register. + *---------------------------------------------------------------------------*/ +BOOL SD_CheckFPGAReg(u16 reg,u16 value) +{ + if( reg & value) { + return TRUE; + }else{ + return FALSE; + } +} + +/*---------------------------------------------------------------------------* + Name: SD_SendNumWRSectors + + Description: send "number of well written blocks" command (for SD card only). + SDカード専用方式でのライト済みセクタ数取得コマンド発行。この後カードは + DATライン経由で1ブロック(4Bytesに設定しておくこと)送信してくる。 + MultiBlock R/W と異なり、DATライン経由で転送されてくるSDカードの + レジスタは、MSBから先に送られてくることに注意。 + (Physical Layer Specification 2.00 p12-13参照) + + Arguments: None + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_SendNumWRSectors(void) +{ + PRINTDEBUG( " ACMD22 (SEND_NUM_WR_SECTORS)\n"); + /* Argument(31:0) = stuff bits */ + SD_TransReadyFPGA(); /* 関連レジスタ初期化 */ + SD_AndFPGA(SD_INFO2_MASK,(~SD_INFO2_MASK_BRE)); /* SDカードからのデータ読込み要求割込み許可 */ + + SD_TransCommand((SD_CMD_ACMD | SEND_NUM_WR_SECTORS)); /* ACMD22(書きこみ完了セクタ数取得コマンド)発行、レスポンス(R1)待ち */ + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SD_MultiWriteBlock + + Description: send multiple block write command. + マルチブロックライトコマンド発行。この後カードに + DATライン経由でデータを送信する必要がある。 + + Arguments: ulOffset : offset address to write(BYTE). + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +u16 SD_MultiWriteBlock(u32 ulOffset) +{ + PRINTDEBUG( " CMD25 (WRITE_MULTIPLE_BLOCK)\n"); + + SD_TransReadyFPGA(); /* INFOレジスタ初期化 */ +// if( !SDCARD_UseFifoFlag) { /* FIFOを使わないとき */ + SD_AndFPGA(SD_INFO2_MASK,(~SD_INFO2_MASK_BWE)); /* SDカードからのデータ書込み要求割込み許可 */ +// } + + /* 書き込み開始オフセット設定 */ + SD_SetFPGA(SD_ARG0,(((LELONG *)&ulOffset)->dt2word.low)); + SD_SetFPGA(SD_ARG1,(((LELONG *)&ulOffset)->dt2word.high)); + + SD_TransCommand(WRITE_MULTIPLE_BLOCK); /* CMD25(マルチセクタライトコマンド)発行 */ + + return SDCARD_ErrStatus; +} + + diff --git a/build/libraries/devices/sdmc/ARM7/sdif_ip.h b/build/libraries/devices/sdmc/ARM7/sdif_ip.h new file mode 100644 index 0000000..96493b4 --- /dev/null +++ b/build/libraries/devices/sdmc/ARM7/sdif_ip.h @@ -0,0 +1,349 @@ +/* +** Copyright (c) 2000-2001 Matsushita Electric Industrial Co., Ltd. +** All Rights Reserved. +*/ + +/* +** $Module SDカードアクセスモジュール・インクルード +** $Filename SD_CARD_IP.H +** $Version 1.0 版 +** $Date 01/02/16 +** $Log 01/02/16 rev1.0作成 +** 松下電器産業(株)半導体開発本部 +*/ + + +#ifndef __SD_CARD_IP_H__ +#define __SD_CARD_IP_H__ + +//#define IO3 0 /* Insert Remove SW = FALSE IO3 = TRUE */ + +//#define MAX_SD_CLOCK_4M 0 /* MAX SD Clock 4.608MHz */ +//#define MAX_SD_CLOCK_9M 1 /* MAX SD Clock 9.216MHz */ + + +/*--------------------------------------------- + ラッパーレジスタのビット制御(フラグ定義) +---------------------------------------------*/ +/* SD_CNTレジスタ */ +#define SDIF_CNT_USEDTC (0x0001) /* DTC使用フラグ (R/W) */ +#define SDIF_CNT_USEFIFO (0x0002) /* FIFO使用フラグ (R/W) */ +#define SDIF_CNT_FULL (0x0100) /* FIFO FULLフラグ (RO) */ +#define SDIF_CNT_NEMP (0x0200) /* FIFO NOT EMPTYフラグ (RO) */ +#define SDIF_CNT_FCLR (0x0400) /* FIFO クリアフラグ (WO) */ +#define SDIF_CNT_FFIE (0x0800) /* FIFO FULLで割り込み (R/W) */ +#define SDIF_CNT_FEIE (0x1000) /* FIFO EMPTYで割り込み (R/W) */ + +#define SDCARD_UseFifoFlag ((*SDIF_CNT) & SDIF_CNT_USEFIFO) + +/*------------------------------------- +レジスタのビット制御(フラグ定義) +-------------------------------------*/ +/* SD_CMDレジスタ*/ +#define SD_CMD_CMD 0x0000 /* SDカードへの通常アクセス(CMD) */ +#define SD_CMD_ACMD 0x0040 /* SDカードへのセキュリティアクセス(ACMD) */ + +/* SD_STOPレジスタ */ +#define SD_STOP_STP 0x0001 /* データ転送終了を知らせる */ +#define SD_STOP_SEC_ENABLE 0x0100 /* SD_SECCNTレジスタ有効(セクタカウントレジスタ) */ + +/* SD_SECCNTレジスタ */ +#define SD_SECCNT_END 0x0000 /* SD_SECCNTレジスタ カウントチェック */ + +/* SD_SIZEレジスタ */ +#define SD_SIZE_DATA_LENGTH_1B 0x0001 /* SDカード転送データサイズ 1Bytes */ +#define SD_SIZE_DATA_LENGTH_2B 0x0002 /* SDカード転送データサイズ 2Bytes */ +#define SD_SIZE_DATA_LENGTH_4B 0x0004 /* SDカード転送データサイズ 4Bytes */ +#define SD_SIZE_DATA_LENGTH_8B 0x0008 /* SDカード転送データサイズ 8Bytes (SCR) */ +#define SD_SIZE_DATA_LENGTH_16B 0x0010 /* SDカード転送データサイズ 16Bytes */ +#define SD_SIZE_DATA_LENGTH_32B 0x0020 /* SDカード転送データサイズ 32Bytes */ +#define SD_SIZE_DATA_LENGTH_64B 0x0040 /* SDカード転送データサイズ 64Bytes (SD_Status) */ +#define SD_SIZE_DATA_LENGTH_128B 0x0080 /* SDカード転送データサイズ 128Bytes */ +#define SD_SIZE_DATA_LENGTH_256B 0x0100 /* SDカード転送データサイズ 256Bytes */ +#define SD_SIZE_DATA_LENGTH_512B 0x0200 /* SDカード転送データサイズ 512Bytes (データ) */ + +/* SD_OPTIONレジスタ */ +#define SD_OPTION_WIDTH_1BIT 0x8000 /* ビット幅の選択 1bit幅 */ +#define SD_OPTION_MSEL_C2NOUSE 0x4000 /* C2モジュール未使用 */ +#define SD_CD_DETECT_TIME 0xFFF0 /* CD 検出タイムだけをクリアするためのマスク */ + +/* SD_INFO2レジスタ */ +#define SD_INFO2_ERR_ILA 0x8000 /* イリーガルアクセスエラー */ +#define SD_INFO2_BWE 0x0200 /* SDカードから512byteのデータ書込み要求 */ +#define SD_INFO2_BRE 0x0100 /* SDカードから512byteのデータ読込み要求 */ +#define SD_INFO2_ERR_ALLCLR 0x807F /* SD Card エラーレジスタクリア */ +#define SD_INFO2_ERR_SDDAT0 0x0080 /* SD Card Busy bit */ +#define SD_INFO2_ERR_RESTIMEOUT 0x0040 /* レスポンスタイムアウトエラー */ +#define SD_INFO2_ERR_UNDERFLOW 0x0020 /* FIFO アンダーフローエラー */ +#define SD_INFO2_ERR_OVERFLOW 0x0010 /* FIFO オーバーフローエラー */ +#define SD_INFO2_ERR_TIMEOUT 0x0008 /* レスポンス以外のタイムアウトエラー */ +#define SD_INFO2_ERR_END 0x0004 /* フレーム終了認識できないときの(END)エラー */ +#define SD_INFO2_ERR_CRC 0x0002 /* CRC エラー */ +#define SD_INFO2_ERR_CMD 0x0001 /* CMDエラー */ + +#define SD_INFO2_RW_SET 0x0300 /* SDカード Read/Write 要求割込み要因チェック */ +#define SD_INFO2_ERROR_SET 0x807F /* SDカード エラー割込み要因チェック */ + +/* SD_INFO2_MASKレジスタ */ +#define SD_INFO2_MASK_ILA 0x8000 /* イリーガルアクセスエラー割込みマスク */ +#define SD_INFO2_MASK_BWE 0x0200 /* SDカードからのデータ書込み要求割込み禁止 */ +#define SD_INFO2_MASK_BRE 0x0100 /* SDカードからのデータ読込み要求割込み禁止 */ +#define SD_INFO2_MASK_ALLERRMASK 0x807F /* 全エラー割り込み禁止 */ +#define SD_INFO2_MASK_EXCEPT_OVERFLOW 0x802F /* 全エラー割り込み禁止 FIFO Overflow Errorを除く */ +#define SD_INFO2_MASK_RESTIMEOUT 0x0040 /* Time out 割込みEnable */ +#define SD_INFO2_MASK_UNDERFLOW 0x0020 /* FIFO アンダーフロー 割込みEnable */ +#define SD_INFO2_MASK_OVERFLOW 0x0010 /* FIFO オーバーフロー 割込みEnable */ +#define SD_INFO2_MASK_TIMEOUT 0x0008 /* Time out 割込みEnable */ +#define SD_INFO2_MASK_END 0x0004 /* END エラー 割込みEnable */ +#define SD_INFO2_MASK_CRC 0x0002 /* CRC エラー 割込みEnable */ +#define SD_INFO2_MASK_CMD 0x0001 /* CMD エラー 割込みEnable */ +#define SD_INFO2_MASK_ERRSET 0x807F /* SDカード エラー割込み要因チェック */ + +/* SD_INFO1レジスタ */ +#define SD_INFO1_DAT3DETECT 0x0400 /* (IO3検出) card detect(検出=1) : CTRでは使用できない*/ +#define SD_INFO1_DAT3INSERT 0x0200 /* (IO3検出) card inserted(挿入=1) : CTRでは使用できない */ +#define SD_INFO1_DAT3REMOVE 0x0100 /* (IO3検出) card removed(抜け=1) : CTRでは使用できない */ +#define SD_INFO1_DAT3INIT 0x0300 /* (IO3検出) の初期化 */ +#define SD_INFO1_WRITEPROTECT 0x0080 /* write protect(書き込み禁止=1) */ +#define SD_INFO1_DETECT 0x0020 /* card detect(検出=1) */ + +#define SD_INFO1_INSERT 0x0010 /* card inserted(挿入=1) */ +#define SD_INFO1_REMOVE 0x0008 /* card removed(抜け=1) */ + +#define SD_INFO1_ALL_END 0x0004 /* R/W access all end */ +#define SD_INFO1_RES_END 0x0001 /* Response end */ +#define SD_INFO1_INIT 0x0005 /* SD Cardの状態を初期化 */ + +#define SD_INFO1_SET 0x031D /* SDカード 挿抜 and RWアクセス終了 and レスポンス終了 要求割込み要因チェック */ + +/* SD_INFO1_MASKレジスタ (0:割り込み許可、1:割り込み禁止)*/ +#define SD_INFO1_MASK_DAT3INSERT 0x0200 /* (IO3検出) card inserted(挿入) 割込み禁止 */ +#define SD_INFO1_MASK_DAT3REMOVE 0x0100 /* (IO3検出) card removed(抜け) 割込み禁止 */ +#define SD_INFO1_MASK_INSERT 0x0010 /* card inserted(挿入) 割込み禁止 */ +#define SD_INFO1_MASK_REMOVE 0x0008 /* card removed(抜け) 割込み禁止 */ +#define SD_INFO1_MASK_ALL_END 0x0004 /* R/W access all end 割込み禁止 */ +#define SD_INFO1_MASK_RES_END 0x0001 /* Response end 割込み禁止 */ + +/* CC_EXT_MODEレジスタ */ +#define CC_EXT_MODE_PIO 0x0000 /* PIOモード */ +#define CC_EXT_MODE_DMA 0x0002 /* DMAモード */ + +/* SOFT_RSTレジスタ */ +#define SOFT_RST_SDIF_RST 0x0001 /* SD I/Fモジュールをリセット */ + +/* SD_CLK_CTRLレジスタ */ +#define SD_CLK_CTRL_SDCLKEN 0x0100 /* SDカードクロック出力イネーブル */ +#define SD_CLK_CTRL_512 0x0180 /* SDクロックの周波数(分周比512)*/ +#define SD_CLK_CTRL_256 0x0140 /* SDクロックの周波数(分周比256)*/ +#define SD_CLK_CTRL_128 0x0120 /* SDクロックの周波数(分周比128)*/ +#define SD_CLK_CTRL_64 0x0110 /* SDクロックの周波数(分周比 64)*/ +#define SD_CLK_CTRL_32 0x0108 /* SDクロックの周波数(分周比 32)*/ +#define SD_CLK_CTRL_16 0x0104 /* SDクロックの周波数(分周比 16)*/ +#define SD_CLK_CTRL_8 0x0102 /* SDクロックの周波数(分周比 8)*/ +#define SD_CLK_CTRL_4 0x0101 /* SDクロックの周波数(分周比 4)*/ +#define SD_CLK_CTRL_2 0x0100 /* SDクロックの周波数(分周比 2)*/ + + +/*------------------------------------- +マルチポート対応ビット制御(フラグ定義) +-------------------------------------*/ +#define SDCARD_PORT_NO 0x0300 /* カードポート選択数 */ +#define SDCARD_PORT_NO_MAX 0x04//0x02 /* カードポート最大選択数 */ +#define SDCARD_PORT_NO_MIN 0x01 /* カードポート最小選択数 */ +#define SDCARD_PORT_SELECT_NO 0x0001 /* カードポート番号ビット */ +#define SDCARD_PORT0 0x0000 /* カードポート番号ビット */ +#define SDCARD_PORT1 0x0001 /* カードポート番号ビット */ + +/* EXT_WPレジスタ(ポート1以降のライトプロテクト) */ +#define EXT_WP_PORT1 0x0001 /* ポート1write protect(書き込み禁止=1)*/ + +/* EXT_CDレジスタ */ +#define EXT_CD_PORT1_REMOVE 0x0001 /* ポート1 card detect(検出=1) */ +#define EXT_CD_PORT1_INSERT 0x0002 /* ポート1 card inserted(挿入=1) */ +#define EXT_CD_PORT1_DETECT 0x0004 /* ポート1 card removed(抜け=1) */ + +/* EXT_CD_DAT3レジスタ */ +//#define EXT_CD_PORT1_DAT3INIT 0x0003 /* ポート1 card の状態 (IO3検出) の初期化 */ +#define EXT_CD_PORT1_DAT3REMOVE 0x0001 /* ポート1 dat3 card detect(検出=1) */ +#define EXT_CD_PORT1_DAT3INSERT 0x0002 /* ポート1 dat3 card inserted(挿入=1) */ +#define EXT_CD_PORT1_DAT3DETECT 0x0004 /* ポート1 dat3 card removed(抜け=1) */ + +/* EXT_CD_DAT3_MASKレジスタ */ +#define EXT_CD_MASK_PORT1INSERT 0x0002 /* port1 card inserted(挿入) 割込み禁止 */ +#define EXT_CD_MASK_PORT1REMOVE 0x0001 /* port1 card removed(抜け) 割込み禁止 */ +#define EXT_CD_MASK_PORT1DAT3INSERT 0x0002 /* port1 (IO3検出) card inserted(挿入) 割込み禁止 */ +#define EXT_CD_MASK_PORT1DAT3REMOVE 0x0001 /* port1 (IO3検出) card removed(抜け) 割込み禁止 */ + +/*------------------------------------- +カードの選択 +--------------------------------------*/ +#define SDCARD_DETECT_VISUAL_BIT 0x0400 /*** upper layer card detected visual bit ***/ + +/*------------------------------------- +ライトプロテクト +-------------------------------------*/ +#define SDCARD_WP_PERMANENT_BIT 0x0020 /*** permanent write protection ***/ +#define SDCARD_WP_TEMPORARY_BIT 0x0010 /*** temporary write protection ***/ + +/*------------------------------------- +SD or MMCカードコマンド +-------------------------------------*/ +#define GO_IDLE_STATE (0) /* resets all cards to idle state */ +#define SEND_OP_COND (1) /* Asks all cards in idle state to send their operation conditions */ +#define ALL_SEND_CID (2) /* send CID numbers */ +#define SEND_RELATIVE_ADDR (3) /* ask the card to publish a new relative address(RCA) */ +#define SET_BUS_WIDTH (6) /* ビット幅の選択 */ +#define SELECT_CARD (7) /* Command toggles acard between thr Stand-by and Transfer states */ +#define SEND_CSD (9) /* addressed card sends its card-specific data(CSD) */ +#define STOP_TRANSMISSION (12) /* forces the card to stop transmission */ +#define SD_SEND_STATUS (13) /* addressed card sends its status register */ +#define SET_BLOCKLEN (16) /* sets the block length */ +#define READ_MULTIPLE_BLOCK (18) /* マルチブロックリード */ +#define WRITE_MULTIPLE_BLOCK (25) /* マルチブロックライト */ +#define APP_CMD (55) /* CMD55 */ +#define SD_STATUS (13) /* ACMD13 Send the SD_CARD status */ +#define SEND_NUM_WR_SECTORS (22) /* ACMD22 書きこみ完了セクタ数取得 */ +#define SD_APP_OP_COND (41) /* ACMD41 */ +#define SEND_SCR (51) /* SD configuration register (SCR) */ + +/* MMCplus, eMMCの定義 */ +#define EXT_CSD_ACCESS (6) + +/* Extended Commandの定義 */ +#define EXT_NORMAL (0) +#define EXT_SDIO (0x4000) + +#define EXT_COM_R3 (0x0700) + +#define EXT_CMD (0x00C0) + +#define SEND_IF_COND (8) /* Physical Layer 2.0 で追加されたコマンド */ +#define SEND_IF_COND_EXT (EXT_SDIO | EXT_COM_R3 | EXT_CMD | SEND_IF_COND) + + +/*------------------------------------- +IP レジスタアクセス マクロ関数 +-------------------------------------*/ +#define SD_OrFPGA(reg,value) ((reg) |= (value)); +#define SD_AndFPGA(reg,value) ((reg) &= (value)); +#define SD_SetFPGA(reg,value) ((reg) = (value)); +#define SD_GetFPGA(dest,reg) ((dest) = (reg)); + +/*********************************************************************** + 構造体定数 +***********************************************************************/ +typedef union{ + u32 dat; + struct { + u16 low; + u16 high; + } dt2word; +} LELONG; + +/*------------------------------------- +その他(ビット制御) +-------------------------------------*/ +#define RSP_R3_OCR31 0x8000 /* Use OCR Busy bit Check */ +#define RSP_R3_OCR_VDD 0x0030 /* Use OCR VDD bit Check (3.2-3.3v,3.3-3.4v is OK)*/ + +/*--- SCR[0]に対する4bitバス幅対応フラグのマスク ---*/ +//SCRのbit50に相当するが、SCRとSD_STATUSはMSBから送られてくることを考慮してある +#define SCR_DAT_BUS_WIDTH_4BIT 0x0400 /* SCR DAT Bus width supported 4bit */ + +/*--- 127bitCSD (CSD[0]〜CSD[7])関連 ---*/ +#define CSD_VDD_R_CURR_MIN 0x0038 /* "VDD_R_CURR_MIN" (for CSD[3]) */ +#define CSD_VDD_R_CURR_MAX 0x0007 /* "VDD_R_CURR_MAX" (for CSD[3]) */ +#define CSD_VDD_W_CURR_MIN 0xE000 /* "VDD_W_CURR_MIN" (for CSD[2]) */ +#define CSD_VDD_W_CURR_MAX 0x1C00 /* "VDD_W_CURR_MAX" (for CSD[2]) */ + +#define CSD_READ_BL_LEN 0x0F00 /* "READ_BL_LEN" (for CSD[4])*/ +#define CSD_WRITE_BL_LEN_BIT_25_24 0x0003 /* "WRITE_BL_LEN" (for CSD[1])*/ +#define CSD_WRITE_BL_LEN_BIT_23_22 0xC000 /* "WRITE_BL_LEN" (for CSD[0]*/ +#define CSD_READ_BL_PARTIAL 0x0080 /* "READ_BL_PARTIAL" (for CSD[4]) */ +#define CSD_TRANSFER_RATE 0x0700 /* "Transfer rate unit" of "TRAN_SPEED" (for CSD[5]) */ +#define CSD_TRAN_SPEED_100K 0x0000 /* 100Kbit/s (for CSD Transfer rate) */ +#define CSD_TRAN_SPEED_1M 0x0001 /* 1Mbit/s (for CSD Transfer rate) */ +#define CSD_TRAN_SPEED_10M 0x0002 /* 10Mbit/s (for CSD Transfer rate) */ +#define CSD_TRAN_SPEED_100M 0x0003 /* 100Mbit/s (for CSD Transfer rate) */ +#define CSD_TRAN_SPEED_OTHER 0x0004 /* Reserve (for CSD Transfer rate) */ + +#define CSD_C_SIZE_MULT 0x0380 /* RSP2 の bit[49:47] */ +#define CSD_C_SIZE_BIT_73_72 0x0003 /* RSP3 の bit[73:72](C_SIZE) */ +#define CSD_C_SIZE_BIT_71_62 0xFFC0 /* RSP3 の bit[71:62](C_SIZE) */ +//SDHC(CSD format version2)の場合 +#define CSD_C_SIZE_BIT_69_56 0x3FFF /* SD_CSD[3] */ +#define CSD_C_SIZE_BIT_55_48 0xFF00 /* SD_CSD[2] */ + +#define CSD_STRUCT_BIT_127_126 0x00C0 /* SD_CSD[7] */ + +//#define VDD_R_CURR_MIN 0x0000 /* CSD max read current@VDD min */ +//#define VDD_R_CURR_MAX 0x0007 /* CSD max read current@VDD max */ +//#define VDD_W_CURR_MIN 0x0000 /* CSD max write current@VDD min */ +//#define VDD_W_CURR_MAX 0x0007 /* CSD max write current@VDD max */ + +/*--- R1レスポンスの card status(32bit)に対するマスク ---*/ +#define RSP_R1_STATUS_ERR 0xF9FF0008 /* R1(レスポンス)のカードステータスのチェック */ +#define SDCARD_STATUS_OUT_OF_RANGE 0x80000000 /* Card Status OUT_OF_RANGE のチェック */ +#define RSP_R1_CURRENT_STATE 0x1E00 /* CARD current state */ +/*-------------------------------------------------------*/ + +/* レスポンスのRSP0 & RSP_R1_CURRENT_STATEを1ビット右シフトした値に対するフラグ */ +#define CURRENT_STATE_DATA 0x0500 /* CARD current state data */ +#define CURRENT_STATE_RCV 0x0600 /* CARD current state rcv */ + +/* カードステータス */ +#define SD_MEMORY_CARD 0x00FF /* SD_CARD_TYPE SD memory card */ + + +/*------------------------------------- +プロトタイプ宣言 +-------------------------------------*/ +void SD_Init(void); /* SD Cardインターフェース部をリセット&初期設定 */ +void SD_EnableInfo(void); /* SD Card 挿抜 割り込みイネーブル・ディスエーブル */ +u16 SD_Command(u16 ucCommand); /* SDカードコマンド送出 */ +u16 SD_AppCommand(void); /* SDカード RCA = 1をセット後 CMD55 発行 */ +u16 SD_AppOpCond(void); /* ACMD41 発行 busyでなくなるまで繰り返し */ +u16 SD_SendOpCond(void); /* CMD1 発行 busyでなくなるまで繰り返し */ +u16 SD_SendIfCond(void); /* CMD8 発行 (SDHCのみ反応してくる) */ +u16 SD_SendRelativeAddr(void); /* CMD3 発行 正常終了時 RCA<-ResのRCA */ +u16 SD_SelectCard(void); /* CMD7 発行 Command toggles acard between the Stand-by and Transfer states */ +u16 SD_SetBlockLength(u32 ulBlockLength); /* ブロックレングス(1セクタの転送量)の設定 */ +u16 SD_SendCID(void); /* card identification data の取得コマンド発行 */ +u16 SD_SendCSD(void); /* card-specific data の取得コマンド発行 */ +u16 SD_SendSCR(void); /* SD register の取得コマンド発行 */ +u16 SD_SDStatus(void); /* SD STATUS の取得コマンド発行 */ +u16 SD_SendStatus(void); /* SD status register の取得コマンド発行 */ +u16 SD_MultiReadBlock(u32 ulOffset); /* マルチセクタリードコマンド発行 */ +u16 SD_ClockDivSet(u16 usTranSpeed); /* カードの動作クロック設定 */ + +void SD_EnableClock( void); /* SDカードのクロック有効 */ +void SD_DisableClock( void); /* SDカードのクロック無効(省電力) */ + +u16 SD_SelectBitWidth(s16 b4bit); /* ビット幅の選択 */ + +u16 MMCP_WriteBusWidth(s16 b4bit); +u16 MMCP_BusTest( BOOL readflag); + + +s16 SD_FPGA_irq(void); /* カード転送要求時のFPGAの制御 */ +void SD_StopTransmission(void); /* カード転送終了をFPGAに通知する。 */ +void SD_TransEndFPGA(void); /* カード転送の終了処理(割り込みマスクを戻す) */ +u16 SD_CheckStatus(BOOL bRead); /* Normal response command カードステータスのチェック */ +u16 SD_SwapByte(u16 *data); /* 上位byte、下位byteを入れ替える関数 */ + +void SD_EnableSeccnt( u32 ulSDCARD_SectorCount); /* SD_SECCNTレジスタ有効化&値設定 */ +void SD_DisableSeccnt( void); /* SD_SECCNTレジスタ無効化 */ + +void SD_SetErr(u16 Error); /* エラーステータスを設定する */ +void SD_ClrErr(u16 Error); /* エラーステータスをクリアする */ + +BOOL SD_CheckFPGAReg(u16 reg,u16 value); /* IPレジスタにフラグが立っているか判定 */ + +void SD_TransReadyFPGA(void); /* 転送処理準備FPGA設定 */ +u16 SD_TransCommand(u16 ucCommand); /* 命令発行処理 */ + +u16 SD_MultiWriteBlock(u32 ulOffset); /* マルチセクタライトコマンド発行 */ +u16 SD_SendNumWRSectors(void); /* ACMD22 書きこみ完了セクタ数取得コマンド発行 */ + + +#endif /* __SD_CARD_IP_H__ */ diff --git a/build/libraries/devices/sdmc/ARM7/sdif_reg.h b/build/libraries/devices/sdmc/ARM7/sdif_reg.h new file mode 100644 index 0000000..31011d9 --- /dev/null +++ b/build/libraries/devices/sdmc/ARM7/sdif_reg.h @@ -0,0 +1,109 @@ +/*---------------------------------------------------------------------------* + Project: CTR - SD driver + File: sd_ip_reg.h + + Copyright 2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#ifndef __SD_IP_REG_H__ +#define __SD_IP_REG_H__ + +/********************************************* + SD IPレジスタ + + (R/W) : readable and writable + (RO) : read only +*********************************************/ +#if (CTR_DEF_ENVIRONMENT_DSEMU == 1) +#define SD_IP_BASE (0x08030000) // NTR用ブレッドボード設定 +#else +#define SD_IP_BASE (0x400B0000) // IOP実機設定 +#endif + + +#define SD_CMD (*(vu16 *)(SD_IP_BASE + 0x00)) /* CMD発行レジスタ(R/W) */ +#define SD_PORTSEL (*(vu16 *)(SD_IP_BASE + 0x02)) /* ポート選択レジスタ(R/W) */ +#define SD_ARG0 (*(vu16 *)(SD_IP_BASE + 0x04)) /* Argument[15:0] (R/W) */ +#define SD_ARG1 (*(vu16 *)(SD_IP_BASE + 0x06)) /* Argument[31:16] (R/W) */ +#define SD_STOP (*(vu16 *)(SD_IP_BASE + 0x08)) /* 転送終了後STOP、SD_SECCNT有効 (R/W) */ +#define SD_SECCNT (*(vu16 *)(SD_IP_BASE + 0x0A)) /* 転送セクタ数制御 (R/W) */ +#define SD_RSP0 (*(vu16 *)(SD_IP_BASE + 0x0C)) /* Response [23:8] (RO) */ +#define SD_RSP1 (*(vu16 *)(SD_IP_BASE + 0x0E)) /* Response [39:24] (RO) */ +#define SD_RSP2 (*(vu16 *)(SD_IP_BASE + 0x10)) /* Response [55:40] (RO) */ +#define SD_RSP3 (*(vu16 *)(SD_IP_BASE + 0x12)) /* Response [71:56] (RO) */ +#define SD_RSP4 (*(vu16 *)(SD_IP_BASE + 0x14)) /* Response [87:72] (RO) */ +#define SD_RSP5 (*(vu16 *)(SD_IP_BASE + 0x16)) /* Response [103:88] (RO) */ +#define SD_RSP6 (*(vu16 *)(SD_IP_BASE + 0x18)) /* Response [119:104] (RO) */ +#define SD_RSP7 (*(vu16 *)(SD_IP_BASE + 0x1A)) /* Response [127:120] (RO) */ +#define SD_INFO1 (*(vu16 *)(SD_IP_BASE + 0x1C)) /* SDメモリーカードの状態 (R/W) */ +#define SD_INFO2 (*(vu16 *)(SD_IP_BASE + 0x1E)) /* バッファ制御とエラー情報 (R/W) */ +#define SD_INFO1_MASK (*(vu16 *)(SD_IP_BASE + 0x20)) /* SD_INFO1割込みマスク (R/W) */ +#define SD_INFO2_MASK (*(vu16 *)(SD_IP_BASE + 0x22)) /* SD_INFO2割込みマスク (R/W) */ +#define SD_CLK_CTRL (*(vu16 *)(SD_IP_BASE + 0x24)) /* SDクロック設定 (R/W) */ +#define SD_SIZE (*(vu16 *)(SD_IP_BASE + 0x26)) /* ビット幅と転送データ長の設定 (R/W) */ +#define SD_OPTION (*(vu16 *)(SD_IP_BASE + 0x28)) /* タイムアウト、width、CD検出タイム (R/W) */ +#define SD_ERR_STS1 (*(vu16 *)(SD_IP_BASE + 0x2C)) /* CMD, CRC, ENDエラー割込み原因 (RO) */ +#define SD_ERR_STS2 (*(vu16 *)(SD_IP_BASE + 0x2E)) /* タイムアウトエラー割込み原因 (RO) */ +#define SD_BUF0 (vu16 *)(SD_IP_BASE + 0x30) /* SDバッファ読込/書込データポート (R/W) */ +#define CC_EXT_MODE (*(vu16 *)(SD_IP_BASE + 0xD8)) /* DMAモード/PIOモード切り替え */ +#define SOFT_RST (*(vu16 *)(SD_IP_BASE + 0xE0)) /* ソフトウェアリセット (R/W) */ +#define VERSION (*(vu16 *)(SD_IP_BASE + 0xE2)) /* Version レジスタ (RO) */ +#define EXT_WP (*(vu16 *)(SD_IP_BASE + 0xF6)) /* 拡張SD Card ライトプロテクト (RO) */ +#define EXT_CD (*(vu16 *)(SD_IP_BASE + 0xF8)) /* 拡張SD Card 検出、挿入、抜け フラグ (R/W) */ +#define EXT_CD_DAT3 (*(vu16 *)(SD_IP_BASE + 0xFA)) /* 拡張SD Card 検出、挿入、抜け フラグ (R/W) */ +#define EXT_CD_MASK (*(vu16 *)(SD_IP_BASE + 0xFC)) /* 拡張SD Card 検出、挿入、抜け 割込みマスク (R/W) */ +#define EXT_CD_DAT3_MASK (*(vu16 *)(SD_IP_BASE + 0xFE)) /* 拡張SD Card 検出、挿入、抜け 割込みマスク (R/W) */ + + + +/********************************************* + SD I/F(ラッパー)レジスタ +*********************************************/ +#if (CTR_DEF_ENVIRONMENT_DSEMU == 1) +#define SD_IF_BASE (0x08030100) // NTR用ブレッドボード設定 +#else +#define SD_IF_BASE (0x400B0100) // IOP実機設定 +#endif + + +#define SDIF_CNT ((vu32 *)(SD_IF_BASE+0x00)) /* コントロール */ +#define SDIF_FDS ((vu32 *)(SD_IF_BASE+0x04)) /* FIFOサイズ */ +#define SDIF_FSC ((vu32 *)(SD_IF_BASE+0x08)) /* セクタカウント */ +#define SDIF_FI ((vu32 *)(SD_IF_BASE+0x0c)) /* FIFOウィンドウ */ + +#define SDIF_CNT_L ((vu16 *)(SD_IF_BASE+0x00)) +#define SDIF_CNT_H ((vu16 *)(SD_IF_BASE+0x02)) +#define SDIF_FDS_L ((vu16 *)(SD_IF_BASE+0x04)) +#define SDIF_FDS_H ((vu16 *)(SD_IF_BASE+0x06)) +#define SDIF_FSC_L ((vu16 *)(SD_IF_BASE+0x08)) +#define SDIF_FSC_H ((vu16 *)(SD_IF_BASE+0x0a)) +#define SDIF_FI_L ((vu16 *)(SD_IF_BASE+0x0c)) +#define SDIF_FI_H ((vu16 *)(SD_IF_BASE+0x0e)) + + + +/********************************************* + INTCレジスタ +*********************************************/ +#if (CTR_DEF_ENVIRONMENT_DSEMU == 1) +#define CTR_INT_BASE (0x08000000) +#else +#define CTR_INT_BASE (0x40010000) +#endif + + +#define CTR_INT_SE (CTR_INT_BASE + 0x00) //割り込み要求選択レジスタ +#define CTR_INT_IE (CTR_INT_BASE + 0x04) //IE +#define CTR_INT_IF (CTR_INT_BASE + 0x08) //IF + +#define CTR_IE_SD_MASK (1<<13) //SD割り込みフラグ + + + +#endif /* __SD_IP_REG_H__ */ diff --git a/build/libraries/devices/sdmc/ARM7/sdmc.c b/build/libraries/devices/sdmc/ARM7/sdmc.c new file mode 100644 index 0000000..11fe507 --- /dev/null +++ b/build/libraries/devices/sdmc/ARM7/sdmc.c @@ -0,0 +1,2748 @@ +/* + Project: CTR SD port driver + File: Carddrv.c + + 2006, Research and Development Department, Nintendo. +*/ + +#include +//#include +#include "sdmc_config.h" +#include "sdif_reg.h" /* IP 対応レジスタ定義 */ +#include "sdmc.h" +#include "sdif_ip.h" /* IP 対応フラグ定義 */ + +#if (SD_DEBUG_PRINT_ON == 1) + #if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + #define PRINTDEBUG osTPrintf + #else + #include + #define PRINTDEBUG vlink_dos_printf + #endif +#else + #define PRINTDEBUG( ...) ((void)0) +#endif + + +/*********************************************************************** + 定数 +***********************************************************************/ +#define SD_STACK_SIZE (4096) +#define SD_THREAD_PRIO (10) +#define SD_INTR_THREAD_PRIO (SD_THREAD_PRIO - 1) + +#define SD_OPERATION_INIT (0) +#define SD_OPERATION_READ (1) +#define SD_OPERATION_READ_WITH_FIFO (2) +#define SD_OPERATION_WRITE (3) +#define SD_OPERATION_WRITE_WITH_FIFO (4) + + +/*********************************************************************** + extern変数 +***********************************************************************/ +extern u16 BgBak[32*32]; +//u16 sdcard_request_flag; //カードからのデータ転送要求の有無フラグ +static BOOL thread_flag; + + +/*********************************************************************** + global変数 +***********************************************************************/ +BOOL sdmc_tsk_created = FALSE; +#if (TARGET_OS_CTR == 1) +ER_ID sdmc_tsk_id; //SDタスクID +ER_ID sdmc_dtq_id; +ER_ID sdmc_result_dtq_id; +ER_ID sdmc_alm_id; +ER_ID sdmc_intr_tsk_id; +#else +OSThread sdmc_tsk; +OSMessageQueue sdmc_dtq; +OSMessage sdmc_dtq_array[1]; +OSMessageQueue sdmc_result_dtq; +OSMessage sdmc_result_dtq_array[1]; +OSAlarm sdmc_alm; +OSThread sdmc_intr_tsk; +#endif + +#if (TARGET_OS_CTR == 1) +#else +u64 sd_stack[SD_STACK_SIZE / sizeof(u64)]; +u64 sd_intr_stack[SD_STACK_SIZE / sizeof(u64)]; +#endif + +/* drsdmc.cでも参照 */ +SdmcSpec sdmc_current_spec; //TODO:ポート切り替え時、Port0とPort1に保存するように + +typedef struct { //OSMessage + void* buf; + u32 bufsize; + u32 offset; + void (*func)(void); + SdmcResultInfo* info; + u32 operation; + void (*func2)(void); +} SDCARDMsg; + + +/*********************************************************************** + static関数の宣言 +***********************************************************************/ +static void SDCARD_Backup_port0(void); +static void SDCARD_Backup_port1(void); +static void SDCARD_Restore_port0(void); +static void SDCARD_Restore_port1(void); + +static SDMC_ERR_CODE SDCARDi_ReadFifo(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info); +static SDMC_ERR_CODE SDCARDi_WriteFifo(void* buf,u32 bufsize,u32 offset,void(*func)(),SdmcResultInfo *info); + +static SDMC_ERR_CODE SDCARDi_Read(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info); +static SDMC_ERR_CODE SDCARDi_Write(void* buf,u32 bufsize,u32 offset,void(*func)(),SdmcResultInfo *info); + +int MMCP_SetBusWidth( BOOL b4bit); /* ビット幅の選択(MMCplus, eMMC, moviNAND) */ + +static void SDCARD_Thread( void* arg); //SDスレッド +static void SDCARD_Intr_Thread( void* arg); //SD割り込み処理スレッド + +SDMC_ERR_CODE sdmcGoIdle( void (*func1)(),void (*func2)()); +static SDMC_ERR_CODE i_sdmcInit( void); +static SDMC_ERR_CODE SDCARD_Layer_Init(void); +static SDMC_ERR_CODE i_sdmcMPInit( void); /* カードドライバ初期化(マルチポート対応) */ + +static u16 i_sdmcErrProcess(void); /* エラー時の処理 */ +static u16 i_sdmcGetResid(u32 *pResid); /* 書きこみ完了セクタ数の取得 */ +static u16 i_sdmcCheckWP(void); /* SDCARD ライトプロテクトチェック */ + +static void i_sdmcEnable( void); /* SD使用のためのOS準備 */ +static void i_sdmcDisable( void); + +static u16 i_sdmcSendSCR(void); /* SCRの読み出し */ +static u16 SDCARD_SD_Status(void); /* SD_STATUSの取得 */ +static u32 SDCARD_GetR1Status(void); /* Normal response command カードステータスを取得 */ + +static void SDCARD_Dmy_Handler( void); /* 何もしない */ +static void SDCARD_Timer_irq(void* arg); /* タイムアウト割り込みハンドラ */ +static void SDCARD_irq_Handler( void); /* SD-IPからの割り込みハンドラ */ +static void SDCARD_FPGA_irq(void); /* カードリードライト割り込み処理 */ +static void SDCARD_ATC0_irq(void); /* ATC0転送完了割り込み処理 */ +static void SYSFPGA_irq(void); /* SYSFPGAエラー割り込み処理 */ + +/*ポート1は無線固定なのでポート選択関数は公開しない*/ +static u16 i_sdmcSelectedNo(void); /* カードポートの選択 */ +static u16 i_sdmcSelect(u16 select); /* 現在のカードポート番号のチェック */ + + + + +/*********************************************************************** + 外部参照変数 +***********************************************************************/ +static void SDCARD_TimerStart(u32 tim); /* タイムアウト計測スタート */ +static void SDCARD_TimerStop(void); /* タイムアウト計測停止 */ + +extern u16 SD_CID[8]; /* CID値保存用 */ +extern u16 SD_CSD[8]; /* CSD値保存用 */ +extern u16 SD_OCR[2]; /* OCR値保存用 */ +extern u16 SD_SCR[4]; /* SCR値保存用 */ +extern u16 SD_RCA; /* RCA値保存用 */ + +extern s16 SDCARD_MMCFlag; /* MMCカードフラグ */ +extern s16 SDCARD_SDHCFlag; /* SDHCカードフラグ */ +extern u16 SD_port_number; /* 現在ポート番号 */ + +//static BOOL init_io_exist; /* SDIO存在フラグ */ +//static BOOL init_mem_exist; /* メモリ存在フラグ */ + + +/*********************************************************************** + グローバル +***********************************************************************/ +u16 SD_SDSTATUS[32]; /* SD_STATUSレジスタ保存用 */ +u16 SDCARD_WP_FLAG0; /* カードライトプロテクトフラグ。0=なし、1=有り */ +u16 SDCARD_WP_FLAG1; /* カードライトプロテクトフラグ。0=なし、1=有り */ +u16 SDCARD_WP_PERMANENT; /* カードライトプロテクト永久フラグ。0=なし、1=有り */ +u16 SDCARD_WP_TEMPORARY; /* カードライトプロテクト一時フラグ。0=なし、1=有り */ + +u16* pSDCARD_BufferAddr; /* 保存用データ格納バッファアドレス */ + +u32 ulSDCARD_SectorCount; /* 転送セクタ数 */ +u32 ulSDCARD_RestSectorCount; /* 残り転送セクタ数 */ +u32 SDCARD_SectorSize; /* セクタサイズ デフォルト 512bytes */ + +u16 SD_INFO1_VALUE; /* SD_INFO1レジスタ取得用変数 */ +u16 SD_INFO1_MASK_VALUE; /* SD_INFO1割込みマスク用変数(0で許可, 1で禁止) */ +u16 SD_INFO2_VALUE; /* SD_INFO2レジスタ取得用変数 */ +u16 SD_INFO2_MASK_VALUE; /* SD_INFO2割り込みマスク用変数(0で許可, 1で禁止) */ +u16 SD_INFO_ERROR_VALUE; /* SD_INFO2, SD_INFO1のエラービット確認用変数 */ + +u16 SD_port_en_numbers; /* サポートするポート数 */ + + +/*--- ポート0 保存変数 ---*/ +u16 SD_CLK_CTRL_port0; +u16 SD_OPTION_port0; + +SDMC_ERR_CODE SDCARD_ErrStatus_port0; +u16 SD_RCA0; +s16 SDCARD_MMCFlag_port0; +u32 SDCARD_Status_port0; +s16 SDCARD_SDFlag_port0; +s16 SDCARD_OutFlag_port0; +SdmcResultInfo *pSDCARD_info_port0; +u16 SDCARD_WP_PERMANENT_port0; +u16 SDCARD_WP_TEMPORARY_port0; +u16 SD_CID_port0[8] = {0}; +u16 SD_CSD_port0[8] = {0}; +/*------------------------*/ + +/*--- ポート1 保存変数 ---*/ +u16 SD_CLK_CTRL_port1; +u16 SD_OPTION_port1; + +SDMC_ERR_CODE SDCARD_ErrStatus_port1; +u16 SD_RCA1; +s16 SDCARD_MMCFlag_port1; +u32 SDCARD_Status_port1; +s16 SDCARD_SDFlag_port1; +s16 SDCARD_OutFlag_port1; +SdmcResultInfo *pSDCARD_info_port1; +u16 SDCARD_WP_PERMANENT_port1; +u16 SDCARD_WP_TEMPORARY_port1; +u16 SD_CID_port1[8] = {0}; +u16 SD_CSD_port1[8] = {0}; +/*------------------------*/ + +u16 TransCount; /* R/W転送カウント変数 */ + +u32 ulSDCARD_Size; /* カード全セクタ数 */ + +volatile s16 SDCARD_ATC0_Flag; /* 全ATC完了フラグ */ +volatile s16 SDCARD_FPGA_Flag; /* FPGA処理完了フラグ */ +volatile s16 SDCARD_EndFlag; /* 転送処理完了フラグ */ + +SDMC_ERR_CODE SDCARD_ErrStatus; /* エラーステータス */ +vu32 SDCARD_Status; /* カードステータス */ +s16 SDCARD_SDFlag; /* SDカードフラグ */ + +volatile s16 SDCARD_OutFlag; /* カード排出発生判定フラグ */ +SdmcResultInfo *pSDCARD_info; /* 保存用実行結果構造体ポインタ */ + +u16 SDCARD_IO_Port; /* カード挿入/排出割り込み発生時のポート番号 */ + +void (*func_SDCARD_In)(void); /* カード挿入イベント用コールバック保存用 */ +void (*func_SDCARD_Out)(void); /* カード排出イベント用コールバック保存用 */ +/* void (*func_SDCARD_CallBack)(SdmcResultInfo *info); 処理結果通知用コールバック保存用 */ + + +/*---------------------------------------------------------------------------* + Name: SDCARD_Backup_port0 + + Description: backup registers and variables of port0. + ポート0のレジスタや変数をバックアップする + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_Backup_port0(void) +{ + /* registers */ + SD_GetFPGA(SD_CLK_CTRL_port0,SD_CLK_CTRL); + SD_GetFPGA(SD_OPTION_port0,SD_OPTION); + + /* variables */ + SDCARD_ErrStatus_port0 = SDCARD_ErrStatus; + SD_RCA0 = SD_RCA; + SDCARD_MMCFlag_port0 = SDCARD_MMCFlag; + SDCARD_Status_port0 = SDCARD_Status; + SDCARD_SDFlag_port0 = SDCARD_SDFlag; + SDCARD_OutFlag_port0 = SDCARD_OutFlag; + pSDCARD_info_port0 = pSDCARD_info; + SDCARD_WP_PERMANENT_port0 = SDCARD_WP_PERMANENT; + SDCARD_WP_TEMPORARY_port0 = SDCARD_WP_TEMPORARY; + + /*registers*/ +#if (TARGET_OS_CTR == 1) + miCpuCopy8( SD_CID, SD_CID_port0, 16); + miCpuCopy8( SD_CSD, SD_CSD_port0, 16); +#else + MI_CpuCopy8( SD_CID, SD_CID_port0, 16); + MI_CpuCopy8( SD_CSD, SD_CSD_port0, 16); +#endif +} + +/*---------------------------------------------------------------------------* + Name: SDCARD_Backup_port1 + + Description: backup registers and variables of port1. + ポート1のレジスタや変数をバックアップする + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_Backup_port1(void) +{ + /* registers */ + SD_GetFPGA(SD_CLK_CTRL_port1,SD_CLK_CTRL); + SD_GetFPGA(SD_OPTION_port1,SD_OPTION); + + /* variables */ + SDCARD_ErrStatus_port1 = SDCARD_ErrStatus; + SD_RCA1 = SD_RCA; + SDCARD_MMCFlag_port1 = SDCARD_MMCFlag; + SDCARD_Status_port1 = SDCARD_Status; + SDCARD_SDFlag_port1 = SDCARD_SDFlag; + SDCARD_OutFlag_port1 = SDCARD_OutFlag; + pSDCARD_info_port1 = pSDCARD_info; + SDCARD_WP_PERMANENT_port1 = SDCARD_WP_PERMANENT; + SDCARD_WP_TEMPORARY_port1 = SDCARD_WP_TEMPORARY; + + /*registers*/ +#if (TARGET_OS_CTR == 1) + miCpuCopy8( SD_CID, SD_CID_port1, 16); + miCpuCopy8( SD_CSD, SD_CSD_port1, 16); +#else + MI_CpuCopy8( SD_CID, SD_CID_port1, 16); + MI_CpuCopy8( SD_CSD, SD_CSD_port1, 16); +#endif +} + + +/*---------------------------------------------------------------------------* + Name: i_sdmcEnable + + Description: assign OS resouce for using SD memory card. + SDカードを使うための準備をする + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void i_sdmcEnable( void) +{ +#if (TARGET_OS_CTR == 1) +#if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + u16 ctrdg_reg; + + ctrdg_reg = (*(vu16*)REG_EXMEMCNT_ADDR) & (0xFF00); /* ブレッドボード設定 */ + *(vu16*)REG_EXMEMCNT_ADDR = (ctrdg_reg | 0); //ARM9優先、PHIにLo出力、1st:10,2nd:6サイクル +#endif + + /*SD interrupt setting*/ +// osInitIntrFlag(); +// osClearInterruptPendingID( OS_INTR_ID_SD); +// *(vu32*)CTR_INT_IF = CTR_IE_SD_MASK; + osSetInterruptHandler( OS_INTR_ID_SD, SDCARD_irq_Handler); + osEnableInterruptID( OS_INTR_ID_SD); +// *(vu32*)CTR_INT_SE = CTR_IE_SD_MASK; //割り込み(IRQ)発生許可 +// *(vu32*)CTR_INT_IE = CTR_IE_SD_MASK; +// osEnableInterrupts(); +// *(vu16*)0x04000208 = 1; +#else + OS_SetIrqFunction( OS_IE_SD, SDCARD_irq_Handler); + OS_EnableIrqMask( OS_IE_SD); +#endif +} + +/*---------------------------------------------------------------------------* + Name: i_sdmcDisable + + Description: under construction + 工事中 + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void i_sdmcDisable( void) +{ +} + + +/*---------------------------------------------------------------------------* + Name: SDCARD_irq_Handler + + Description: SD interrupt handler + SD割り込みハンドラ + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SDCARD_irq_Handler( void) +{ +#if (TARGET_OS_CTR == 1) + iwup_tsk( sdmc_intr_tsk_id); +#else + OS_WakeupThreadDirect( &sdmc_intr_tsk); +#endif +} + +/*---------------------------------------------------------------------------* + Name: SDCARD_Dmy_Handler + + Description: dmy handler for timer interrupt. + タイマー割り込み用ダミーハンドラ + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_Dmy_Handler( void) +{ +} + +/*---------------------------------------------------------------------------* + Name: sdmcInit + + Description: Initialize SD interface and SD card. + 初期化 + + Arguments: func1 : カード挿入時コールバック関数 + func2 : カード排出時コールバック関数 + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +SDMC_ERR_CODE sdmcInit(void (*func1)(),void (*func2)()) +{ +#if (TARGET_OS_CTR == 1) + T_CALM calm; + T_CTSK ctsk; + T_CDTQ cdtq; +#endif +// SDCARDMsg SdMsg; +// u32 init_msg; + SDMC_ERR_CODE api_result; + + if( sdmc_tsk_created == FALSE) { + /*---------- OS準備 ----------*/ + /* アラームハンドラ登録 */ +#if (TARGET_OS_CTR == 1) + calm.almatr = TA_HLNG; // set attribution : for high level language + calm.exinf = 0; // set argument for alarm handler + calm.almhdr = SDCARD_Timer_irq; // set alarm handler + sdmc_alm_id = acre_alm(&calm); + if (sdmc_alm_id < 0) + { + PRINTDEBUG("create_alarm_simple: Cannot create new alarm handler (%d).\n", sdmc_alm_id); + } + + /* メッセージ初期化 */ + // setup dataqueue structure + cdtq.dtqatr = TA_TFIFO; // set attribution : normal FIFO + cdtq.dtqcnt = 1; // there are 2 datas in queue + cdtq.dtq = NULL; // set data queue address : NULL means automatically allocated by kernel + sdmc_dtq_id = acre_dtq(&cdtq); + if (sdmc_dtq_id < 0) + { + PRINTDEBUG("create_dataqueue_simple: Cannot create new data queue.\n"); + } + /**/ + cdtq.dtqatr = TA_TFIFO; // set attribution : normal FIFO + cdtq.dtqcnt = 1; // there are 2 datas in queue + cdtq.dtq = NULL; // set data queue address : NULL means automatically allocated by kernel + sdmc_result_dtq_id = acre_dtq(&cdtq); + if (sdmc_result_dtq_id < 0) + { + PRINTDEBUG("create_dataqueue_simple: Cannot create new data queue.\n"); + } + +// OS_InitThread(); //自分の優先度が16になる +// chg_pri( (ID)0, (PRI)12); + + /* SDタスクの立ち上げ */ + ctsk.tskatr = TA_HLNG | TA_ACT; // set attribution : for high level language and running now + ctsk.task = SDCARD_Thread; // set task routine + ctsk.exinf = (void*)0; // set argument for task routine + ctsk.itskpri = SD_THREAD_PRIO; // set priority + ctsk.stksz = SD_STACK_SIZE; // set stack size + ctsk.stk = NULL; // set stack address : NULL means automatically allocated by kernel + sdmc_tsk_id = acre_tsk(&ctsk); + if (sdmc_tsk_id < 0) + { + PRINTDEBUG("create_task_sd: Cannot create new task.\n"); + }else{ + if( (sdmc_tsk_id == E_NOID)||(sdmc_tsk_id == E_NOMEM)||(sdmc_tsk_id == E_RSATR)|| + (sdmc_tsk_id == E_PAR)||(sdmc_tsk_id == E_OBJ)) { + PRINTDEBUG("create_task_sd: Cannot create new task.\n"); + } + PRINTDEBUG("create_task_sd: 0x%x\n", sdmc_tsk_id); + } + /*----------------------------*/ + + /* SD割り込み処理タスクの立ち上げ */ + ctsk.tskatr = TA_HLNG | TA_ACT; // set attribution : for high level language and running now + ctsk.task = SDCARD_Intr_Thread; // set task routine + ctsk.exinf = (void*)0; // set argument for task routine + ctsk.itskpri = SD_INTR_THREAD_PRIO; // set priority + ctsk.stksz = SD_STACK_SIZE; // set stack size + ctsk.stk = NULL; // set stack address : NULL means automatically allocated by kernel + sdmc_intr_tsk_id = acre_tsk(&ctsk); + if (sdmc_intr_tsk_id < 0) + { + PRINTDEBUG("create_intr_task_sd: Cannot create new task.\n"); + }else{ + if( (sdmc_intr_tsk_id == E_NOID)||(sdmc_intr_tsk_id == E_NOMEM)||(sdmc_intr_tsk_id == E_RSATR)|| + (sdmc_intr_tsk_id == E_PAR)||(sdmc_intr_tsk_id == E_OBJ)) { + PRINTDEBUG("create_intr_task_sd: Cannot create new task.\n"); + } + PRINTDEBUG("create_intr_task_sd: 0x%x\n", sdmc_intr_tsk_id); + } + +#else //(TARGET_OS_NITRO = 1) + /*---------- OS準備 ----------*/ + if( !OS_IsAlarmAvailable()) { /* アラームチェック(OS_InitAlarm済みか?) */ + SDCARD_ErrStatus |= SDMC_ERR_END; + }else{ + OS_CreateAlarm( &sdmc_alm); //使用可能であれば初期化 + } + + /* メッセージ初期化 */ + OS_InitMessageQueue( &sdmc_dtq, &sdmc_dtq_array[0], 1); + OS_InitMessageQueue( &sdmc_result_dtq, &sdmc_result_dtq_array[0], 1); + + OS_InitThread(); //自分の優先度が16になる + + /* SDスレッドの立ち上げ */ + OS_CreateThread( &sdmc_tsk, SDCARD_Thread, NULL, + (sd_stack+SD_STACK_SIZE / sizeof(u64)), SD_STACK_SIZE, SD_THREAD_PRIO); + OS_WakeupThreadDirect( &sdmc_tsk); + + /* SD割り込み処理スレッドの立ち上げ */ + OS_CreateThread( &sdmc_intr_tsk, SDCARD_Intr_Thread, NULL, + (sd_intr_stack+SD_STACK_SIZE / sizeof(u64)), SD_STACK_SIZE, SD_INTR_THREAD_PRIO); + OS_WakeupThreadDirect( &sdmc_intr_tsk); + /*----------------------------*/ +#endif + /**/ + sdmc_tsk_created = TRUE; + } + + /**/ + func_SDCARD_In = func1; /* カード挿入イベント用関数のアドレスを設定 */ + func_SDCARD_Out = func2; /* カード排出イベント用関数のアドレスを設定 */ + api_result = sdmcGoIdle( func1, func2); +// api_result = SDMC_NORMAL; + + return api_result; +} + +/* カードが入れ換わったときなどに初期化(RTFS用)*/ +SDMC_ERR_CODE sdmcGoIdle( void (*func1)(),void (*func2)()) +{ + SDCARDMsg SdMsg; +#if (TARGET_OS_CTR == 1) + u32 init_msg; +#else + OSMessage init_msg; +#endif + SDMC_ERR_CODE api_result; + + func_SDCARD_In = func1; /* カード挿入イベント用関数のアドレスを設定 */ + func_SDCARD_Out = func2; /* カード排出イベント用関数のアドレスを設定 */ + + /*----- SDスレッドと通信 -----*/ +#if (TARGET_OS_CTR == 1) + SdMsg.operation = SD_OPERATION_INIT; + + snd_dtq( sdmc_dtq_id, (VP_INT)&SdMsg); + + /* 返り値待ち */ + rcv_dtq( sdmc_result_dtq_id, (VP_INT*)&init_msg); + api_result = (SDMC_ERR_CODE)init_msg; +#else + SdMsg.operation = SD_OPERATION_INIT; +// SdMsg.func = func1; +// SdMsg.func2 = func2; + + init_msg = (OSMessage)&SdMsg; + OS_SendMessage( &sdmc_dtq, init_msg, OS_MESSAGE_BLOCK); + + /* 返り値待ち */ + OS_ReceiveMessage( &sdmc_result_dtq, &init_msg, OS_MESSAGE_BLOCK); + api_result = *(SDMC_ERR_CODE*)init_msg; +#endif + /*----------------------------------*/ + + return api_result; +} + + +/*---------------------------------------------------------------------------* + Name: i_sdmcInit + + Description: Initialize SD interface and SD card. + 初期化 + + Arguments: + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +static SDMC_ERR_CODE i_sdmcInit( void) +{ + i_sdmcEnable(); + + /* SD初期化 */ + SDCARD_ErrStatus = sdmcReset(); + + if(!SDCARD_ErrStatus) { + SDCARD_ErrStatus = i_sdmcMPInit(); + } + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: sdmcReset + + Description: reset SD card. + リセット + + Arguments: + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +SDMC_ERR_CODE sdmcReset( void) +{ + OSIntrMode irq_core_flag; + ulSDCARD_Size = 0; /* カード全セクタ数クリア */ + SDCARD_MMCFlag = FALSE; /* MMCカード判定フラグクリア */ + SDCARD_SDHCFlag = FALSE; + SDCARD_SDFlag = FALSE; /* SDカード判定フラグクリア */ + + /*** カードステータスをクリア ***/ + SDCARD_ErrStatus = SDMC_NORMAL; + SDCARD_Status = SDMC_NORMAL; + + /*** カードCSD WPビットをクリア ***/ + SDCARD_WP_FLAG0 = 0; + SDCARD_WP_FLAG1 = 0; + SDCARD_WP_PERMANENT = 0; + SDCARD_WP_TEMPORARY = 0; + + pSDCARD_info = NULL; + SDCARD_OutFlag = FALSE; /* カード排出発生判定フラグクリア */ + +#if (TARGET_OS_CTR == 1) + irq_core_flag = osDisableInterrupts(); /* 割込み禁止 */ +#else + irq_core_flag = OS_DisableInterrupts(); /* 割込み禁止 */ +#endif + *SDIF_CNT_L = 0x0402; //ラッパーレジスタ + *SDIF_CNT_L = 0x0000; //ラッパーレジスタ + *SDIF_FDS_L = 0; + *SDIF_FSC_L = 1; + SD_Init(); /* SD Card I/F 初期化処理 */ + SD_AndFPGA( SD_OPTION, SD_CD_DETECT_TIME); /* CD 検出タイムをゼロクリア */ + + SD_port_en_numbers = SDCARD_PORT_NO_MAX; /*** サポートするポート数をデフォルトに設定 ***/ + SD_port_number = 0; /*** 現在のポート番号をデフォルトに設定 ***/ + + SDCARD_Backup_port0(); /* port0 backup */ + SDCARD_Backup_port1(); /* port1 backup */ + +#if (TARGET_OS_CTR == 1) + osRestoreInterrupts( irq_core_flag); /* 割り込み設定を元に戻す */ +#else + OS_RestoreInterrupts( irq_core_flag); /* 割り込み設定を元に戻す */ +#endif + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SDCARD_Layer_Init + + Description: initialize sequence for SD card. + SDカード規定の初期化手順 + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static SDMC_ERR_CODE SDCARD_Layer_Init(void) +{ + u32 ulCSize; +// SYSTIM wait_tim, limit_tim; + u16 read_block_len_val, mult_val; + +// u16 memory_exist, function_number; + SDCARD_Status = SDMC_NORMAL; /* カードステータスをクリア */ +/* func_SDCARD_CallBack = NULL; */ + pSDCARD_info = NULL; + SDCARD_EndFlag = TRUE; /* 転送処理完了フラグセット */ + SDCARD_MMCFlag = FALSE; /* MMCカード判定フラグクリア */ + SDCARD_SDHCFlag = FALSE; + SDCARD_SDFlag = FALSE; /* SDカード判定フラグクリア */ + SDCARD_OutFlag = FALSE; /* カード排出発生判定フラグクリア */ + ulSDCARD_Size = 0; /* カード全セクタ数クリア */ + TransCount = 0; /* 転送カウント変数クリア */ +// init_io_exist = 0; +// init_mem_exist = 0; + + SD_SetFPGA(SD_CLK_CTRL,(SD_CLK_CTRL_256)); /* SDクロックの周波数 144KHz */ + SD_EnableClock(); /* SDカードのクロックをイネーブルにする */ + + /* SD I/F部ダミー80クロック(1mSec)転送待ち(タイマーで待ちを実装しても良い) */ +#if (TARGET_OS_CTR == 1) + dly_tsk( 1); +#else + OS_Sleep( 1); +#endif + + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスをクリア */ + + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_RESET_TIMEOUT); /* タイムアウト判定用タイマスタート */ +#endif + + /***********************ここからSDIOテスト*************************/ +// SDIO_RWDirectCommand(); +/* while( 1) { + SDIO_ReadOCR(); + if(SDCARD_ErrStatus){ // エラーステータスの確認(エラー有り?) + init_io_exist = 0; + break; //メモリ初期化へ + } + memory_exist = SD_OCR[1] & SD_OCR_MEMORY_PRESENT; + function_number = (SD_OCR[1] & SD_OCR_NUM_OF_FUNC)>>12; + if( function_number > 0) { // function number が 0でないか? + if( (SD_OCR[0] == 0x0000)&&((SD_OCR[1]&SD_OCR_OCR_23_16)==0x0000)) { //OCR invalidか? + if( memory_exist) { //メモリが存在するか? + init_mem_exist = 1; + break; //メモリカード初期化へ + }else{ + init_mem_exist = 0; + return SDCARD_ErrStatus; //エラー(OCR invalid & メモリなし) + } + } + SDIO_WriteOCR(); + if(SDCARD_ErrStatus){ // エラーステータスの確認(エラー有り?) + init_io_exist = 0; + return SDCARD_ErrStatus; + } + init_io_exist = 1; + memory_exist = SD_OCR[1] & SD_OCR_MEMORY_PRESENT; + if( memory_exist) { + break; //メモリ初期化へ + }else{ + //IO初期化の続き(CMD3)へ + } + } + } +*/ + /******************************************************************/ + + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_RESET_TIMEOUT); /* タイムアウト判定用タイマスタート */ +#endif + + PRINTDEBUG( " CMD0 (GO_IDLE_STATE)\n"); + SD_ClrErr((u16)(~SDMC_ERR_FPGA_TIMEOUT)); /* タイムアウト以外のエラーをクリア */ + { +PRINTDEBUG( "SD_INFO1 : 0x%x\n", SD_INFO1); +PRINTDEBUG( "SD_INFO2 : 0x%x\n", SD_INFO2); +PRINTDEBUG( "SD_INFO1_MASK : 0x%x\n", SD_INFO1_MASK); +PRINTDEBUG( "SD_INFO2_MASK : 0x%x\n", SD_INFO2_MASK); +PRINTDEBUG( "SD_CLK_CTRL : 0x%x\n", SD_CLK_CTRL); +PRINTDEBUG( "SD_SIZE : 0x%x\n", SD_SIZE); + +PRINTDEBUG( "SD_INFO1_MASK : 0x%x\n", (*(vu32 *)(SD_IP_BASE + 0x20))); + } + SD_Command(SD_CMD_CMD | GO_IDLE_STATE); /* CMD0発行、レスポンス確認 */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?) */ + return SDCARD_ErrStatus; + } + + /*------- idle state -------*/ +#if (TARGET_OS_CTR == 1) + dly_tsk( 1); /* 1ms待ち */ +#else + OS_Sleep( 1); +#endif + SD_SendIfCond(); /* CMD8発行、レスポンス確認 */ + if( !SDCARD_SDHCFlag) { /* SDHC以外は失敗してるはずなので */ + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーフラグをクリアしておく */ + } + + while(!(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT)){ /* タイムアウトになったら抜ける */ + SD_ClrErr((u16)(~SDMC_ERR_FPGA_TIMEOUT)); + + SD_RCA = 0; /* RCA = 0をセット */ + if(!SDCARD_MMCFlag){ /* MMCカードフラグが 0(OFF) か? */ + if(!SD_AppCommand()){ /* CMD55 発行処理が正常か? */ + SDCARD_MMCFlag = FALSE; /* MMCカードフラグクリア */ + if(!SD_AppOpCond()){ /* ACMD41発行処理が正常か?(OCR31bit = L の時 No Response) */ + SDCARD_SDFlag = TRUE; /* SDカードフラグセット */ + break; + } + }else{ /* CMD55 が正常終了しない */ + if(SDCARD_ErrStatus == SDMC_ERR_TIMEOUT){ /* タイムアウト(==No Response)か? */ + SDCARD_MMCFlag = TRUE; /* MMCカードフラグセット */ + }else{ +// break; //コメントアウトしないとSDカードの初期化に失敗する + } + } + } + if(SDCARD_MMCFlag){ /* MMCカードフラグが 1(ON) のとき */ + SD_RCA = 1; /* RCA = 1をセット */ + if(!SD_SendOpCond()){ /* CMD1発行処理が正常か? */ + break; + } + } + } +/* + if( SDCARD_SDHCFlag) { + SD_ReadOCR(); + } +*/ + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?)*/ + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーかチェック */ + SD_ClrErr(SDMC_ERR_FPGA_TIMEOUT); /* タイムアウトエラーの設定クリア */ + SD_SetErr(SDMC_ERR_RESET); /* 初期化カードリセットコマンド時1.5秒タイムアウトエラーの設定 */ + } + SDCARD_MMCFlag = FALSE; /* MMCカードフラグクリア */ + return SDCARD_ErrStatus; + } + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_INITIAL_TIMEOUT); /* タイムアウト判定用タイマスタート */ +#endif + + SD_SendCID(); /* CMD2発行 レスポンス確認 */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?) */ + return SDCARD_ErrStatus; + } + + while(1){ + SD_SendRelativeAddr(); /* CMD3発行 レスポンス確認 正常終了時 RCA<-ResのRCA */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?) */ + return SDCARD_ErrStatus; + } + if(SD_RCA != 0){ + break; + } + } + + /*------- standby state -------*/ + + SD_SendCSD(); /* CMD9発行 レスポンス確認 */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?) */ + return SDCARD_ErrStatus; + } + SDCARD_WP_PERMANENT = (u16)(SD_CSD[0] & (u16)(SDCARD_WP_PERMANENT_BIT)); + SDCARD_WP_TEMPORARY = (u16)(SD_CSD[0] & (u16)(SDCARD_WP_TEMPORARY_BIT)); + + /* 転送速度設定 */ + SD_ClockDivSet(SD_RSP5); /* SDカードの動作クロック設定 (CSD[5]) */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?) */ + return SDCARD_ErrStatus; + } + + /* Command toggles acard between the Stand-by and Transfer states */ + SD_SelectCard(); /* CMD7発行 レスポンス確認 */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?) */ + return SDCARD_ErrStatus; + } + + /*------- translate state -------*/ + + SDCARD_SectorSize = SECTOR_SIZE; /* セクタサイズ デフォルト 512bytes */ + SD_SetBlockLength(SDCARD_SectorSize); /* CMD16 ブロックサイズの設定 */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?) */ + return SDCARD_ErrStatus; + } + +#if SCR + SD_SelectBitWidth(FALSE); /* CMD55->ACMD6 ビット幅の選択 1bit */ + + /* ACMD51 発行 SD configuration register (SCR) */ + if(SDCARD_SDFlag){ /* SDカードフラグ ON かチェック */ + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + i_sdmcSendSCR(); + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?) */ + return SDCARD_ErrStatus; + } +#if TIMEOUT + SDCARD_TimerStart(SDCARD_CLOCK_WAIT);/* タイムアウト判定用タイマスタート */ +#endif + } +#endif + SD_EnableClock(); /* SD-CLK Enable */ + + if(SDCARD_MMCFlag){ /* MMCカード ON かチェック */ + if( ((SD_CSD[7] & 0x3C)>>2) >= 4) { + MMCP_SetBusWidth( TRUE); + } +// SD_SelectBitWidth(FALSE); /* CMD55->ACMD6 ビット幅の選択 1bit */ + }else{ + SD_SelectBitWidth(TRUE); /* CMD55->ACMD6 ビット幅の選択 4bit */ + } + + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + + if(SDCARD_SDFlag){ /* SDカードフラグ ON かチェック */ + if (SDCARD_SD_Status()) /* CMD55->ACMD13 カードステータスを取得 */ + return SDCARD_ErrStatus; + if(SD_SDSTATUS[1] & SD_MEMORY_CARD){ + SDCARD_SDFlag = FALSE; /* SDカードフラグクリア */ + } + } + + /*--------------カードサイズの算出---------------*/ + if( ((SD_CSD[7] & CSD_STRUCT_BIT_127_126) >> 6) == 0x1) { //SDHCのとき + sdmc_current_spec.csd_ver2_flag = 1; + ulCSize = (u32)((((u32)(SD_CSD[3]) & CSD_C_SIZE_BIT_69_56) << 8) + + ((SD_CSD[2] & CSD_C_SIZE_BIT_55_48) >> 8) + 1); + ulCSize = ulCSize * 1024; //もともと512KByte単位なのを512Byte単位にする + /* データ領域サイズ算出 */ + sdmc_current_spec.memory_capacity = ulCSize; + ulSDCARD_Size = ulCSize; + /* プロテクト領域サイズ算出 */ + sdmc_current_spec.protected_capacity = (((SD_SwapByte( &SD_SDSTATUS[2])) << 16) + + (SD_SwapByte( &SD_SDSTATUS[3]))) / 0x200; + /*トータルサイズ算出 */ + sdmc_current_spec.card_capacity = sdmc_current_spec.memory_capacity + + sdmc_current_spec.protected_capacity; + + }else{ //従来SDカードのとき + sdmc_current_spec.csd_ver2_flag = 0; + ulCSize = (u32)(((SD_CSD[3] & CSD_C_SIZE_BIT_71_62) >> 6) + + ((SD_CSD[4] & CSD_C_SIZE_BIT_73_72) << 10) + 1); + mult_val = ((SD_CSD[2] & CSD_C_SIZE_MULT) >> 7) + 2; //2の乗数 + ulCSize = ulCSize << mult_val; + if(SDCARD_MMCFlag){ /* MMCカードフラグON かチェック */ + read_block_len_val = ((SD_CSD[4] & CSD_READ_BL_LEN) >> 8); + ulCSize = (ulCSize << read_block_len_val); + }else{ /* SDカードフラグ(SDCARD_SDFlag)ON のはず */ + read_block_len_val = (((SD_CSD[1] & CSD_WRITE_BL_LEN_BIT_25_24) << 2) | + ((SD_CSD[0] & CSD_WRITE_BL_LEN_BIT_23_22) >> 14)); + ulCSize = (ulCSize << read_block_len_val); + } + /* データ領域サイズ算出 */ + ulCSize /= SDCARD_SectorSize; /* 全セクタ数の算出 */ + sdmc_current_spec.memory_capacity = ulCSize; + ulSDCARD_Size += ulCSize; /* 全セクタ数のセット */ + /* プロテクト領域サイズ算出 */ + sdmc_current_spec.protected_capacity = ((SD_SwapByte( &SD_SDSTATUS[2])) << 16) + + (SD_SwapByte( &SD_SDSTATUS[3])); + sdmc_current_spec.protected_capacity <<= mult_val; + sdmc_current_spec.protected_capacity <<= read_block_len_val; + sdmc_current_spec.protected_capacity /= SDCARD_SectorSize; //TODO:構造体にまとめること + /*トータルサイズ算出 */ + sdmc_current_spec.card_capacity = sdmc_current_spec.memory_capacity + + sdmc_current_spec.protected_capacity; + } + sdmc_current_spec.SS = SDCARD_SectorSize; + + PRINTDEBUG( "SD memory capacity : 0x%x\n", sdmc_current_spec.memory_capacity); + PRINTDEBUG( "SD protected capacity : 0x%x\n", sdmc_current_spec.protected_capacity); + PRINTDEBUG( "SD total capacity : 0x%x\n", sdmc_current_spec.card_capacity); + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: i_sdmcMPInit + + Description: initialize SD card in multi ports. + マルチポートのSDカード初期化 + + Arguments: + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +static SDMC_ERR_CODE i_sdmcMPInit( void) +{ + if(((SD_port_number == SDCARD_PORT0) && (!SD_CheckFPGAReg(SD_INFO1,SD_INFO1_DETECT))) || + ((SD_port_number == SDCARD_PORT1) && (!SD_CheckFPGAReg(EXT_CD,EXT_CD_PORT1_DETECT))) || + (SD_port_number > SDCARD_PORT1)) + { + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスをクリア */ + SDCARD_OutFlag = TRUE; /* 排出フラグをセット */ + }else{ + SDCARD_ErrStatus = SDCARD_Layer_Init(); + SDCARD_OutFlag = FALSE; /* 排出フラグをリセット */ + } + + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + SD_DisableClock(); /* SD-CLK Disable */ + SD_EnableInfo(); /* SD Card 挿抜 割り込み許可 */ + + return SDCARD_ErrStatus; +} + + +/*---------------------------------------------------------------------------* + Name: sdmcGetStatus + + Description: get card status + カードの状態を取得する + bit15 SDカード判別ビット(検出したら1) + bit14 MMCカード判別ビット(検出したら1) + bit10 IO3 card detect(検出したら1) ※CTRではプルアップのため使えない + bit9 IO3 card inserted(挿入動作で1) ※CTRではプルアップのため使えない + bit8 IO3 card removed(脱動作で1) ※CTRではプルアップのため使えない + bit7 write protect(書き込み禁止の場合1) + bit5 card detect(検出したら1) + bit4 card inserted(挿入動作で1) + bit3 card removed(脱動作で1) + bit2 R/W access all end + bit0 Response end + + Arguments: *status : カードの状態を格納する変数へのポインタ + + Returns: 0 : success + > 0 : error code + *---------------------------------------------------------------------------*/ +/*----------------------------------------------- +SD_INFO1レジスタ +bit[10,9,8,7] = DAT3CD, DAT3IN, DAT3OUT, WP +bit[5, 4, 3, 2] = CD, INS, REM, ALLEND +bit0 = RESEND +------------------------------------------------- +EXT_CDレジスタ +bit[2,1,0] = P1CD, P1INS, P1REM +------------------------------------------------- +EXT_CD_DAT3レジスタ +bit[2, 1, 0] = P1DCD, P1DINS, P1DREM +------------------------------------------------- +EXT_WPレジスタ +bit0 = P1WP +-----------------------------------------------*/ +SDMC_ERR_CODE sdmcGetStatus(u16 *status) +{ + u16 SD_INFO1_STATUS; + + SD_INFO1_STATUS = SD_INFO1; /* SD_INFO1レジスタ読み出し */ + *status = SD_INFO1_STATUS; /* 論理反転 */ + + /*--- ポート0のとき ---*/ + if(SD_port_number == SDCARD_PORT0) + { + *status &= SDCARD_FLAG_CLR; /* SD/MMCフラグクリア */ + } + /*--- ポート1のとき ---*/ + else if (SD_port_number == SDCARD_PORT1) + { + *status &= SDCARD_PORT1_CLR; /* port1に関係ない部分をクリア */ + SD_INFO1_STATUS = (u16)((EXT_CD & 0x0007) << 3); + SD_INFO1_STATUS |= ((EXT_CD_DAT3 & 0x0007) << 8); + SD_INFO1_STATUS |= ((EXT_WP & 0x0001) << 7); + *status |= SD_INFO1_STATUS; /* カードport1フラグ設定 */ + } + /*--- SD/MMCフラグをセット ---*/ + if( SDCARD_MMCFlag) { /* 検出したカードがMMCカードの時 */ + *status |= SDCARD_FLAG_MMC; /* カード判定部分MMCカード */ + } + if( SDCARD_SDFlag) { /* 検出したカードがSDカードの時 */ + *status |= SDCARD_FLAG_SD; /* カード判定部分SDカード */ + } + + return SDMC_NORMAL; +} + + +/*---------------------------------------------------------------------------* + Name: SDCARD_GetR1Status + + Description: get the card status of R1 response. + R1レスポンスのカードステータスを取得する。 + + Arguments: None + + Returns: SDCARD_Status : R1の[39:8] + *---------------------------------------------------------------------------*/ +static u32 SDCARD_GetR1Status(void) +{ + /* SD_CheckStatusでSDCARD_Statusに値が入る */ + return SDCARD_Status; +} + + +/*---------------------------------------------------------------------------* + Name: sdmcReadFifo + + Description: read from card. + ラッパーのFIFOを使用してカードからの読み出し。 + + Arguments: buf : 読み出したデータを格納するためのバッファのアドレス + bufsize : 読み出しサイズ(セクタ数) + offset : 読み出し開始オフセット(セクタ番号) + info : 実行結果を格納するための構造体へのアドレス + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +SDMC_ERR_CODE sdmcReadFifo(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info) +{ + SDCARDMsg SdMsg; +#if (TARGET_OS_CTR == 1) + u32 recv_dat; +#else + OSMessage recv_dat; +#endif + SDMC_ERR_CODE api_result; //SDCARD関数の返り値 + + SdMsg.buf = buf; + SdMsg.bufsize = bufsize; + SdMsg.offset = offset; + SdMsg.func = func; + SdMsg.info = info; + SdMsg.operation = SD_OPERATION_READ_WITH_FIFO; + +#if (TARGET_OS_CTR == 1) + PRINTDEBUG( "readfifo : snd_dtq begin\n"); + snd_dtq( sdmc_dtq_id, (VP_INT)&SdMsg); + + /* 返り値待ち */ + PRINTDEBUG( "readfifo : rcv_dtq begin\n"); + rcv_dtq( sdmc_result_dtq_id, (VP_INT*)&recv_dat); + + api_result = (SDMC_ERR_CODE)recv_dat; +#else + recv_dat = (OSMessage)&SdMsg; //SdMsgのアドレスを伝える + OS_SendMessage( &sdmc_dtq, recv_dat, OS_MESSAGE_BLOCK); + + /* 返り値待ち */ + OS_ReceiveMessage( &sdmc_result_dtq, &recv_dat, OS_MESSAGE_BLOCK); + api_result = (SDMC_ERR_CODE)recv_dat; +#endif + + return api_result; +} + +/*---------------------------------------------------------------------------* + Name: SDCARDi_ReadFifo + + Description: read from card. + ラッパーのFIFOを使用してカードからの読み出し。 + + Arguments: buf : 読み出したデータを格納するためのバッファのアドレス + bufsize : 読み出しサイズ(セクタ数) + offset : 読み出し開始オフセット(セクタ番号) + info : 実行結果を格納するための構造体へのアドレス + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +static SDMC_ERR_CODE SDCARDi_ReadFifo(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info) +{ + SDMC_ERR_CODE result; + + /* FIFO Empty割り込み無効、FIFO Full割り込み有効 */ + *(SDIF_CNT) = (*(SDIF_CNT) & (~SDIF_CNT_FEIE)) | SDIF_CNT_FFIE; + *(SDIF_FDS) = (u16)SDCARD_SectorSize; /* FIFOのデータサイズ */ + *(SDIF_FSC) = bufsize; + *(SDIF_CNT) |= SDIF_CNT_USEFIFO; /* FIFO使用フラグON */ + CC_EXT_MODE = CC_EXT_MODE_DMA; /* DMAモードON */ + + result = SDCARDi_Read( buf, bufsize, offset, func, info); + + /* FIFO無効に */ + *(SDIF_CNT) &= (~SDIF_CNT_USEFIFO); /* FIFO使用フラグOFF */ + CC_EXT_MODE = CC_EXT_MODE_PIO; /* PIOモード(DMAモードOFF) */ + + return result; +} + +/*---------------------------------------------------------------------------* + Name: sdmcRead + + Description: read from card. + カードからの読み出し。 + + Arguments: buf : 読み出したデータを格納するためのバッファのアドレス + bufsize : 読み出しサイズ(セクタ数) + offset : 読み出し開始オフセット(セクタ番号) + info : 実行結果を格納するための構造体へのアドレス + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +SDMC_ERR_CODE sdmcRead(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info) +{ + SDCARDMsg SdMsg; +#if (TARGET_OS_CTR == 1) + u32 recv_dat; +#else + OSMessage recv_dat; +#endif + SDMC_ERR_CODE api_result; + + SdMsg.buf = buf; + SdMsg.bufsize = bufsize; + SdMsg.offset = offset; + SdMsg.func = func; + SdMsg.info = info; + SdMsg.operation = SD_OPERATION_READ; + +#if (TARGET_OS_CTR == 1) + snd_dtq( sdmc_dtq_id, (VP_INT)&SdMsg); + + /* 返り値待ち */ + rcv_dtq( sdmc_result_dtq_id, (VP_INT*)&recv_dat); + + api_result = (SDMC_ERR_CODE)recv_dat; +#else + recv_dat = (OSMessage)&SdMsg; + OS_SendMessage( &sdmc_dtq, recv_dat, OS_MESSAGE_BLOCK); + + /* 返り値待ち */ + OS_ReceiveMessage( &sdmc_result_dtq, &recv_dat, OS_MESSAGE_BLOCK); + api_result = (SDMC_ERR_CODE)recv_dat; +#endif + + return api_result; +} + +/*---------------------------------------------------------------------------* + Name: SDCARDi_Read + + Description: read from card. + カードからの読み出し。 + + Arguments: buf : 読み出したデータを格納するためのバッファのアドレス + bufsize : 読み出しサイズ(セクタ数) + offset : 読み出し開始オフセット(セクタ番号) + info : 実行結果を格納するための構造体へのアドレス + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +static SDMC_ERR_CODE SDCARDi_Read(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info) +{ + s16 nRetryCount; /* リトライ回数カウント */ + SDMC_ERR_CODE SaveErrStatus; /* エラーステータス保存用 */ + u32 SaveStatus; /* カードステータス保存用 */ + + for( nRetryCount=0; nRetryCountresult = SDCARD_ErrStatus; /* SdmcResultInfo に情報設定 */ + pSDCARD_info->resid = (ulSDCARD_SectorCount - ulSDCARD_RestSectorCount) * + SDCARD_SectorSize; /* SdmcResultInfo に処理セクタ数設定 */ + } + } + } + }else{ /*--- 残りセクタ数が 0 でないとき ---*/ + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ +#if TIMEOUT + SDCARD_TimerStart(SDCARD_RW_TIMEOUT); /* タイムアウト判定用タイマスタート(4000msec) */ +#endif + } +} + +/*---------------------------------------------------------------------------* + Name: SYSFPGA_irq + + Description: insert/remove/error/access end interrupt handler. + BREやBWE割り込みを除く割り込みのハンドラ。挿抜、エラー発生、 + アクセス終了の割り込み発生時にそれぞれの処理を行う。 + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void SYSFPGA_irq(void) +{ + /*--- ポート0の挿抜割り込みチェックとコールバック起動 ---*/ + if(!SD_CheckFPGAReg( SD_INFO1_MASK, SD_INFO1_MASK_REMOVE)){ /* SD Card 抜け 割込み許可状態か? */ + if( SD_CheckFPGAReg( SD_INFO1, SD_INFO1_REMOVE)){ /* SD Card 抜け 発生か? */ + SD_AndFPGA( SD_INFO1,(~SD_INFO1_REMOVE)); /* INFO1の抜けフラグを落とす */ + SDCARD_OutFlag = TRUE; /* 排出フラグセット */ + if(func_SDCARD_Out){ /* コールバック関数のNullチェック */ + SDCARD_IO_Port = SDCARD_PORT0; /* カード抜けポート番号を設定 */ + func_SDCARD_Out(); /* カード抜けコールバック関数呼び出し */ + } + } + } + if(!SD_CheckFPGAReg( SD_INFO1_MASK, SD_INFO1_MASK_INSERT)){ /* SD Card 挿入 割込み許可状態か? */ + if( SD_CheckFPGAReg( SD_INFO1, SD_INFO1_INSERT)){ /* SD Card 挿入 発生か? */ + SD_AndFPGA( SD_INFO1, (~SD_INFO1_INSERT)); /* INFO1の挿入フラグを落とす */ + SDCARD_OutFlag = FALSE; /* 排出フラグリセット */ + if(func_SDCARD_In){ /* コールバック関数のNullチェック */ + SDCARD_IO_Port = SDCARD_PORT0; /* カード挿入ポート番号を設定 */ + func_SDCARD_In(); /* カード挿入コールバック関数呼び出し */ + } + } + } + /* (CTRはポート1のCD端子が未接続(常に挿入状態)なので、ポート1の挿抜チェックは行わない) */ + + /*--- 割り込み要求と割り込みマスクを保存 ---*/ + SD_GetFPGA( SD_INFO1_VALUE, SD_INFO1); + SD_GetFPGA( SD_INFO1_MASK_VALUE, SD_INFO1_MASK); + SD_GetFPGA( SD_INFO2_VALUE, SD_INFO2); + SD_GetFPGA( SD_INFO2_MASK_VALUE, SD_INFO2_MASK); + /*------------------------------------------*/ + + /*--- SD_INFO2のエラーフラグ作成 ---*/ + SD_INFO_ERROR_VALUE = (u16)(SD_INFO2_VALUE & (~SD_INFO2_MASK_VALUE)); + /*--- エラーステータス作成 (RESTIMEOUTとILAエラーのフラグは反映しない) ---*/ + SDCARD_ErrStatus |= SD_INFO_ERROR_VALUE & (~(SD_INFO2_ERR_RESTIMEOUT)) & + (~(SD_INFO2_ERR_ILA)) & SD_INFO2_MASK_ERRSET; + + /*--- RESTIMEOUTとILAエラーはフラグの位置をずらして反映する ---*/ + if( SD_INFO_ERROR_VALUE & SD_INFO2_ERR_ILA) { + SDCARD_ErrStatus |= SDMC_ERR_ILA; /* イリーガルアクセスエラー発生 */ + } + if( SD_INFO_ERROR_VALUE & SD_INFO2_ERR_RESTIMEOUT) { + SDCARD_ErrStatus |= SDMC_ERR_TIMEOUT; /* Response Time out エラー発生 */ + }/*------------------------------------------------------------*/ + + SD_AndFPGA( SD_INFO2,(~(SD_INFO2_ERROR_SET))); /* SD_INFO2のエラーフラグを全て落とす */ + if ( SDCARD_ErrStatus) { /* 何らかのエラーが発生しているか? */ + SD_OrFPGA( SD_INFO2_MASK, SD_INFO2_MASK_ERRSET); /* 全てのエラー割り込みを禁止 */ + } + /*--- SD_INFO2のエラーチェック終了 ---*/ + + + /*--- SD_INFO1の割り込み発生状況フラグ作成 ---*/ + SD_INFO_ERROR_VALUE = (u16)(SD_INFO1_VALUE & (~SD_INFO1_MASK_VALUE)); + + if( SD_INFO_ERROR_VALUE & SD_INFO1_MASK_ALL_END) { /* R/W access all end 割込み発生か? */ + SD_OrFPGA( SD_INFO1_MASK, SD_INFO1_MASK_ALL_END); /* INFO1の access all end 割込み禁止 */ + SDCARD_FPGA_Flag = TRUE; /* R/Wアクセス終了(IP処理完了)フラグセット */ + if( SDCARD_ATC0_Flag) { /* 転送完了処理(SDCARD_ATC0_irq)が完了しているか? */ + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + SD_TransEndFPGA(); /* 転送終了処理(割り込みマスクを禁止に戻す) */ + if( SDCARD_EndFlag == FALSE) { /* 転送が終了していないか? */ + SDCARD_EndFlag = TRUE; /* 転送処理完了フラグセット */ + if( pSDCARD_info) { /* Nullチェック */ + pSDCARD_info->result = SDCARD_ErrStatus; /* SdmcResultInfo に情報設定 */ + pSDCARD_info->resid = (ulSDCARD_SectorCount - ulSDCARD_RestSectorCount) * + SDCARD_SectorSize; /* SdmcResultInfo に処理セクタ数設定 */ + } + } /* 転送が終了済みのとき */ + return; + } /* 全ATC完了フラグ OFF の場合 */ + if( SDCARD_ErrStatus != SDMC_NORMAL) { /* エラーが発生している場合 */ + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + if( SDCARD_EndFlag == FALSE) { /* 転送が終了していないか? */ + SD_TransEndFPGA(); /* カード転送の終了処理 */ + SDCARD_EndFlag = TRUE; /* 転送処理完了フラグセット */ + /* SD_Init(); */ /* SD Cardインターフェース部をリセット&初期設定 */ + if( pSDCARD_info) { /* Nullチェック */ + pSDCARD_info->result = SDCARD_ErrStatus; /* SdmcResultInfo に情報設定 */ + pSDCARD_info->resid = (ulSDCARD_SectorCount - ulSDCARD_RestSectorCount) * + SDCARD_SectorSize; /* SdmcResultInfo に処理セクタ数設定 */ + } + } + } + } /* R/W access all end 割り込み未発生 */ +} + +/*---------------------------------------------------------------------------* + Name: SDCARD_TimerStart + + Description: start timer for measure timeout. + タイムアウト計測を開始する + + Arguments: tim : ms単位のタイムアウト時間 + (50msを超える値の場合は50ms単位になる) + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_TimerStart(u32 tim) +{ +#if (TARGET_OS_CTR == 1) + sta_alm( sdmc_alm_id, tim); + PRINTDEBUG( "Timer Started.\n"); +#else + OSTick tim_tick; + + tim_tick = OS_MicroSecondsToTicks( tim); //us単位からTick単位へ + + OS_CancelAlarm( &sdmc_alm); //アラーム破棄 + OS_SetAlarm( &sdmc_alm, tim_tick, SDCARD_Timer_irq, NULL); //アラームセット +#endif +} + +/*---------------------------------------------------------------------------* + Name: SDCARD_TimerStop + + Description: stop timer + タイムアウト計測を停止する + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_TimerStop(void) +{ +#if (TARGET_OS_CTR == 1) + stp_alm( sdmc_alm_id); + PRINTDEBUG( "Timer Stopped.\n"); + + //memo:割り込み禁止状態にしてstp_almを呼んでも停止しないので注意 +#else + OS_DisableIrq(); + OS_CancelAlarm( &sdmc_alm); + OS_EnableIrq(); +#endif +} + +/*---------------------------------------------------------------------------* + Name: SDCARD_Timer_irq + + Description: timer interrupt handler. + タイマー割り込みハンドラ + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_Timer_irq(void* arg) +{ +#if (SD_DEBUG_PRINT_ON == 1) + u16 tmp; + + PRINTDEBUG( ">>>>> Timer interrupt (Timeout)\n"); + + SDCARD_ErrStatus |= SDMC_ERR_FPGA_TIMEOUT; /* タイムアウトエラービットの設定 */ + + tmp = SD_INFO1; + PRINTDEBUG( "SD_INFO1 : 0x%x\n", tmp); + tmp = SD_INFO1_MASK; + PRINTDEBUG( "SD_INFO1_MASK : 0x%x\n", tmp); + tmp = SD_INFO2; + PRINTDEBUG( "SD_INFO2 : 0x%x\n", tmp); + tmp = SD_INFO2_MASK; + PRINTDEBUG( "SD_INFO2_MASK : 0x%x\n", tmp); + tmp = SD_ERR_STS1; + PRINTDEBUG( "SD_ERR_STS1 : 0x%x\n", tmp); +#if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + *(vu16*)0x08030200 = 1; +#endif + tmp = SD_ERR_STS2; +#if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + *(vu16*)0x08030200 = 0; +#endif + PRINTDEBUG( "SD_ERR_STS2 : 0x%x\n", tmp); + tmp = *(vu16 *)(SD_IF_BASE+0x00); + PRINTDEBUG( "SD_CNT : 0x%x\n", tmp); + tmp = SD_SECCNT; + PRINTDEBUG( "SD_SECCNT : 0x%x\n", tmp); +#endif + + + if(SDCARD_EndFlag == FALSE){ /* 転送処理完了フラグの確認(クリア?)*/ + SDCARD_EndFlag = TRUE; /* 転送処理完了フラグをセット */ + /* SD_TransEndFPGA(); */ /* カード転送の終了処理 */ + /* SD_StopTransmission(); */ /* カード転送終了設定 */ + /* SD_Init(); */ /* SD Cardインターフェース部をリセット&初期設定 */ + if(pSDCARD_info){ /* Nullチェック */ + pSDCARD_info->result = SDCARD_ErrStatus;/* SdmcResultInfo に情報設定 */ + pSDCARD_info->resid = (ulSDCARD_SectorCount - ulSDCARD_RestSectorCount) * + SDCARD_SectorSize; /* SdmcResultInfo に処理セクタ数設定 */ + } + } + + /**/ + PRINTDEBUG( "----- Wakeup Thread -----\n"); +#if (TARGET_OS_CTR == 1) + iwup_tsk( sdmc_tsk_id); + PRINTDEBUG( "id : 0x%x\n", sdmc_tsk_id); +#else + OS_WakeupThreadDirect( &sdmc_tsk); +#endif +} + + +/*---------------------------------------------------------------------------* + Name: i_sdmcSendSCR + + Description: get SCR register. + SCRを取得する(DATライン経由で8バイト送られてくる)。 + MultiBlock R/W と異なり、DATライン経由で転送されてくるSDカードの + レジスタは、MSBから先に送られてくることに注意。 + (Physical Layer Specification 2.00 p12-13参照) + + Arguments: None + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +static u16 i_sdmcSendSCR(void) +{ + SDMC_ERR_CODE SaveErrStatus; /* エラーステータス保存用 */ + u32 SaveStatus; /* カードステータス保存用 */ + u32 ulSave_SectorSize; /* セクタサイズ保存用 */ + + SD_EnableClock(); /* SD-CLK Enable */ + + /* ブロックサイズの設定 */ + ulSave_SectorSize = SDCARD_SectorSize; /* セクタサイズの保存 */ + SDCARD_SectorSize = 8; /* SCR レジスタ 転送サイズ 8bytes */ + SD_SetBlockLength(SDCARD_SectorSize); /* SDカードデータ転送サイズ 8byte 設定 */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?)*/ + return SDCARD_ErrStatus; + } + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_RW_TIMEOUT); /* タイムアウト判定用タイマスタート */ +#endif + + if(SD_AppCommand()){ /* RCA設定後 CMD55発行処理が正常終了しない? */ + SD_DisableClock(); /* SD-CLK Disable */ + return SDCARD_ErrStatus; /* エラー終了 */ + } + +/* func_SDCARD_CallBack = NULL; */ + pSDCARD_info = NULL; + ulSDCARD_RestSectorCount = ulSDCARD_SectorCount = 1; /* 残りセクタサイズ、セクタカウントに1を設定 */ + pSDCARD_BufferAddr = SD_SCR; /* データ格納バッファのアドレスを設定 */ + + /* 転送前の準備処理 */ + SDCARD_ATC0_Flag = FALSE; /* 全ATC完了フラグクリア */ + SDCARD_FPGA_Flag = FALSE; /* FPGA処理完了フラグクリア */ + SDCARD_EndFlag = FALSE; /* 転送処理完了フラグクリア */ + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスのクリア */ + +#if SCR + thread_flag = TRUE; + SD_SendSCR(); /* SCRの取得コマンド発行 */ + PRINTDEBUG( "----- Sleep Thread -----\n"); + //can_wup( 0); +#if (TARGET_OS_CTR == 1) + slp_tsk(); +#else + OS_SleepThread( NULL); +#endif + PRINTDEBUG( "waked\n"); + thread_flag = FALSE; + + while(!SDCARD_EndFlag){ /* カードアクセス終了待ち */ + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか確認 */ + return SDCARD_ErrStatus; + } + } + + if(!(SDCARD_ErrStatus & SDMC_ERR_R1_STATUS)){ /* コマンドレスポンス(R1)のカードステータスがエラーでないか確認 */ + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1)の Card Status チェック */ + if(!(SDCARD_ErrStatus & SDMC_ERR_R1_STATUS)){ /* コマンドレスポンス(R1)のカードステータスがエラーでないか確認 */ + SD_SendStatus(); /* カードステータスの取得コマンド発行処理 */ + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1)の Card Status チェック */ + } + } + SaveStatus = SDCARD_Status; /* カードステータスの保存 */ + SaveErrStatus = SDCARD_ErrStatus; /* エラーステータスの保存 */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?)*/ + i_sdmcErrProcess(); /* エラー時の処理 */ + } + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか確認 */ + return SDCARD_ErrStatus; + } + SDCARD_Status = SaveStatus; /* カードステータスの復帰 */ + SDCARD_ErrStatus = SaveErrStatus; /* エラーステータスの復帰 */ +#endif + + SDCARD_SectorSize = ulSave_SectorSize; /* 保存していたセクタサイズを戻す(default:512bytes)*/ + SD_SetBlockLength(SDCARD_SectorSize); /* SDカードデータ転送サイズ 512bytes 設定 */ + + SD_DisableClock(); /* SD-CLK Disable */ + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: i_sdmcErrProcess + + Description: when error is occured, get Card Status to check and stop the + transfer. + エラー発生時の処理。カードステータスを取得し、データ転送中で + あればストップさせる。 + + Arguments: None + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +static u16 i_sdmcErrProcess(void) +{ + u16 usRSP0; + + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスのクリア */ + + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_ERRPROC_TIMEOUT); /* タイムアウト判定用タイマスタート(2000msec) */ +#endif + + SD_SendStatus(); /* CMD13 addressed card sends its status register 発行、レスポンス待ち */ + + if(!SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?)*/ + SD_GetFPGA( usRSP0, SD_RSP0); + usRSP0 = (u16)(( usRSP0 & RSP_R1_CURRENT_STATE) >> 1); /* カレントステートを取り出す */ + if((usRSP0 == CURRENT_STATE_DATA) || (usRSP0 == CURRENT_STATE_RCV)){ /* SDCARD Status が data rcv の時 */ + SD_Command(SD_CMD_CMD | STOP_TRANSMISSION); /* CMD12(StopTransmission)発行処理 */ + } + } + + SDCARD_TimerStop(); /* タイムアウト判定用タイマストップ */ + + return SDCARD_ErrStatus; +} + + +/*---------------------------------------------------------------------------* + Name: sdmcGetCardSize + + Description: get card size (number of sectors). + カードのセクタ数を取得する + + Arguments: None + + Returns: number of sectors in the SD card which inserted. + *---------------------------------------------------------------------------*/ +u32 sdmcGetCardSize(void) +{ + /* カード全セクタ数 (SDCARD_Layer_Init関数内で算出される) */ + return ulSDCARD_Size; +} + + +/*---------------------------------------------------------------------------* + Name: SDCARD_SD_Status + + Description: get SD Status. + SDステータスを取得する(DATライン経由で64バイト送られてくる)。 + カードステータスではないことを留意。 + MultiBlock R/W と異なり、DATライン経由で転送されてくるSDカードの + レジスタは、MSBから先に送られてくることに注意。 + (Physical Layer Specification 2.00 p12-13参照) + + Arguments: None + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +static u16 SDCARD_SD_Status(void) +{ + SDMC_ERR_CODE SaveErrStatus; /* エラーステータス保存用 */ + u32 SaveStatus; /* カードステータス保存用 */ + u32 ulSave_SectorSize; /* セクタサイズ保存用 */ + u32 ulSaveRestSectorCount; /* 残りセクタサイズ保存用 */ + + SD_EnableClock(); /* SD-CLK Enable */ + + /* ブロックサイズの設定 */ + ulSave_SectorSize = SDCARD_SectorSize; /* セクタサイズの保存 */ + SDCARD_SectorSize = 64; /* SD_STATUS 転送サイズ 64bytes */ + SD_SetBlockLength(SDCARD_SectorSize); /* SDカードデータ転送サイズ 64byte 設定 */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?)*/ + return SDCARD_ErrStatus; + } + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_RW_TIMEOUT); /* タイムアウト判定用タイマスタート(4000msec) */ +#endif + + if(SD_AppCommand()){ /* RCA設定後 CMD55発行処理 が正常終了しない?*/ + SD_DisableClock(); /* SD-CLK Disable */ + return SDCARD_ErrStatus; /* エラー終了 */ + } + +/* func_SDCARD_CallBack = NULL; */ + pSDCARD_info = NULL; + ulSaveRestSectorCount = ulSDCARD_RestSectorCount; /* 残りセクタサイズを保存(TODO:いらない?) */ + ulSDCARD_RestSectorCount = ulSDCARD_SectorCount = 1; /* 残りセクタサイズ、セクタカウントを1に設定 */ + pSDCARD_BufferAddr = SD_SDSTATUS; /* データ格納バッファのアドレスを設定 */ + + /* 転送前の準備処理 */ + SDCARD_ATC0_Flag = FALSE; /* 全ATC完了フラグクリア */ + SDCARD_FPGA_Flag = FALSE; /* FPGA処理完了フラグクリア */ + SDCARD_EndFlag = FALSE; /* 転送処理完了フラグクリア */ + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスのクリア */ + + thread_flag = TRUE; + SD_SDStatus(); /* ACMD13 SD_STATUSの取得コマンド発行処理 */ + PRINTDEBUG( "----- Sleep Thread -----\n"); +#if (TARGET_OS_CTR == 1) + //can_wup( 0); + slp_tsk(); +#else + OS_SleepThread( NULL); +#endif + PRINTDEBUG( "waked\n"); + thread_flag = FALSE; + + while(!SDCARD_EndFlag){ /* カードアクセス終了待ち */ + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか確認 */ + return SDCARD_ErrStatus; + } + } + + if(!(SDCARD_ErrStatus & SDMC_ERR_R1_STATUS)){ /* コマンドレスポンス(R1)のカードステータスが何らかのエラーでないか確認 */ + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1)の Card Status チェック */ + if(!(SDCARD_ErrStatus & SDMC_ERR_R1_STATUS)){ /* コマンドレスポンス(R1)のカードステータスがエラーでないか確認 */ + SD_SendStatus(); /* カードステータス取得コマンド発行、レスポンス(R1)待ち */ + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1)の Card Status チェック */ + } + } + SaveStatus = SDCARD_Status; /* カードステータスの保存 */ + SaveErrStatus = SDCARD_ErrStatus; /* エラーステータスの保存 */ + if(SDCARD_ErrStatus){ /* エラーステータスの確認(エラー有り?)*/ + i_sdmcErrProcess(); /* エラー時の処理 */ + } + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか確認 */ + return SDCARD_ErrStatus; + } + SDCARD_Status = SaveStatus; /* カードステータスの復帰 */ + SDCARD_ErrStatus = SaveErrStatus; /* エラーステータスの復帰 */ + + SDCARD_SectorSize = ulSave_SectorSize; /* 保存していたセクタサイズを戻す(default:512bytes)*/ + SD_SetBlockLength(SDCARD_SectorSize); /* SDカードデータ転送サイズ 512bytes 設定 */ + ulSDCARD_RestSectorCount = ulSaveRestSectorCount; /* 保存していた残りセクタサイズを戻す(TODO:いらない?)*/ + + SD_DisableClock(); /* SD-CLK Disable */ + + return SDCARD_ErrStatus; +} + +/*******************************************************************************/ +int MMCP_SetBusWidth( BOOL b4bit) +{ + u32 ulSave_SectorSize; /* セクタサイズ保存用 */ +// u16 TestData; + u16 Resid; + + SD_EnableClock(); /* SD-CLK Enable */ + +#if 0 + TestData = 0x5A;//0xA5; + + /* ブロックサイズの設定 */ + ulSave_SectorSize = SDCARD_SectorSize; /* セクタサイズの保存 */ + SDCARD_SectorSize = 4; /* 転送サイズ 1バイト */ + SD_SetBlockLength( SDCARD_SectorSize); /* SDカードデータ転送サイズ 1byte 設定 */ +#endif + /*コマンド6発行*/ + MMCP_WriteBusWidth( b4bit); + SD_AndFPGA(SD_OPTION,(~SD_OPTION_WIDTH_1BIT)); /* IPにビット幅の設定(4bit幅) */ + +#if 0 + /**/ + pSDCARD_info = NULL; + ulSDCARD_RestSectorCount = ulSDCARD_SectorCount = 1; + pSDCARD_BufferAddr = &TestData; /* データ格納バッファのアドレスを設定 */ + SDCARD_ATC0_Flag = FALSE; /* 全ATC完了フラグクリア */ + SDCARD_FPGA_Flag = FALSE; /* FPGA処理完了フラグクリア */ + SDCARD_EndFlag = FALSE; /* 転送処理完了フラグクリア */ + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスのクリア */ + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_RW_TIMEOUT); /* タイムアウト判定用タイマスタート(4000msec) */ +#endif + + /* IPのSD_SECCNTレジスタ有効化、転送セクタ数設定(自動CMD12発行のため) */ +// SD_EnableSeccnt( ulSDCARD_RestSectorCount); + + /*バステスト*/ + MMCP_BusTest( FALSE); + + /**/ + while( !SDCARD_EndFlag) { /* カードアクセス終了待ち */ + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか? */ + return SDCARD_ErrStatus; /* エラー終了 */ + } + } + + + /**/ + ulSDCARD_RestSectorCount = ulSDCARD_SectorCount = 1;/* 残りセクタサイズ、セクタカウントに1を設定 */ + pSDCARD_BufferAddr = &Resid; /* データ格納バッファのアドレスを設定 */ + SDCARD_ATC0_Flag = FALSE; /* 全ATC完了フラグクリア */ + SDCARD_FPGA_Flag = FALSE; /* FPGA処理完了フラグクリア */ + SDCARD_EndFlag = FALSE; /* 転送処理完了フラグクリア */ + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスのクリア */ + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_RW_TIMEOUT); /* タイムアウト判定用タイマスタート(4000msec) */ +#endif + + /**/ + MMCP_BusTest( TRUE); + + /**/ + while( !SDCARD_EndFlag) { /* カードアクセス終了待ち */ + if( SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか? */ + return SDCARD_ErrStatus; /* エラー終了 */ + } + } + + /*バステスト合格ならバス幅を4bitに拡張*/ +// if( TestData == (~(Resid))) { + if( Resid == 0xA5) { + SD_AndFPGA(SD_OPTION,(~SD_OPTION_WIDTH_1BIT)); /* IPにビット幅の設定(4bit幅) */ + }else{ + SD_OrFPGA(SD_OPTION,(SD_OPTION_WIDTH_1BIT)); /* IPにビット幅の設定(1bit幅) */ + } + + SDCARD_SectorSize = ulSave_SectorSize; /* 保存していたセクタサイズを設定(デフォルト 512bytesに戻す)*/ + SD_SetBlockLength( SDCARD_SectorSize); /* SDカードデータ転送サイズ 512bytes 設定 */ +#endif + return( 0); +} +/*******************************************************************************/ + + +/*---------------------------------------------------------------------------* + Name: i_sdmcCheckWP + + Description: check the write protect bit in the SD_INFO1 register. + SD_INFO1レジスタのライトプロテクトビットを調べる + (0:ライトプロテクトされている、1:ライトプロテクトされていない)。 + CTRではポート1は常に1。 + + Arguments: None + + Returns: number of sectors in the SD card which inserted. + *---------------------------------------------------------------------------*/ +static u16 i_sdmcCheckWP(void) +{ + if (SD_port_number == SDCARD_PORT0) /* ポート0のとき */ + { + if(!(SD_CheckFPGAReg(SD_INFO1,SD_INFO1_WRITEPROTECT))) { //WPフラグが立っていないか? + SDCARD_ErrStatus |= SDMC_ERR_WP; //エラーフラグのWPエラービットを立てる + }else{ //WPフラグが立っていたとき + SDCARD_ErrStatus &= ~SDMC_ERR_WP; //エラーフラグのWPエラービットを落とす + } + return SDCARD_ErrStatus; + } + else if (SD_port_number == SDCARD_PORT1) /* ポート1のとき */ + { + if(!(SD_CheckFPGAReg(EXT_WP,EXT_WP_PORT1))) { //WPフラグが立っていないか? + SDCARD_ErrStatus |= SDMC_ERR_WP; //エラーフラグのWPエラービットを立てる + }else{ //WPフラグが立っていたとき + SDCARD_ErrStatus &= ~SDMC_ERR_WP; //エラーフラグのWPエラービットを落とす + } + return SDCARD_ErrStatus; + } + return SDMC_ERR_END; //ここには来ない +} + +/*---------------------------------------------------------------------------* + Name: sdmcWriteFifo + + Description: write to card. + ラッパーのFIFOを使用してカードへの書き込み。 + + Arguments: buf : 書き込みデータが格納されているバッファのアドレス + bufsize : 書き込むサイズ(セクタ数) + offset : 書き込み開始オフセット(セクタ番号) + info : 実行結果を格納するための構造体へのアドレス + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +SDMC_ERR_CODE sdmcWriteFifo(void* buf,u32 bufsize,u32 offset,void(*func)(),SdmcResultInfo *info) +{ + SDCARDMsg SdMsg; +#if (TARGET_OS_CTR == 1) + u32 recv_dat; +#else + OSMessage recv_dat; +#endif + SDMC_ERR_CODE api_result; + + SdMsg.buf = buf; + SdMsg.bufsize = bufsize; + SdMsg.offset = offset; + SdMsg.func = func; + SdMsg.info = info; + SdMsg.operation = SD_OPERATION_WRITE_WITH_FIFO; + +#if (TARGET_OS_CTR == 1) + snd_dtq( sdmc_dtq_id, (VP_INT)&SdMsg); + + /* 返り値待ち */ + rcv_dtq( sdmc_result_dtq_id, (VP_INT*)&recv_dat); + + api_result = (SDMC_ERR_CODE)recv_dat; +#else + recv_dat = (OSMessage)&SdMsg; + OS_SendMessage( &sdmc_dtq, recv_dat, OS_MESSAGE_BLOCK); + + /* 返り値待ち */ + OS_ReceiveMessage( &sdmc_result_dtq, &recv_dat, OS_MESSAGE_BLOCK); + api_result = (SDMC_ERR_CODE)recv_dat; +#endif + + return api_result; +} + +/*---------------------------------------------------------------------------* + Name: SDCARDi_WriteFifo + + Description: write to card. + ラッパーのFIFOを使用してカードへの書き込み。 + + Arguments: buf : 書き込みデータが格納されているバッファのアドレス + bufsize : 書き込むサイズ(セクタ数) + offset : 書き込み開始オフセット(セクタ番号) + info : 実行結果を格納するための構造体へのアドレス + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +static SDMC_ERR_CODE SDCARDi_WriteFifo(void* buf,u32 bufsize,u32 offset,void(*func)(),SdmcResultInfo *info) +{ + SDMC_ERR_CODE result; + + /* FIFO割り込み禁止 */ + *(SDIF_CNT) = (*(SDIF_CNT) & (~(SDIF_CNT_FFIE | SDIF_CNT_FEIE))); + *(SDIF_FDS) = (u16)SDCARD_SectorSize; /* FIFOのデータサイズ */ + *(SDIF_FSC) = bufsize; + *(SDIF_CNT) |= SDIF_CNT_USEFIFO; /* FIFO使用フラグON */ + CC_EXT_MODE = CC_EXT_MODE_DMA; /* DMAモードON */ + + result = SDCARDi_Write( buf, bufsize, offset, func, info); + + /* FIFO無効に */ + *(SDIF_CNT) &= (~SDIF_CNT_USEFIFO); /* FIFO使用フラグOFF */ + CC_EXT_MODE = CC_EXT_MODE_PIO; /* PIOモード(DMAモードOFF) */ + + return result; +} + +/*---------------------------------------------------------------------------* + Name: sdmcWrite + + Description: write to card. + カードへの書き込み。 + + Arguments: buf : 書き込みデータが格納されているバッファのアドレス + bufsize : 書き込むサイズ(セクタ数) + offset : 書き込み開始オフセット(セクタ番号) + info : 実行結果を格納するための構造体へのアドレス + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +SDMC_ERR_CODE sdmcWrite(void* buf,u32 bufsize,u32 offset,void(*func)(),SdmcResultInfo *info) +{ + SDCARDMsg SdMsg; +#if (TARGET_OS_CTR == 1) + u32 recv_dat; +#else + OSMessage recv_dat; +#endif + SDMC_ERR_CODE api_result; + + SdMsg.buf = buf; + SdMsg.bufsize = bufsize; + SdMsg.offset = offset; + SdMsg.func = func; + SdMsg.info = info; + SdMsg.operation = SD_OPERATION_WRITE; + +#if (TARGET_OS_CTR == 1) + snd_dtq( sdmc_dtq_id, (VP_INT)&SdMsg); + + /* 返り値待ち */ + rcv_dtq( sdmc_result_dtq_id, (VP_INT*)&recv_dat); + + api_result = (SDMC_ERR_CODE)recv_dat; +#else + recv_dat = (OSMessage)&SdMsg; + OS_SendMessage( &sdmc_dtq, recv_dat, OS_MESSAGE_BLOCK); + + /* 返り値待ち */ + OS_ReceiveMessage( &sdmc_result_dtq, &recv_dat, OS_MESSAGE_BLOCK); + api_result = (SDMC_ERR_CODE)recv_dat; +#endif + + return api_result; +} + +/*---------------------------------------------------------------------------* + Name: SDCARDi_Write + + Description: write to card. + カードへの書き込み。 + + Arguments: buf : 書き込みデータが格納されているバッファのアドレス + bufsize : 書き込むサイズ(セクタ数) + offset : 書き込み開始オフセット(セクタ番号) + info : 実行結果を格納するための構造体へのアドレス + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +static SDMC_ERR_CODE SDCARDi_Write(void* buf,u32 bufsize,u32 offset,void(*func)(),SdmcResultInfo *info) +{ + s16 nRetryCount; + u32 ulResid; + SDMC_ERR_CODE SaveErrStatus; /* エラーステータス保存用 */ + u32 SaveStatus; /* カードステータス保存用 */ + + if( func != NULL){ /* コールバック関数のNullチェック */ + return SDMC_ERR_PARAM; /* コマンドパラメータエラー */ + } + +#if WP_ena + if( i_sdmcCheckWP()) { + return SDMC_ERR_WP; /*** ライトプロテクトのチェック ***/ + } +#endif + for(nRetryCount = 0;nRetryCount < SDCARD_RETRY_COUNT;nRetryCount++){ + + SD_EnableClock(); /* SD-CLK Enable */ + +/* func_SDCARD_CallBack = func; */ + pSDCARD_info = info; + ulSDCARD_RestSectorCount = ulSDCARD_SectorCount = bufsize; + pSDCARD_BufferAddr = buf; /* データ格納バッファのアドレスを設定 */ + + SDCARD_ATC0_Flag = FALSE; /* 全ATC完了フラグクリア */ + SDCARD_FPGA_Flag = FALSE; /* FPGA処理完了フラグクリア */ + SDCARD_EndFlag = FALSE; /* 転送処理完了フラグクリア */ + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスのクリア */ + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_RW_TIMEOUT); /* タイムアウト判定用タイマスタート(4000msec) */ +#endif + + /* IPのSD_SECCNTレジスタ有効化、転送セクタ数設定(自動CMD12発行のため) */ + SD_EnableSeccnt( ulSDCARD_RestSectorCount); + + /*--- ライトコマンド発行 ---*/ + if( SDCARD_SDHCFlag) { + SD_MultiWriteBlock( offset); /* ライトコマンド発行(引数:オフセット) */ + }else{ + SD_MultiWriteBlock( offset * SDCARD_SectorSize); /* ライトコマンド発行(引数:オフセット*セクタサイズ) */ + } + /*--------------------------*/ + + /**/ + while( !SDCARD_EndFlag) { /* カードアクセス終了待ち */ + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか? */ + return SDCARD_ErrStatus; /* エラー終了 */ + } + } + + /* エラーが発生していないか、タイムアウト以外のエラーの場合 */ + if(!(SDCARD_ErrStatus & SDMC_ERR_R1_STATUS)){ /* コマンドレスポンス(R1)のカードステータスがエラーでないか確認 */ + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1)の Card Status チェック */ + if(!(SDCARD_ErrStatus & SDMC_ERR_R1_STATUS)){ /* コマンドレスポンス(R1)のカードステータスがエラーでないか確認 */ + SD_SendStatus(); /* カードステータスの取得コマンド発行処理 */ + SD_CheckStatus(FALSE); /* コマンドレスポンス(R1)の Card Status チェック */ + } + } + SaveStatus = SDCARD_Status; /* カードステータスの保存 */ + SaveErrStatus = SDCARD_ErrStatus; /* エラーステータスの保存 */ + if( SDCARD_ErrStatus) { /* エラーステータスの確認(エラー有り?)*/ + i_sdmcErrProcess(); /* エラー時の処理(status取得、強制停止) */ + } + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか確認 */ + return SDCARD_ErrStatus; /* エラー終了 */ + } +#if RESID + if( SDCARD_SDFlag) { /* SDカード(MMCでない)の場合 */ + if( SDCARD_UseFifoFlag) { /*--- FIFOを使用しているときは ---*/ + *(SDIF_CNT) &= (~SDIF_CNT_USEFIFO); /* 一時的にFIFO未使用モードにする */ + CC_EXT_MODE = CC_EXT_MODE_PIO; + i_sdmcGetResid(&ulResid); /* 書き込み完了セクタ数の取得 */ + *(SDIF_CNT) |= SDIF_CNT_USEFIFO; /* FIFO使用モードに戻す */ + CC_EXT_MODE = CC_EXT_MODE_DMA; + }else{ /*--- FIFOを使用していないとき ---*/ + i_sdmcGetResid(&ulResid); /* 書き込み完了セクタ数の取得 */ + } + if(info){ + info->resid = ulResid * SDCARD_SectorSize;/*** pSDCARD_info->resid をinfo->resid に修正 柏 2000.08.31. ***/ + } + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか確認 */ + return SDCARD_ErrStatus; + } + } +#endif + SDCARD_Status = SaveStatus; /* カードステータスの復帰 */ + SDCARD_ErrStatus = SaveErrStatus; /* エラーステータスの復帰 */ +#if RESID + if( SDCARD_SDFlag) { /* SDカード(MMCでない)の場合 */ + if( bufsize != ulResid){ /* ライト済みセクタ数が正しくないか? */ + SD_SetErr( SDMC_ERR_NUM_WR_SECTORS); /* エラーフラグセット */ + } + } +#endif + if( SDCARD_ErrStatus == SDMC_NORMAL) { /* エラーステータスの確認(エラー無し?)*/ + break; + } + } + + SD_DisableClock(); /* クロック供給停止 */ + + return SDCARD_ErrStatus; +} + + +/*---------------------------------------------------------------------------* + Name: i_sdmcGetResid + + Description: get the numbers of the well written(without errors) blocks. + 書き込みが完了したセクタの数を取得する。 + + + Arguments: pResid : 書き込み完了セクタ数を返す変数へのポインタ + + Returns: 0 : success + > 0 : error + *---------------------------------------------------------------------------*/ +static u16 i_sdmcGetResid(u32 *pResid) +{ + u16 Resid[2]; + u32 ulSave_SectorSize; /* セクタサイズ保存用 */ + + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスをクリア */ + + /* ブロックサイズの設定 */ + ulSave_SectorSize = SDCARD_SectorSize; /* セクタサイズの保存 */ + SDCARD_SectorSize = 4; /* 転送サイズ 4バイト */ + SD_SetBlockLength( SDCARD_SectorSize); /* SDカードデータ転送サイズ 4byte 設定 */ + if(SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか? */ + return SDCARD_ErrStatus; + } + +#if TIMEOUT + SDCARD_TimerStart(SDCARD_RW_TIMEOUT); /* タイムアウト判定用タイマスタート(4000msec) */ +#endif + + if( SD_AppCommand()) { /* RCA設定後 CMD55発行処理 */ + return SDCARD_ErrStatus; /* エラー発生(CMD55が正常終了しない)なら戻る */ + } + +/* func_SDCARD_CallBack = NULL; */ + pSDCARD_info = NULL; + ulSDCARD_RestSectorCount = ulSDCARD_SectorCount = 1;/* 残りセクタサイズ、セクタカウントに1を設定 */ + pSDCARD_BufferAddr = Resid; /* データ格納バッファのアドレスを設定 */ + + SDCARD_ATC0_Flag = FALSE; /* 全ATC完了フラグクリア */ + SDCARD_FPGA_Flag = FALSE; /* FPGA処理完了フラグクリア */ + SDCARD_EndFlag = FALSE; /* 転送処理完了フラグクリア */ + SDCARD_ErrStatus = SDMC_NORMAL; /* エラーステータスのクリア */ + + thread_flag = TRUE; + /*--- ACMD22 ライト済みセクタ数取得コマンド発行 ---*/ + SD_SendNumWRSectors(); + /*-------------------------------------------------*/ + PRINTDEBUG( "----- Sleep Thread -----\n"); +#if (TARGET_OS_CTR == 1) +// can_wup( 0); + slp_tsk(); +#else + OS_SleepThread( NULL); +#endif + PRINTDEBUG( "waked\n"); + thread_flag = FALSE; + + while( !SDCARD_EndFlag) { /* カードアクセス終了待ち */ + if( SDCARD_ErrStatus & SDMC_ERR_FPGA_TIMEOUT){ /* タイムアウトエラーか? */ + return SDCARD_ErrStatus; /* エラー終了 */ + } + } + /* エラーが発生していないか、タイムアウト以外のエラーの場合 */ + if( SDCARD_ErrStatus) { /* (タイムアウト以外に)エラー発生か? */ + i_sdmcErrProcess(); /* エラー時の処理 */ + *pResid = 0L; /* ライト済みセクタ数に 0 を設定 */ + }else{ /* エラーが発生していない場合 */ + /* SDカードのレジスタはMSBから送られてくるため並べ替えを行う */ + Resid[1] = SD_SwapByte(&Resid[1]); /* 上位 1byte と下位 1byte を入れ替える */ + (((LELONG *)pResid)->dt2word.low) = Resid[1]; /* Resid[1]の設定 */ + Resid[0] = SD_SwapByte(&Resid[0]); /* 上位 1byte と下位 1byte を入れ替える */ + (((LELONG *)pResid)->dt2word.high) = Resid[0]; /* Resid[0]の設定 */ + } + + SDCARD_SectorSize = ulSave_SectorSize; /* 保存していたセクタサイズを設定(デフォルト 512bytesに戻す)*/ + SD_SetBlockLength( SDCARD_SectorSize); /* SDカードデータ転送サイズ 512bytes 設定 */ + /* SetBlockLengthでエラーが出たらどうする? */ + + return SDCARD_ErrStatus; +} + +/*---------------------------------------------------------------------------* + Name: SDCARD_Restore_port0 + + Description: restore registers and variables of port0. + ポート0のレジスタや変数を復帰する。 + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_Restore_port0(void) +{ + /* registers */ + SD_SetFPGA( SD_CLK_CTRL, SD_CLK_CTRL_port0); + SD_SetFPGA( SD_OPTION, SD_OPTION_port0); + + /* variables */ + SDCARD_ErrStatus = SDCARD_ErrStatus_port0; + SD_RCA = SD_RCA0; + SDCARD_MMCFlag = SDCARD_MMCFlag_port0; + SDCARD_Status = SDCARD_Status_port0; + SDCARD_SDFlag = SDCARD_SDFlag_port0; + SDCARD_OutFlag = SDCARD_OutFlag_port0; + pSDCARD_info = pSDCARD_info_port0; + SDCARD_WP_PERMANENT = SDCARD_WP_PERMANENT_port0; + SDCARD_WP_TEMPORARY = SDCARD_WP_TEMPORARY_port0; + + /* registers */ +#if (TARGET_OS_CTR == 1) + miCpuCopy8( SD_CID_port0, SD_CID, 16); + miCpuCopy8( SD_CSD_port0, SD_CSD, 16); +#else + MI_CpuCopy8( SD_CID_port0, SD_CID, 16); + MI_CpuCopy8( SD_CSD_port0, SD_CSD, 16); +#endif +} + +/*---------------------------------------------------------------------------* + Name: SDCARD_Restore_port1 + + Description: restore registers and variables of port0. + ポート1のレジスタや変数を復帰する。 + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_Restore_port1(void) +{ + /*registers*/ + SD_SetFPGA( SD_CLK_CTRL, SD_CLK_CTRL_port1); + SD_SetFPGA( SD_OPTION, SD_OPTION_port1); + + /*variables*/ + SDCARD_ErrStatus = SDCARD_ErrStatus_port1; + SD_RCA = SD_RCA1; + SDCARD_MMCFlag = SDCARD_MMCFlag_port1; + SDCARD_Status = SDCARD_Status_port1; + SDCARD_SDFlag = SDCARD_SDFlag_port1; + SDCARD_OutFlag = SDCARD_OutFlag_port1; + pSDCARD_info = pSDCARD_info_port1; + SDCARD_WP_PERMANENT = SDCARD_WP_PERMANENT_port1; + SDCARD_WP_TEMPORARY = SDCARD_WP_TEMPORARY_port1; + + /*registers*/ +#if (TARGET_OS_CTR == 1) + miCpuCopy8( SD_CID_port1, SD_CID, 16); + miCpuCopy8( SD_CSD_port1, SD_CSD, 16); +#else + MI_CpuCopy8( SD_CID_port1, SD_CID, 16); + MI_CpuCopy8( SD_CSD_port1, SD_CSD, 16); +#endif +} + + +/*---------------------------------------------------------------------------* + Name: i_sdmcSelectedNo + + Description: get selected port number. + 選択されているポート番号を取得する + + Arguments: None + + Returns: [15:8]port numbers which supported(サポートされているポート数) + [7:0]port number which selected now(選択されているポート番号) + *---------------------------------------------------------------------------*/ +static u16 i_sdmcSelectedNo(void) +{ + u16 i_sdmcSelect_Value; + + SD_GetFPGA(i_sdmcSelect_Value,SD_PORTSEL); /* SD_PORTSELレジスタ値を取得 */ + + return i_sdmcSelect_Value; +} + +/*---------------------------------------------------------------------------* + Name: i_sdmcSelect + + Description: select port. + ポートを選択する + + Arguments: select : [15:8]port numbers which supported(サポートされているポート数) + [7:0]port number which selected now(選択するポート番号) + Returns: 0 : success + >0 : error + *---------------------------------------------------------------------------*/ +static u16 i_sdmcSelect(u16 select) +{ + union + { + u16 Val; + struct + { + u8 L; /* 指定したいポート */ + u8 H; /* サポートされているポート */ + } PSel; + } SDCARD_PSel; + + //TODO : transferモードからstand-byモードにする? +/* if( !SDCARD_EndFlag) { // 転送が残っている場合はエラー + return SDMC_ERR_END; + } +*/ + SDCARD_PSel.Val = select; + if ((SDCARD_PSel.PSel.H > SDCARD_PORT_NO_MAX) + | (SDCARD_PSel.PSel.H < SDCARD_PORT_NO_MIN) + | (SDCARD_PSel.PSel.L > SDCARD_PORT_SELECT_NO)) + { + return SDMC_ERR_PARAM; /* コマンドパラメータエラー */ + }else{ + SD_port_en_numbers = SDCARD_PSel.PSel.H; /* 新しくサポートするポート数を保存 */ + SD_port_number = SDCARD_PSel.PSel.L; /* 新しく選択するポート番号を保存 */ + + SD_GetFPGA(SDCARD_PSel.Val,SD_PORTSEL); /* 現在のレジスタを取得 */ + + if (SDCARD_PSel.PSel.L == 0) /* 現在選択されているポートが 0 ? */ + { + SDCARD_Backup_port0(); + }else{ + SDCARD_Backup_port1(); + } + + SDCARD_PSel.Val= select; + + if (SDCARD_PSel.PSel.L == 0) /* 新しく選択されたポートが 0 ? */ + { + SDCARD_Restore_port0(); + }else{ + SDCARD_Restore_port1(); + } + + SD_SetFPGA(SD_PORTSEL,select); /* レジスタへセット */ + return SDMC_NORMAL; /* 0 リターン */ + } +} + + +/*---------------------------------------------------------------------------* + Name: SDCARD_Thread + + Description: SDメモリカードメインタスク + + Arguments: + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_Thread( void* arg) +{ + SDCARDMsg* SdMsg; +#if (TARGET_OS_CTR == 1) + u32 current_dat; +#else + OSMessage current_dat; +#endif + SDMC_ERR_CODE api_result; + + while( TRUE) { + /* メッセージ待ち */ +#if (TARGET_OS_CTR == 1) + rcv_dtq( sdmc_dtq_id, (VP_INT*)¤t_dat); +#else + OS_ReceiveMessage( &sdmc_dtq, ¤t_dat, OS_MESSAGE_BLOCK); +#endif + SdMsg = (SDCARDMsg*)current_dat; + PRINTDEBUG( "sd task : receive command : %d\n", SdMsg->operation); + + switch( SdMsg->operation) { + case SD_OPERATION_INIT: + api_result = i_sdmcInit(); + break; + case SD_OPERATION_READ: + PRINTDEBUG( "from:0x%x, sectors:0x%x\n", SdMsg->offset, SdMsg->bufsize); + api_result = SDCARDi_Read( SdMsg->buf, SdMsg->bufsize, + SdMsg->offset, SdMsg->func, + SdMsg->info); + break; + case SD_OPERATION_READ_WITH_FIFO: + PRINTDEBUG( "from:0x%x, sectors:0x%x\n", SdMsg->offset, SdMsg->bufsize); + api_result = SDCARDi_ReadFifo( SdMsg->buf, SdMsg->bufsize, + SdMsg->offset, SdMsg->func, + SdMsg->info); + break; + case SD_OPERATION_WRITE: + PRINTDEBUG( "from:0x%x, sectors:0x%x\n", SdMsg->offset, SdMsg->bufsize); + api_result = SDCARDi_Write( SdMsg->buf, SdMsg->bufsize, + SdMsg->offset, SdMsg->func, + SdMsg->info); + break; + case SD_OPERATION_WRITE_WITH_FIFO: + PRINTDEBUG( "from:0x%x, sectors:0x%x\n", SdMsg->offset, SdMsg->bufsize); + api_result = SDCARDi_WriteFifo( SdMsg->buf, SdMsg->bufsize, + SdMsg->offset, SdMsg->func, + SdMsg->info); + break; + default: + PRINTDEBUG( "sdmc-thread error : undefined command.\n"); + api_result = SDMC_ERR_COMMAND; + break; + } + + PRINTDEBUG( "sd task : operation ends(result : 0x%x), send message begin\n", api_result); + + /*メッセージ返送*/ + current_dat = (OSMessage)api_result; +#if (TARGET_OS_CTR == 1) + snd_dtq( sdmc_result_dtq_id, (VP_INT)api_result); +#else + OS_SendMessage( &sdmc_result_dtq, current_dat, OS_MESSAGE_BLOCK); +#endif + } +} + +/*---------------------------------------------------------------------------* + Name: SDCARD_Intr_Thread + + Description: SDメモリカード割り込み処理タスク + + Arguments: + + Returns: None + *---------------------------------------------------------------------------*/ +static void SDCARD_Intr_Thread( void* arg) +{ + u16 sd_info1;//, sd_info2; + + while( 1) { +#if (TARGET_OS_CTR == 1) + slp_tsk(); +#else + OS_SleepThread( NULL); +#endif + + PRINTDEBUG( "sdmc interrupt handler : \n"); + /*SD割り込みのIF解除*/ + *(vu32*)CTR_INT_IF = CTR_IE_SD_MASK; + + /*--- FIFOを使うとき ---*/ + if( SDCARD_UseFifoFlag) { + sd_info1 = SD_INFO1; + if( ((*SDIF_CNT & SDIF_CNT_FULL)&&(*SDIF_CNT & SDIF_CNT_FFIE)) || + ((!(*SDIF_CNT & SDIF_CNT_NEMP))&&(*SDIF_CNT & SDIF_CNT_FEIE))) { + + PRINTDEBUG( ">>>>> SD Interrupt (FIFO Full or Empty)\n"); +#if (TARGET_OS_CTR == 1) + osDisableInterruptID( OS_INTR_ID_SD); +#else + OS_DisableIrqMask( OS_IE_SD); +#endif + SDCARD_FPGA_irq(); /*カードからのリードライト要求割り込み*/ +#if (TARGET_OS_CTR == 1) + osEnableInterruptID( OS_INTR_ID_SD); +#else + OS_EnableIrqMask( OS_IE_SD); +#endif + /* FIFO割り込みとALLEND割り込みがほぼ同時の場合に対応 */ + if( SD_CheckFPGAReg( sd_info1, SD_INFO1_ALL_END)) { +#if (TARGET_OS_CTR == 1) + osDisableInterruptID( OS_INTR_ID_SD); +#else + OS_DisableIrqMask( OS_IE_SD); +#endif + SYSFPGA_irq(); +#if (TARGET_OS_CTR == 1) + osEnableInterruptID( OS_INTR_ID_SD); +#else + OS_EnableIrqMask( OS_IE_SD); +#endif + if( thread_flag) { + PRINTDEBUG( "----- Wakeup Thread! -----\n"); +#if (TARGET_OS_CTR == 1) + wup_tsk( sdmc_tsk_id); + PRINTDEBUG( "id : 0x%x\n", sdmc_tsk_id); +#else + OS_WakeupThreadDirect( &sdmc_tsk); +#endif + } + } + }else{ + if( SD_CheckFPGAReg( SD_INFO2, (SD_INFO2_MASK_BRE | SD_INFO2_MASK_BWE))) { + PRINTDEBUG( ">>>>> SD Interrupt (R/W Request from CARD)\n"); + //ここで自動的にラッパーのFIFO<->SD_BUF0間で通信が行われる + // if((!(*SDIF_CNT & SDIF_CNT_NEMP))&&(*SDIF_CNT & SDIF_CNT_FEIE)) { +#if (TARGET_OS_CTR == 1) + osDisableInterruptID( OS_INTR_ID_SD); +#else + OS_DisableIrqMask( OS_IE_SD); +#endif + SDCARD_FPGA_irq(); +#if (TARGET_OS_CTR == 1) + osEnableInterruptID( OS_INTR_ID_SD); +#else + OS_EnableIrqMask( OS_IE_SD); +#endif + }else{ + PRINTDEBUG( ">>>>> SD Interrupt (End or Err)\n"); +#if (TARGET_OS_CTR == 1) + osDisableInterruptID( OS_INTR_ID_SD); +#else + OS_DisableIrqMask( OS_IE_SD); +#endif + SYSFPGA_irq(); /*完了またはエラー割り込み*/ +#if (TARGET_OS_CTR == 1) + osEnableInterruptID( OS_INTR_ID_SD); +#else + OS_EnableIrqMask( OS_IE_SD); +#endif + /**/ + if( thread_flag) { + PRINTDEBUG( "----- Wakeup Thread -----\n"); +#if (TARGET_OS_CTR == 1) + wup_tsk( sdmc_tsk_id); + PRINTDEBUG( "id : 0x%x\n", sdmc_tsk_id); +#else + OS_WakeupThreadDirect( &sdmc_tsk); +#endif + } + } + } + /*--- FIFOを使わないとき ---*/ + }else{ + if( SD_CheckFPGAReg( SD_INFO2, (SD_INFO2_MASK_BRE | SD_INFO2_MASK_BWE))) { + PRINTDEBUG( ">>>>> SD Interrupt (R/W Request from CARD)\n"); +#if (TARGET_OS_CTR == 1) + osDisableInterruptID( OS_INTR_ID_SD); +#else + OS_DisableIrqMask( OS_IE_SD); +#endif + SDCARD_FPGA_irq(); /*カードからのリードライト要求割り込み*/ +#if (TARGET_OS_CTR == 1) + osEnableInterruptID( OS_INTR_ID_SD); +#else + OS_EnableIrqMask( OS_IE_SD); +#endif + }else{ + PRINTDEBUG( ">>>>> SD Interrupt (End or Err)\n"); +#if (TARGET_OS_CTR == 1) + osDisableInterruptID( OS_INTR_ID_SD); +#else + OS_DisableIrqMask( OS_IE_SD); +#endif + SYSFPGA_irq(); /*完了またはエラー割り込み*/ +#if (TARGET_OS_CTR == 1) + osEnableInterruptID( OS_INTR_ID_SD); +#else + OS_EnableIrqMask( OS_IE_SD); +#endif + /**/ + if( thread_flag) { + PRINTDEBUG( "----- Wakeup Thread -----\n"); +#if (TARGET_OS_CTR == 1) + wup_tsk( sdmc_tsk_id); + PRINTDEBUG( "id : 0x%x\n", sdmc_tsk_id); +#else + OS_WakeupThreadDirect( &sdmc_tsk); +#endif + } + } + } + } +} diff --git a/build/libraries/devices/sdmc/ARM7/sdmc_config.h b/build/libraries/devices/sdmc/ARM7/sdmc_config.h new file mode 100644 index 0000000..2530111 --- /dev/null +++ b/build/libraries/devices/sdmc/ARM7/sdmc_config.h @@ -0,0 +1,85 @@ +/* +** Copyright (c) 2000-2001 Matsushita Electric Industrial Co., Ltd. +** All Rights Reserved. +*/ + +/* +** $System IP1.1 without C2 サンプルソフト +** $Subsystem カードドライバ +** $Filename CARDDRV.H +** $Version 1.0 版 +** $Date 01/02/16 +** $Log 01/02/16 rev1.0作成 +** 松下電器産業(株)半導体開発本部 +*/ + + +#ifndef __SDMC_CONFIG_H__ +#define __SDMC_CONFIG_H__ + + +#include + +#ifdef USE_OS +#include /* IP 対応レジスタ定義 */ +#endif + + + +/********************************************* + ターゲットOS +*********************************************/ +#define TARGET_OS_CTR (0) +#define TARGET_OS_NITRO (TARGET_OS_CTR ^ 1) + + +/********************************************* + SDドライバ コンフィグレーション +*********************************************/ +#define SD_DEBUG_PRINT_ON 0 /* デバッグ表示 */ + +#define WP_ena 1 /* ライトプロテクトのチェック有効 */ +#define TIMEOUT 1 /* FPGA Timeout none = FALSE */ +#define SCR 1 /* Send SCR Command = TRUE */ +#define RESID 1 /* Write Error Resid enable = TRUE */ +#define ATC_ON 0 /* ATC転送 使用/未使用 */ + +#define SecEnable 1 /* SD_SECCNTレジスタ Enable */ +#define SecDisenable 0 /* SD_SECCNTレジスタ Disable */ +//#define STANDBYMODE 0x04 /*** 5772 standby control bit ***/ + + +/********************************************* + タイムアウト設定値(ms単位) +*********************************************/ +#define SDCARD_RW_TIMEOUT (4000) +#define SDCARD_STDBY_TIMEOUT (50) +#define SDCARD_CLOCK_WAIT (500) +#define SDCARD_SDCLK_WAIT (10) +#define SDCARD_INITIAL_TIMEOUT (800) +#define SDCARD_RESET_TIMEOUT (1500) +#define SDCARD_ERASE_TIMEOUT (1) +#define SDCARD_ERRPROC_TIMEOUT (2000) + + +/********************************************* + リトライ回数(Multiple Block R/W のとき) +*********************************************/ +#define SDCARD_RETRY_COUNT (3) + +/********************************************* + その他 +*********************************************/ +#define SECTOR_SIZE (512) /* 1セクタのバイト数 */ +#define SECTOR_MAX (255) /* SYSFPGA アクセス最大セクタ数 */ + +/*--- 上位レイヤに返すステータス値(SDCARD_Getstatus参照)用 ---*/ +#define SDCARD_FLAG_CLR (0x3FFF) /* カード判定部分クリア用 */ +#define SDCARD_FLAG_SD (0x8000) /* カード判定部分SDカード */ +#define SDCARD_FLAG_MMC (0x4000) /* カード判定部分MMCカード */ +#define SDCARD_PORT1_CLR (0x0007) /* カードポート1判定部分クリア用 */ + + + + +#endif /*__SDMC_CONFIG_H__*/ diff --git a/build/libraries/devices/sdmc/Makefile b/build/libraries/devices/sdmc/Makefile new file mode 100644 index 0000000..4f69ad1 --- /dev/null +++ b/build/libraries/devices/sdmc/Makefile @@ -0,0 +1,34 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - libraries - spi +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + + +#---------------------------------------------------------------------------- + +# SUBDIRS = ARM9 + +ifdef TWL_WITH_ARM7 +SUBDIRS += ARM7 +endif + +#---------------------------------------------------------------------------- + +include $(TWLSDK_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/build/libraries/fatfs/ARM7/Makefile b/build/libraries/fatfs/ARM7/Makefile new file mode 100644 index 0000000..9f6a71e --- /dev/null +++ b/build/libraries/fatfs/ARM7/Makefile @@ -0,0 +1,74 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - libraries - mi/ARM7 +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +SUBDIRS = + + +#---------------------------------------------------------------------------- + +# build ARM & THUMB libraries +TWL_CODEGEN_ALL ?= True + +# Codegen for sub processer +TWL_PROC = ARM7 + +SRCDIR = ../common/src src + +INCDIR = ../common +INCDIR += . \ + $(TWLSDK_ROOT)/include/twl/ARM7 \ + $(TWLSDK_ROOT)/include/twl/fatfs/ARM7 \ + +SRCS = apistat.c prfsapi.c rtlowl.c apickdsk.c apiwrite.c \ + prfscore.c rtnvfat.c apicnfig.c \ + prfsnvio.c rttermin.c apideltr.c appdemo.c prfstest.c \ + rtutbyte.c apienum.c csascii.c rtutil.c \ + apifilio.c csjis.c rtdevio.c rtvfat.c apifilmv.c csjistab.c \ + rtdrobj.c apifrmat.c csstrtab.c rtfat16.c apigetwd.c \ + rtfat32.c apigfrst.c csunicod.c rtfatxx.c apiinfo.c \ + portio.c apiinit.c portkern.c \ + apimkdir.c apirealt.c \ + prapipro.c apiregrs.c prblock.c apisetwd.c \ + rtkernfn.c \ + drdefault.c drfile.c attach.c \ + +TARGET_LIB = libfatfs_sp$(TWL_LIBSUFFIX).a + + +#---------------------------------------------------------------------------- + +# DEBUG版ビルドの場合、RELEASE版でビルドして +# DEBUG版のライブラリを装います。 + +ifdef NITRO_DEBUG +NITRO_BUILD_TYPE = RELEASE +endif + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + +INSTALL_TARGETS = $(TARGETS) +INSTALL_DIR = $(TWL_INSTALL_LIBDIR) + + +#---------------------------------------------------------------------------- + +do-build: $(TARGETS) + +include $(TWLSDK_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/build/libraries/fatfs/ARM7/apickdsk.c b/build/libraries/fatfs/ARM7/apickdsk.c new file mode 100644 index 0000000..c5bf6b4 --- /dev/null +++ b/build/libraries/fatfs/ARM7/apickdsk.c @@ -0,0 +1,1346 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright Peter Van Oudenaren , 1993 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/**************************************************************************** +CHKDSK.C - Check Files System Integrity + + Summary + + CHKDSK + + Description + This program scans the disk searching for crosslinked chains and + and lost clusters. If the -f argument is present it converts lost + chains to FILE???.CHK in the root, and file sizes are adjusted if + they are not correct. + + If -v is specified every file on the disk is listed. If crossed cluster + chains are found the files/directories containing them are printed. + + NOTE: This program is portable except for calls to exit. + + Bugs: + This routine does not recognize bad directories as well as it could. + While travesring directories it calls pc_fndnode(). This in itself is + good but we have to add validation code to fndnode and makenode (soon). + + Returns: + + Example: + + +****************************************************************************/ +/* Tips for usage: + + A. First run chkdsk without the save lost clusters option. + B. If there are any crossed files make copies of them. Chances are all + copies will end up fine since copy will copy only file_size bytes. + Delete the original files. + C. If there are crossed chains in a directory first recreate the + directory in another subdirectory. Then delete all files + and subdirectories inside the bad subdirectory. If the dir is + crossed with a file treat the file as in part B. + D. You will probably not be able to delete the bad directory + because it is non-empty. There is no API call similar to the + icheck -p call in unix. A very slightly modified version of + pc_unlink() could provide this service (do not check for + ADIRENT). + E. If any files are reported to have the wrong size make a copy of them. + F. run chkdsk with save lost clusters on to fix up the file system. + +*/ + +#include + +/* 10-24-2000 added LBA formatting. Submit to main tree */ + +typedef CLUSTERTYPE CLTYPE; + +#define PATHSIZE 256 + + +/************************************************************************ +* * +* Function prototypes * +* * +************************************************************************/ + +void app_entry(void); +void print_crossed_files(void); +BOOLEAN allocate_chkdsk_core(void); +void free_chkdsk_core(void); +BOOLEAN write_lost_chains(void); +BOOLEAN build_chk_file(long bad_chain_no, long current_file_no, long *ret_file_no); +BOOLEAN scan_all_files(byte *dir_name); +BOOLEAN process_used_map(DROBJ *pobj, byte *filename); +BOOLEAN scan_crossed_files(byte *dir_name); +BOOLEAN process_crossed_file(DROBJ *pobj, byte *filename); +BOOLEAN add_cluster_to_lost_list(DDRIVE *pdr , CLTYPE cluster); +BOOLEAN check_lost_clusters(DDRIVE *pdr); +dword count_lost_clusters(DDRIVE *pdr); +BOOLEAN add_cluster_to_crossed(CLTYPE cluster); +CLTYPE chain_size(CLTYPE cluster); +void clr_bit(byte *bitmap, dword index); +byte get_bit(byte *bitmap, dword index); +void set_bit(byte *bitmap, dword index); + +/* Provided in the vfat soecific code section */ +dword scan_for_bad_lfns(DROBJ *pmom, int delete_bad_lfn); + +/************************************************************************ +* * +* Programmer modifiable constants * +* * +************************************************************************/ + +#define NCROSSED_ALLOWED 50 +/* For controlling how much to alloc. You may change it . */ + +#define NLOST_ALLOWED 50 +/* For controlling how much to alloc. You may change it . */ + +#define MAX_RECURSION_DEPTH 16 /* 8 */ +/* For controlling how deep you will allow the directory traversal to +go. This guards against stack overflow in systems with deeply nested +subdirectories. Each recursion chews up around 160 bytes if EMAXPATH_BYTES +is 145. Less if EMAXPATH_BYTES is smaller. */ + +#define CL_WINDOW_SIZE 0x10000L +/* This is the size of the largest number of clusters that can be +scanned for crossed and lost chains on a single pass: +(# of passes) = (# of clusters)/CL_WINDOW_SIZE */ + +#define CL_BITMAP_SIZE (int) ((CL_WINDOW_SIZE+7)/8) +/* Size of the bitmap needed to process CL_WINDOW_SIZE clusters on a +single pass */ + +/************************************************************************ +* * +* CHKDSK data structures * +* * +************************************************************************/ + +typedef struct crossed_file { + byte file_name[EMAXPATH_BYTES]; + struct crossed_file *pnext; +} CROSSED_FILE; + +typedef struct crossing_point { + CLTYPE cluster; + struct crossed_file *plist; +} CROSSING_POINT; + +/* The program uses a lot of global data. To keep it manageable we put +it all in one big structure. */ + +typedef struct chk_global { + int be_verbose; /* BE_VERBOSE */ + int fix_problems; + int write_chains; /* if 1 create chk files otherwise delete */ + DDRIVE *drive_structure; /* The drive we are working on */ + dword n_user_files; /* Total #user files found */ + dword n_hidden_files; /* Total #hidden files found */ + dword n_user_directories; /* Total #directories found */ + CLTYPE n_free_clusters; /* # free available clusters */ + CLTYPE n_bad_clusters; /* # clusters marked bad */ + CLTYPE n_file_clusters; /* Clusters in non hidden files */ + CLTYPE n_hidden_clusters; /* Clusters in hidden files */ + CLTYPE n_dir_clusters; /* Clusters in directories */ + CROSSED_FILE *crossed_file_freelist; /* Freelist of crossed file structures */ + CROSSING_POINT crossed_points[NCROSSED_ALLOWED]; /* Array: containing cluster number and a list of + files crossed at that cluster. */ + dword n_crossed_points; /* Number of crossed chains. */ + CLTYPE lost_chain_list[NLOST_ALLOWED]; /* Array listheads of lost chains */ + dword n_lost_chains; /* # lost chains */ + dword n_lost_clusters; /* # lost clusters */ + byte bm_used[CL_BITMAP_SIZE]; /* Bitmap of all clusters used + by directories/files */ + /* Use a global buffer so we do not load the stack up during recursion */ + byte gl_file_name[26]; + byte gl_file_path[EMAXPATH_BYTES]; + int recursion_depth; /* how deep in the stack we are */ + dword n_bad_lfns; /* # corrupt/disjoint win95 lfn chains */ + + /* These fields bound the cluster map processing so we can process + disks with very large FATs by making multiple passes */ + CLTYPE cl_start; + CLTYPE cl_end; + int on_first_pass; +} CHK_GLOBAL; + + +/************************************************************************ +* * +* CHKDSK parameters - set parameters here if no console input is * +* available * +* * +************************************************************************/ + +#define VERBOSE TRUE +/* Set to TRUE for verbose mode; FALSE for silent mode */ + + + +void print_chkdsk_statistics(CHK_GLOBAL *pgl); +void print_chkdsk_crossed_files(CHK_GLOBAL *pgl); + +/************************************************************************ +* * +* GLOBAL DATA * +* * +************************************************************************/ + +/* This is where all the globals are kept. */ +CHK_GLOBAL KS_FAR gl; +CROSSED_FILE KS_FAR crossed_file_core[NCROSSED_ALLOWED]; /* Base of the list */ + + +/************************************************************************ +* * +* MAIN PROGRAM * +* * +************************************************************************/ + + +BOOLEAN pc_check_disk(byte *drive_id, CHKDISK_STATS *pstat, int verbose, int fix_problems, int write_chains) /* __apifn__*/ +{ + int drive_number,i; + byte str_slash[8]; + BOOLEAN ret_val; + byte *p; + + ret_val = FALSE; + + rtfs_memset((byte *)&gl, 0, sizeof(gl)); + gl.fix_problems = fix_problems; + gl.write_chains = write_chains; + gl.be_verbose = verbose; + + /* Initialize filesystem memory */ + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + /* Now make \\ in native char set */ + p = &str_slash[0]; + CS_OP_ASSIGN_ASCII(p,'\\'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + + /* Mount the disk */ + drive_number = (int) check_drive_name_mount(drive_id); + if (drive_number < 0) + { + goto ex_it; + } + /* Release the lock. chkdsk is not atomic */ + release_drive_mount(drive_number); /* Release lock, unmount if aborted */ + + gl.drive_structure = pc_drno2dr(drive_number); + if (!pc_set_default_drive(drive_id)) + { + goto ex_it; + } + + /* Allocate the bit maps and data structures we will need we pass in + the size of the fat so we know how many bits we will need to allocate + in our used bitmap. */ + if (!allocate_chkdsk_core()) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_02,PRFLG_NL);} /* "Failed Allocating Core To Run" */ + goto ex_it; + } + + gl.on_first_pass = 1; + gl.cl_start = 2; + gl.cl_end = CL_WINDOW_SIZE + 2; + if (gl.cl_end > gl.drive_structure->maxfindex+1) + gl.cl_end = (CLTYPE) (gl.drive_structure->maxfindex+1); + + while (gl.cl_start < gl.drive_structure->maxfindex) + { + gl.n_user_files = 0; + gl.n_hidden_files = 0; + gl.n_user_directories = 0; + + /* Build a used map plus get statistics on cluster usage */ + if (!scan_all_files(str_slash)) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_03,PRFLG_NL);} /* "Failed Scanning Disk Files" */ + goto ex_it; + } + + /* Now check if any allocated clusters are unaccounted for */ + if (!check_lost_clusters(gl.drive_structure)) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_04,PRFLG_NL);} /* "Failed Scanning Fat" */ + goto ex_it; + } + + /* Advance the active cluster window */ + gl.cl_start = gl.cl_end; + gl.cl_end += CL_WINDOW_SIZE; + if (gl.cl_end > gl.drive_structure->maxfindex+1) + gl.cl_end = (CLTYPE) (gl.drive_structure->maxfindex+1); + gl.on_first_pass = 0; + + /* Clear the used cluster table */ + rtfs_memset((byte *)gl.bm_used,0,CL_BITMAP_SIZE); + } + + + /* If there are lost chains count the clusters inside. */ + if (gl.n_lost_chains) + count_lost_clusters(gl.drive_structure); + + /* Now recover lost chains into .CHK files */ + if (gl.fix_problems && gl.n_lost_chains) + { + if (gl.write_chains) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_05,PRFLG_NL);} /* " Creating .CHK Files" */ + if (!write_lost_chains()) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_06,PRFLG_NL);} /* " Failed Creating .CHK Files" */ + goto ex_it; + } + } + else + { + for (i = 0; i < (long) gl.n_lost_chains; i++) + { + if (!FATOP(gl.drive_structure)->fatop_freechain(gl.drive_structure, gl.lost_chain_list[i], 0, 0xffffffff)) + goto ex_it; + } + } + } + + /* If there are crossed chains we have to rescan the whole file + system looking for the files that contain he crossed chains. */ + if (gl.n_crossed_points) + { + if (!scan_crossed_files(str_slash)) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_08,PRFLG_NL);} /* "Failed Scanning Crossed Files" */ + goto ex_it; + } + } + + /* Now print the statistics */ + if (gl.be_verbose) + print_chkdsk_statistics(&gl); + + /* print the names of files with crossed chains. */ + if (gl.n_crossed_points) + { + if (gl.be_verbose) + { + RTFS_PRINT_STRING_1(USTRING_CHKDSK_09,PRFLG_NL); /* " Crossed Chains Were Found" */ + print_chkdsk_crossed_files(&gl); + } + } + ret_val = TRUE; + pstat->n_user_files = gl.n_user_files; + pstat->n_hidden_files = gl.n_hidden_files; + pstat->n_user_directories = gl.n_user_directories; + pstat->n_free_clusters = gl.n_free_clusters; + pstat->n_bad_clusters = gl.n_bad_clusters; + pstat->n_file_clusters = gl.n_file_clusters; + pstat->n_hidden_clusters = gl.n_hidden_clusters; + pstat->n_dir_clusters = gl.n_dir_clusters; + pstat->n_crossed_points = gl.n_crossed_points; + pstat->n_lost_chains = gl.n_lost_chains; + pstat->n_lost_clusters = gl.n_lost_clusters; + pstat->n_bad_lfns = gl.n_bad_lfns; +ex_it: + return (ret_val); +} + +/************************************************************************ +* * +* User I/O functions * +* * +************************************************************************/ +void print_chkdsk_statistics(CHK_GLOBAL *pgl) +{ + dword ltemp; + if (!gl.be_verbose) + return; + + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1 ((dword)pgl->n_user_files, 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_11,0); /* " user files in " */ + RTFS_PRINT_LONG_1 ((dword)pgl->n_user_directories, PRFLG_NL); + + ltemp = pgl->drive_structure->numsecs; + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1 ((dword)(ltemp/2), 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_13, PRFLG_NL); /* " KBytes total disk space" */ + + ltemp = (dword) pgl->n_hidden_clusters; + ltemp *= (dword) pgl->drive_structure->secpalloc; + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1 ((dword)(ltemp/2), 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_15, 0); /* " KBytes in " */ + RTFS_PRINT_LONG_1 ((dword)pgl->n_hidden_files, 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_16, PRFLG_NL); /* " hidden files" */ + + + + ltemp = (dword) pgl->n_dir_clusters; + ltemp *= (dword) pgl->drive_structure->secpalloc; + ltemp += (dword) pgl->drive_structure->secproot; + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1 ((dword)(ltemp/2), 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_18, 0); /* " KBytes in " */ + RTFS_PRINT_LONG_1 ((dword)pgl->n_user_directories, 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_19, PRFLG_NL); /* " directories" */ + + ltemp = (dword) pgl->n_file_clusters; + ltemp *= (dword) pgl->drive_structure->secpalloc; + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1 ((dword)(ltemp/2), 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_21, 0); /* " KBytes in " */ + RTFS_PRINT_LONG_1 ((dword)pgl->n_user_files, 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_22, PRFLG_NL); /* " user files" */ + + ltemp = (dword) pgl->n_bad_clusters; + ltemp *= (dword) pgl->drive_structure->secpalloc; + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1 ((dword)(ltemp/2), 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_24, PRFLG_NL); /* " KBytes in bad sectors" */ + + ltemp = (dword) pgl->n_free_clusters; + ltemp *= (dword) pgl->drive_structure->secpalloc; + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1 ((dword)(ltemp), 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_26, PRFLG_NL); /* " Free sectors available on disk" */ + + ltemp = (dword) pgl->drive_structure->secpalloc * 512; + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, PRFLG_NL); /* " " */ + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1 ((dword)ltemp, 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_29, PRFLG_NL); /* " Bytes Per Allocation Unit" */ + + ltemp = (dword)(pgl->drive_structure->maxfindex - 1); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_30, PRFLG_NL); /* "" */ + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1 ((dword)ltemp, 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_32, PRFLG_NL); /* " Total Allocation Units On Disk" */ + + RTFS_PRINT_STRING_1(USTRING_SYS_NULL, PRFLG_NL); /* "" */ + if (pgl->n_lost_chains) + { + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1((dword)pgl->n_lost_clusters, 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_35, 0); /* " lost clusters found in " */ + RTFS_PRINT_LONG_1((dword)pgl->n_lost_chains, 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_36, PRFLG_NL); /* " lost chains" */ + } + + if (pgl->n_bad_lfns) + { + RTFS_PRINT_STRING_1(USTRING_SYS_TAB, 0); /* " " */ + RTFS_PRINT_LONG_1((dword)pgl->n_bad_lfns, 0); + RTFS_PRINT_STRING_1(USTRING_CHKDSK_38, 0); /* " bad long file name chains found" */ + if (pgl->fix_problems) + RTFS_PRINT_STRING_1(USTRING_CHKDSK_39, PRFLG_NL); /* " and deleted" */ + else + RTFS_PRINT_STRING_1(USTRING_CHKDSK_40, PRFLG_NL); /* " that were not deleted" */ + } +} + +/* Print the names of all files that we were able to determine were crossed */ +void print_chkdsk_crossed_files(CHK_GLOBAL *pgl) /*__fn__*/ +{ + int i; + CROSSED_FILE *pcross; + int n_printed; + + for (i = 0; i < (int) pgl->n_crossed_points; i++) + { + if (gl.be_verbose) + { + RTFS_PRINT_STRING_1(USTRING_CHKDSK_41, 0); /* " Chains Crossed at Cluster" */ + RTFS_PRINT_LONG_1((dword) pgl->crossed_points[i].cluster, PRFLG_NL); + } + pcross = pgl->crossed_points[i].plist; + n_printed = 0; + while (pcross) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_42, pcross->file_name, PRFLG_NL);} /* " " */ + pcross = pcross->pnext; + n_printed += 1; + } + if (gl.be_verbose) + { + if (!n_printed) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_43, PRFLG_NL);} /* " Lost Chains" */ + else if (n_printed == 1) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_44, PRFLG_NL);} /* " A Lost Chain" */ + } + } +} + + +/************************************************************************ +* * +* Init/exit functions * +* * +************************************************************************/ + +/* Allocate core for bitmaps, lost chains and crossed files. */ +BOOLEAN allocate_chkdsk_core(void) /*__fn__*/ +{ + long i; + + gl.recursion_depth = 0; + + rtfs_memset((byte *)gl.bm_used, 0, CL_BITMAP_SIZE); + rtfs_memset((byte *)gl.crossed_points, 0, NCROSSED_ALLOWED*sizeof(CROSSING_POINT)); + rtfs_memset((byte *)gl.lost_chain_list, 0, NLOST_ALLOWED * sizeof(CLTYPE)); + /* Build a freelist we can use for crossed files */ + gl.crossed_file_freelist = crossed_file_core; + for (i = 0; i < NCROSSED_ALLOWED-1; i++) + { + gl.crossed_file_freelist->pnext = gl.crossed_file_freelist+1; + gl.crossed_file_freelist++; + } + gl.crossed_file_freelist->pnext = 0; + gl.crossed_file_freelist = crossed_file_core; + return(TRUE); +} + +void free_chkdsk_core() /*__fn__*/ +{ +} + + +/************************************************************************ +* * +* Functions for saving lost chains to CHK files * +* * +************************************************************************/ + +/* Create a FILEXXXX.CHK file for each found lost chain */ +BOOLEAN write_lost_chains() /*__fn__*/ +{ + long current_chk_file; + long i; + current_chk_file = 0; + for (i = 0; i < (long) gl.n_lost_chains; i++) + { + if (!build_chk_file(i, current_chk_file, ¤t_chk_file)) + return(FALSE); + } + return(TRUE); +} + +/* Given a lost chain number and a likely starting point for the +FILE???.CHK file create the file from the lost chain. */ +BOOLEAN build_chk_file(long bad_chain_no, long current_file_no, long *ret_file_no) /*__fn__*/ +{ + long remainder; + long temp; + int fd; + PC_FILE *pfile; + DDRIVE *pdrive; + byte filename[13]; + byte cs_filename[26]; + + for ( ; current_file_no < 999; current_file_no++) + { + /* Create a file name */ + rtfs_strcpy((byte *) &filename[0], (byte *) (CS_OP_ASCII("\\FILE000.CHK"))); + temp = (long) (current_file_no/100); + filename[5] = (byte) (CS_OP_ASCII('0') + temp); + remainder = (long) (current_file_no - (temp * 100)); + temp = (long) (remainder/10); + filename[6] = (byte) (CS_OP_ASCII('0') + (byte) temp); + remainder = (long) (remainder - (temp * 10)); + filename[7] = (byte) (CS_OP_ASCII('0') + remainder); + /* Map to the native character set */ + CS_OP_ASCII_TO_CS_STR(cs_filename, filename); + /* Try to open it exclusive. This will fail if the file exists */ + fd = (int)po_open(cs_filename, (word)PO_CREAT|PO_EXCL|PO_WRONLY, (word)PS_IREAD|PS_IWRITE); + if (fd >= 0) + { + /* Get underneath the file and set the first cluster to the + beginning of the lost_chain. */ + /* Get the file structure and semaphore lock the drive */ + pfile = pc_fd2file(fd, FALSE); + if (pfile) + { + pdrive = pfile->pobj->pdrive; + pc_pfinode_cluster(pfile->pobj->pdrive,pfile->pobj->finode, + gl.lost_chain_list[bad_chain_no]); + /* Update the size. */ + pfile->pobj->finode->fsize = chain_size(pc_finode_cluster(pfile->pobj->pdrive,pfile->pobj->finode)); + pfile->needs_flush = TRUE; + release_drive_mount(pdrive->driveno); /* Release lock, unmount if aborted */ + } + /* Close the file. This will write everything out. */ + po_close(fd); + break; + } + } + + /* If we get here it did not work */ + if (current_file_no == 999) + return(FALSE); + + /* Return the next ??? for FILE???.CHK that will probably work */ + *ret_file_no = (int) (current_file_no + 1); + return(TRUE); +} + + + +/************************************************************************ +* * +* File/Directory Scanning * +* * +************************************************************************/ + +/* Scan all files/directories on the drive - +* Mark all used clusterd in the used bit map. +* Note any crossed cluster chains +* Adjust any incorrect file sizes +*/ + + +/* int scan_all_files(byte *dir_name) +* +* This routine scans all subdirectories and does the following: +* . calculates gl.n_user_directories +* . calculates gl.n_hidden_files +* . calculates gl.n_user_files +* Then it calls process_used_map for each file in the directory and +* for the directory itself . +* process_used_map does the following: +* . calculates gl.n_dir_clusters +* . calculates gl.n_hidden_clusters +* . calculates gl.n_file_clusters +* . notes and if writing adjusts incorrect filesizes +* . finds crossed chains +* . calls scan_for_bad_lfns +* . +* . scan_all_files calls itself recursively for each subdirectory it +* . encounters. +* . +* . +*/ + +BOOLEAN scan_all_files(byte *dir_name) /*__fn__*/ +{ + DROBJ *directory; + DROBJ *entry; + byte ldir_name[EMAXPATH_BYTES]; + + if (gl.recursion_depth > MAX_RECURSION_DEPTH) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_45, dir_name,PRFLG_NL);} /* "Path too deep , directory == " */ + return(FALSE); + } + gl.recursion_depth += 1; + + + /* Only do this on the first pass */ + if (gl.on_first_pass) + { + /* Find the directory again for scanning the dir for lfn errors */ + directory = pc_fndnode(dir_name); + if (!directory) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_46, dir_name,PRFLG_NL);} /* "Failed Scanning This Directory on LFN Pass -" */ + return(FALSE); + } + + /* Scan through the directory looking for bad lfn data */ + gl.n_bad_lfns += scan_for_bad_lfns(directory, gl.fix_problems); + pc_freeobj(directory); + } + + /* Find the directory for scanning the dir for files */ + directory = pc_fndnode(dir_name); + if (!directory) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_47, dir_name,PRFLG_NL);} /* "Failed Scanning This Directory -" */ + return(FALSE); + } + if (gl.be_verbose && gl.on_first_pass) + { + RTFS_PRINT_STRING_2(USTRING_SYS_NULL,dir_name,PRFLG_NL); + } + if (!pc_isroot(directory)) + gl.n_user_directories += 1; + + /* Mark all of this Dirs clusters IN use, and look for crossed chains */ + if (!process_used_map(directory, dir_name)) + { + pc_freeobj(directory); + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_48, dir_name,PRFLG_NL);} /* "Failed Scanning This Directory -" */ + return(FALSE); + } + + /* Scan through the directory looking for all files */ + entry = pc_get_inode(0,directory, 0, 0, GET_INODE_STAR); + if (entry) + { + do + { + if (!(entry->finode->fattribute & (AVOLUME | ADIRENT) )) + { + pc_cs_mfile((byte*)gl.gl_file_name, (byte*)entry->finode->fname,(byte *) entry->finode->fext); + pc_mpath((byte *)gl.gl_file_path, (byte *)dir_name, (byte *)gl.gl_file_name); + if (gl.be_verbose && gl.on_first_pass) + { + RTFS_PRINT_STRING_2(USTRING_CHKDSK_49, gl.gl_file_path,PRFLG_NL); /* " " */ + } + if (entry->finode->fattribute & AHIDDEN) + gl.n_hidden_files += 1; + else + gl.n_user_files += 1; + /* Mark all of this File s clusters IN use, check the size + and look for crossed chains */ + if (!process_used_map(entry, gl.gl_file_path)) + { + pc_freeobj(entry); + pc_freeobj(directory); + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_50, gl.gl_file_path,PRFLG_NL);} /* "Failed Scanning This File " */ + return(FALSE); + } + } + } while (pc_get_inode(entry , directory, 0, 0, GET_INODE_STAR)); + pc_freeobj(entry); + } + pc_freeobj(directory); + + /* Now we call scan_all_files() for each subdirectory */ + /* Find the directory for scanning the dir for files */ + directory = pc_fndnode(dir_name); + if (!directory) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_51, dir_name,PRFLG_NL);} /* "Failed Scanning This Directory -" */ + return(FALSE); + } + + /* Scan through the directory looking for all files */ + entry = pc_get_inode(0,directory, 0, 0, GET_INODE_STAR); + if (entry) + { + do + { + /* Scan it if it is a directory and not . or .. */ + if (entry->finode->fattribute & ADIRENT) + { + if ( !pc_isdot(entry->finode->fname, entry->finode->fext) && + !pc_isdotdot(entry->finode->fname, entry->finode->fext) ) + { + pc_cs_mfile((byte *)gl.gl_file_name, (byte *)entry->finode->fname, (byte *)entry->finode->fext); + pc_mpath((byte *)ldir_name, (byte *)dir_name, (byte *)gl.gl_file_name); + if (!scan_all_files(ldir_name)) + { + pc_freeobj(directory); + pc_freeobj(entry); + return(FALSE); + } + } + } + } while (pc_get_inode(entry , directory, 0, 0, GET_INODE_STAR)); + pc_freeobj(entry); + } + pc_freeobj(directory); + gl.recursion_depth -= 1; + + return (TRUE); +} + + +/* process_used_map(DROBJ *pobj, byte *filename) +* +* This routine is called for each subdirectory and file in the system. +* It traverses the chain owned by drobj and does the following with each +* cluster in the chain: +* . updates gl.n_dir_clusters or .. +* . updates gl.n_hidden_clusters or .. +* . updates gl.n_file_clusters +* It then checks the bit map of already accounted for clusters, if the +* cluster is accounted for it adds the cluster to the list of lost +* chains by calling add_cluster_to_crossed(), if another cluster in the +* chain was already found to be crossed we do not call add_cluster_to_crossed() +* since it works with the whole chain. +* +* After the chain has been traversed its size is compared with the size +* recorded in the directory. If they differ the file is printed and if -f +* was specified the size is adjusted. +*/ + +BOOLEAN process_used_map(DROBJ *pobj, byte *filename) /*__fn__*/ +{ + CLTYPE cluster; + CLTYPE n_clusters; + CLTYPE proper_size; + CLTYPE true_size; + dword t1,t2,t3; + BOOLEAN is_dir; + BOOLEAN is_hidden; + BOOLEAN found_an_intersection; + + is_hidden = FALSE; + if (pc_isroot(pobj)) + /* FAT32 stores the root directory as a cluster chain */ + { + if (pobj->pdrive->fasize == 8) /* FAT32 volume */ + { + cluster = pc_sec2cluster(pobj->pdrive,pobj->pdrive->rootblock); + is_dir = TRUE; + } + else + return(TRUE); + } + else + { + /* For tracking crossed chains. We only want to note the intersection + points of lost chains not every cluster in the chain. */ + + if (pobj->finode->fattribute & ADIRENT) + is_dir = TRUE; + else + { + if (pobj->finode->fattribute & AHIDDEN) + is_hidden = TRUE; + else + is_hidden = FALSE; + is_dir = FALSE; + } + cluster = pc_finode_cluster(pobj->pdrive, pobj->finode); + } + found_an_intersection = FALSE; + n_clusters = 0; + /* If the incoming value is bad do not traverse */ + if ((cluster < 2) || (cluster > pobj->pdrive->maxfindex) ) + cluster = 0; + /* 0xffffffff is gnext's end marker, 0 is error */ + while (cluster && cluster != 0xffffffff) + { + n_clusters += 1; + if ( (gl.cl_start <= cluster) && (cluster < gl.cl_end) ) + { + /* If the cluster is already in use we have a problem its + a crossed chain */ + if ( get_bit(gl.bm_used, cluster - gl.cl_start) ) + { + if (!found_an_intersection) + { + if (!add_cluster_to_crossed(cluster)) + return(FALSE); + found_an_intersection = TRUE; + } + } + set_bit(gl.bm_used, cluster - gl.cl_start); + } + + cluster = FATOP(pobj->pdrive)->fatop_clnext(pobj->pdrive, cluster); /* Fat */ + } + + if (gl.on_first_pass) + { + if (is_dir) + { + /* To do in version 5. if (n_clusters == 0) convert directory + to a file */ + gl.n_dir_clusters = (CLTYPE) (gl.n_dir_clusters + n_clusters); + } + else + { + if (is_hidden) + gl.n_hidden_clusters = (CLTYPE) (gl.n_hidden_clusters + n_clusters); + else + gl.n_file_clusters = (CLTYPE) (gl.n_file_clusters + n_clusters); + + /* Check the file size here */ + /* Re-use the true size variable to save a little stack (we are + inside a recursive algorithm here */ +#define cl_size_minus_1 true_size + cl_size_minus_1 = pobj->pdrive->secpalloc; + cl_size_minus_1 <<= 9; /* *= 512 */ + cl_size_minus_1 -=1; + /* proper_size = + (pobj->finode->fsize + cl_size_minus_1) & ~cl_size_minus_1; + */ + t1 = (dword) cl_size_minus_1; + t2 = (dword) (pobj->finode->fsize + t1); + t3 = (dword) (t2 & ~t1); + /* This is how many bytes the file should occupy in the FAT. */ + proper_size = (CLTYPE) t3; +#undef cl_size_minus_1 + true_size = n_clusters; + true_size <<= pobj->pdrive->log2_secpalloc; + true_size <<= 9; /* *= 512 */ + if (proper_size != true_size) + { + /* Size in the directory entry does not match the size of the + chain. If -f was specified update the file size in the directory */ + if (gl.fix_problems) + { + pobj->finode->fsize = true_size; + if (pobj->finode->fsize == 0) + { /* File is zero sized so zero the cluster pointer */ + pc_pfinode_cluster(pobj->pdrive, pobj->finode, 0); + } + if (!pc_update_inode(pobj, TRUE, TRUE)) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_52, filename,PRFLG_NL);} /* "Failed Writing This Adusted File: " */ + return(FALSE); + } + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_53, filename, PRFLG_NL);} /* "Size Adusted, This File: " */ + } + else + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_54, filename,PRFLG_NL);} /* "Size Needs Adusting This File: " */ + } + } + } + } /* if (on_first_pass) */ + return(TRUE); +} + + +/************************************************************************ +* * +* Lost cluster fns * +* * +************************************************************************/ + +/* check_lost_clusters(DDRIVE *pdr) +* +* This routine is called by (app_entry()) after scan_all_files() was called +* to produce the bm_used bitmap of clusters cliamed by files and +* sub-direcories. +* It scans the FILE allocation table. A cluster is lost if it +* is allocated in the FAT but not in the bm_used bitmap we built up while +* scanning the file system (scan_all_files()). We maintain an array of +* chain heads of lost clusters. +*/ + +BOOLEAN check_lost_clusters(DDRIVE *pdr) /*__fn__*/ +{ + CLTYPE cluster; + CLTYPE nxt; + + for (cluster = gl.cl_start ; cluster < gl.cl_end; cluster++) + { + + if (!FATOP(pdr)->fatop_faxx(pdr, cluster, &nxt)) /* Fat */ + { + return(FALSE); + } + if (nxt != 0) + { + /* If we did not see the cluster during the directory scan */ + if ( !get_bit(gl.bm_used, cluster - gl.cl_start) ) + { + /* ff(f)7 marks a bad cluster if it is not already in + a chain. */ + if ( ((pdr->fasize == 3) && (nxt == 0xff7)) || + ((pdr->fasize == 4) && (nxt == 0xfff7)) + || ((pdr->fasize == 8) && (nxt == 0xfffffff7ul)) + ) + gl.n_bad_clusters += 1; + + if ( ((pdr->fasize == 3) && ((nxt < 0xff0) || (nxt > 0xff8))) || + ((pdr->fasize == 4) && ((nxt < 0xfff0) || (nxt > 0xfff8))) + || ((pdr->fasize == 8) && ((nxt < 0xfffffff0ul) || (nxt > 0xfffffff8ul))) + ) + { + /* And if it is not a bad or reserved cluster */ + /* Add it to the lost list */ + if (!add_cluster_to_lost_list(pdr, cluster)) + return(FALSE); + } + } + } + else + gl.n_free_clusters += 1; + + } + + return (TRUE); +} + +/* int add_cluster_to_lost_list(pdrive, cluster) +* +* This routine is called by check_lost_clusters. It takes a cluster known +* to be lost and adds the chain which it heads to the lost chain list. If a +* chain head in the lost chain list is a member of the chain headed by +* cluster, it is replaced. With cluster. +*/ + +BOOLEAN add_cluster_to_lost_list(DDRIVE *pdr , CLTYPE cluster) /*__fn__*/ +{ + CLTYPE first_cluster_in_chain; + int i; + BOOLEAN found; + + /* Save the top of the chain */ + first_cluster_in_chain = cluster; + + /* No need to check the incoming cluster argument, known good */ + + found = FALSE; + /* 0xffffffff is gnext's end marker, 0 is error */ + while (cluster && cluster != 0xffffffff) + { + /* See if this cluster is the head of another lost chain + If so replace the head of that chain with the head of + the chain we are traversing */ + if (!found) + { + for (i = 0; i < (int)gl.n_lost_chains; i++) + { + if (gl.lost_chain_list[i] == cluster) + { + gl.lost_chain_list[i] = first_cluster_in_chain; + found = TRUE; + break; + } + } + } + /* Now mark the cluster used. - we do this so we do not re-process + any of the clusters in this chain */ + if ((gl.cl_start <= cluster) && (cluster < gl.cl_end)) + { + set_bit(gl.bm_used, cluster - gl.cl_start); + } + + cluster = FATOP(pdr)->fatop_clnext(pdr, cluster); /* Fat */ + } + /* If we did not attach the chain to a list we already had we will + add it to the end */ + if (!found) + gl.lost_chain_list[gl.n_lost_chains++] = first_cluster_in_chain; + + if (gl.n_lost_chains >= NLOST_ALLOWED) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_55,PRFLG_NL);} /* "Chkdsk Giving up. Too Many Lost Chains" */ + return(FALSE); + } + return(TRUE); +} + +/* count_lost_clusters(DDRIVE *pdr) +* +* This routine scans the lost chain list and tallies the total number of +* clusters that were found. +*/ + +dword count_lost_clusters(DDRIVE *pdr) /*__fn__*/ +{ + int i; + CLTYPE cluster; + + gl.n_lost_clusters = 0; + for (i = 0; i < (int)gl.n_lost_chains; i++) + { + cluster = gl.lost_chain_list[i]; + /* clnext will return zero but check range for initial value */ + if ((cluster < 2) || (cluster > pdr->maxfindex) ) + cluster = 0; + /* 0xffffffff is gnext's end marker, 0 is error */ + while (cluster && cluster != 0xffffffff) + { + gl.n_lost_clusters++; + cluster = FATOP(pdr)->fatop_clnext(pdr, cluster); /* Fat */ + } + } + return(gl.n_lost_clusters); +} + + +/************************************************************************ +* * +* Crossed chain fns * +* * +************************************************************************/ + +/* scan_crossed_files() +* +* This routine scans all files and subdirectories in the whole filesystem. +* For each cluster it looks to see if it is in the crossed file list that +* we generated earlier. If the cluster is found the file or subdirectory +* is added to the list of crossed files at that cluster. +*/ + +BOOLEAN scan_crossed_files(byte *dir_name) /*__fn__*/ +{ + DROBJ *directory; + DROBJ *entry; + byte ldir_name[EMAXPATH_BYTES]; + + /* Find the directory for scanning the dir for files */ + directory = pc_fndnode(dir_name); + if (!directory) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_56, dir_name, PRFLG_NL);} /* "Failed Scanning This Directory " */ + return(FALSE); + } + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_SYS_NULL,dir_name, PRFLG_NL);} + if (!pc_isroot(directory)) + gl.n_user_directories += 1; + + /* See if this directory is crossed with another */ + if (!process_crossed_file(directory, dir_name)) + { + pc_freeobj(directory); + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_57, dir_name, PRFLG_NL);} /* "Failed Scanning This Directory " */ + return(FALSE); + } + + /* Scan through the directory looking for all files */ + entry = pc_get_inode(0,directory, 0, 0, GET_INODE_STAR); + if (entry) + { + do + { + if (!(entry->finode->fattribute & (AVOLUME | ADIRENT) )) + { + pc_cs_mfile((byte *)gl.gl_file_name, (byte *)entry->finode->fname, (byte *)entry->finode->fext); + pc_mpath((byte *)gl.gl_file_path, (byte *)dir_name, (byte *)gl.gl_file_name); + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_58, gl.gl_file_path, PRFLG_NL);} /* " " */ + /* See if this file is crossed with another */ + if (!process_crossed_file(entry, gl.gl_file_path)) + { + pc_freeobj(entry); + pc_freeobj(directory); + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_59, gl.gl_file_path,PRFLG_NL);} /* "Failed Scanning This File " */ + return(FALSE); + } + + } + } while (pc_get_inode(entry , directory, 0, 0, GET_INODE_STAR)); + + pc_freeobj(entry); + } + pc_freeobj(directory); + + + /* Now we call scan_crossed_files() for each subdirectory */ + /* Find the directory for scanning the dir for files */ + directory = pc_fndnode(dir_name); + if (!directory) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_2(USTRING_CHKDSK_60, dir_name, PRFLG_NL);} /* "Failed Scanning This Directory " */ + return(FALSE); + } + + /* Scan through the directory looking for all files */ + entry = pc_get_inode(0,directory, 0, 0, GET_INODE_STAR); + if (entry) + { + do + { + /* Scan it if it is a directory and not "." or ".." */ + if (entry->finode->fattribute & ADIRENT) + { + if ( !pc_isdot(entry->finode->fname, entry->finode->fext) && + !pc_isdotdot(entry->finode->fname, entry->finode->fext) ) + { + pc_cs_mfile((byte *)gl.gl_file_name, (byte *)entry->finode->fname, (byte *)entry->finode->fext); + pc_mpath((byte *)ldir_name, (byte *)dir_name, (byte *)gl.gl_file_name); + if (!scan_crossed_files(ldir_name)) + { + pc_freeobj(directory); + pc_freeobj(entry); + return(FALSE); + } + } + } + } while (pc_get_inode(entry , directory, 0, 0, GET_INODE_STAR)); + pc_freeobj(entry); + } + pc_freeobj(directory); + return (TRUE); +} + + +/* Given the file or directory at pobj see if any of its clusters are +crossed with another file or directory. */ +BOOLEAN process_crossed_file(DROBJ *pobj, byte *filename) /*__fn__*/ +{ + CLTYPE cluster; + int i; + CROSSED_FILE *pcross; + + if (pc_isroot(pobj)) + return(TRUE); + + cluster = pc_finode_cluster(pobj->pdrive, pobj->finode); + /* If the incoming value is bad do not traverse */ + if ((cluster < 2) || (cluster > pobj->pdrive->maxfindex) ) + cluster = 0; + /* 0xffffffff is gnext's end marker, 0 is error */ + while (cluster && cluster != 0xffffffff) + { + if ((cluster < 2) || (cluster > pobj->pdrive->maxfindex) ) + break; + for (i = 0; i < (int)gl.n_crossed_points; i++) + { + /* If this cluster intersects with another chain. */ + if (gl.crossed_points[i].cluster == cluster) + { + pcross = gl.crossed_points[i].plist; + while (pcross) + { + /* If already in the list for this cluster continue + processing the next cluster. */ + if (rtfs_cs_strcmp(pcross->file_name, filename) == 0) + break; + pcross = pcross->pnext; + } + if (!pcross) + { + /* Add this file/dir name to the list of files crossed + at this point */ + if (!gl.crossed_file_freelist) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_61,PRFLG_NL);} /* "Chkdsk gives up, too many crossed Files" */ + return(FALSE); + } + else + { + /* Add the file to the beginning of the list of files + crossed at this cluster */ + pcross = gl.crossed_file_freelist; + gl.crossed_file_freelist = pcross->pnext; + pcross->pnext = gl.crossed_points[i].plist; + gl.crossed_points[i].plist = pcross; + rtfs_cs_strcpy(pcross->file_name, filename); + } + } + } + } + cluster = FATOP(pobj->pdrive)->fatop_clnext(pobj->pdrive, cluster); /* Fat */ + } + return(TRUE); +} + +/* add_cluster_to_crossed(cluster) +* +* This routine is called by process_used_map if it detects a crossed +* chain (by finding a cluster that is already marked in the used map) +* It scans the list of crossed clusters. If cluster is not already in +* the list it is added. If we exceed the predefined value NCROSSED_ALLOWED +* it returns FALSE. +*/ + +BOOLEAN add_cluster_to_crossed(CLTYPE cluster) /*__fn__*/ +{ + int i; + + if (gl.n_crossed_points >= NCROSSED_ALLOWED) + { + if (gl.be_verbose) + {RTFS_PRINT_STRING_1(USTRING_CHKDSK_62,PRFLG_NL);} /* "Chkdsk gives up, Too many crossed chains" */ + return(FALSE); + } + + for (i = 0; i < (int) gl.n_crossed_points; i++) + { + if (gl.crossed_points[i].cluster == cluster) + return(TRUE); + } + gl.crossed_points[gl.n_crossed_points].cluster = cluster; + gl.n_crossed_points += 1; + return(TRUE); +} + + +/************************************************************************ +* * +* Utility Functions * +* * +************************************************************************/ + +/* chain_size (CLTYPE cluster) +* +* Calculate (return) a chain s size in bytes +* +* Called by: build_chk_file +*/ + +CLTYPE chain_size(CLTYPE cluster) /*__fn__*/ +{ + CLTYPE n_clusters; + + n_clusters = 0; + /* If the incoming value is bad do not traverse */ + if ((cluster < 2) || (cluster > gl.drive_structure->maxfindex)) + cluster = 0; + /* 0xffffffff is gnext's end marker, 0 is error */ + while (cluster && cluster != 0xffffffff) + { + n_clusters = (CLTYPE) (n_clusters + 1); + cluster = FATOP(gl.drive_structure)->fatop_clnext(gl.drive_structure, cluster); /* Fat */ + } + n_clusters = (CLTYPE) (n_clusters * gl.drive_structure->secpalloc); + n_clusters *= (CLTYPE) 512; + return(n_clusters); +} + +/* clr_bit (byte *bitmap, dword index) +* +* Clear the bit at bitmap[index] +*/ + +void clr_bit(byte *bitmap, dword index) +{ + bitmap[(dword) (index >> 3)] &= ~(1 << (index & 0x7)); +} + +/* get_bit (byte *bitmap, dword index) +* +* Return the bit at bitmap[index] (assuming bitmap is a bit array) +*/ + +byte get_bit(byte *bitmap, dword index) +{ + return ((byte)(bitmap[(dword) (index >> 3)] & (1 << (index & 0x7)))); +} + +/* set_bit (byte *bitmap, dword index) +* +* Set the bit at bitmap[index] to 1 +*/ + +void set_bit(byte *bitmap, dword index) +{ + bitmap[(dword) (index >> 3)] |= (1 << (index & 0x7)); +} diff --git a/build/libraries/fatfs/ARM7/apicnfig.c b/build/libraries/fatfs/ARM7/apicnfig.c new file mode 100644 index 0000000..a2b3ee4 --- /dev/null +++ b/build/libraries/fatfs/ARM7/apicnfig.c @@ -0,0 +1,450 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS inc, 1996 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ + +/****************************************************************************** +PC_ERTFS_CONFIG() - Set up the ERTFS configuration block and allocate +or assign memory blocks for ERTFS usage. + + The user must modify this code if he wishes to reconfigure ERTFS. + He must also initialize the RTFS configuration structure with the + addresses of memory blocks to be used by ERTFS. + + Tutorial: + pc_ertfs_config(void) initializes the ertfs configuration block and + provides ERTFS with the addresses of memory that it needs. It is + called from the ertfs initialization routine pc_ertfs_init(). + + This routine is designed to be modified by the user if he wishes to + change the default configuration. To simplify the user's task we + define some configuration constants in this file that may be modified + to change the configuration. These constants are only used locally to + this file. If another method of configuring ERTFS is more appropriate + for your environment then devize an alternate method to initialize the + configuration block. + + The default configuration is: + + ALLOC_FROM_HEAP 1 Use malloc() to allocate, set to 0 to use declared + memory arrays. + NDRIVES 10 L: Max number of drives from 0 to 25 A: - Z: + Note: A FAT buffer pool must be allocated for each drives. If NDRIVES + is changed you must add code to allocate or assign more FAT buffer pools. + The default configuration allocates or assigns 10 fat buffer pools, + If you increase NDRIVES then add code that duplicates the cuurent code + that initialises prtfs_cfg->fat_buffers[9], to dircves 10, 11. 12 ... + If you decrease NDRIVES then removes the cuurent code that + initialises prtfs_cfg->fat_buffers[9], 8, 7, 6 etc + If you are not using ALLOC_FROM_HEAP you shaoul also add or remove the + memory array declarations. + NUM_USERS 1 Maximum number of USER contexts set to 1 for POLLED + mode, otherwise set to the number of tasks that + will simultaneously use ERTFS + NBLKBUFFS 10 Number of blocks in the buffer pool. Uses 532 + bytes per block. Impacts performance during + directory traversals. Must be at least 4 + + + BLKHASHSIZE 16 Size of the block buffer hash table. + This value must be a power of two. + + NUSERFILES 10 The maximum number of open Files at a time + + LARGE_FAT_SIZE 32 The number of 520 byte blocks committed for + buffering fat blocks on drive C: + + LARGE_FAT_HASHSIZE 32 The number of 12 byte hash table entries + committed for use on drive C: + This value must be a power of two. + + SMALL_FAT_SIZE 2 The number of 520 byte blocks committed for + buffering fat blocks on drives other than C: + + SMALL_FAT_HASHSIZE 2 The number of 12 byte hash table entries + committed for use on drives other than C: + This value must be a power of two. + + Note: Each drive may have a different fat buffer and cache sizes. For + convenience here we only use two possible different sizes. You may tune + each drive individually by setting the configuration value: + prtfs_cfg->cfg_FAT_BUFFER_SIZE[DRIVENUMBER]; and + prtfs_cfg->cfg_FAT_CACHE_SIZE[DRIVENUMBER]; + prtfs_cfg->fat_buffers[DRIVENUMBER]; + prtfs_cfg->fat_hash_table[DRIVENUMBER]; + +*/ +#include + + +#define ALLOC_FROM_HEAP 0 /* Set this to 1 to use malloc() to allocate +ERTFS memory at startup, set to 0 to use +declare memory arrays and provide ertfs +with the addresses of those arrays */ +#define NDRIVES 8 /* Number of drives */ +#define NUM_USERS 1 /* Must be one for POLLOS */ +#define NBLKBUFFS 20 /* Number of blocks in the buffer pool. Uses 532 +bytes per block. Impacts performance during +directory traversals must be at least 4 */ +#define BLKHASHSIZE 16 /* This value must be a power of two. +Size of the block buffer hash table. */ + +#define LARGE_FAT_SIZE 16 /* Number of 520 byte blocks committed for +buffering fat blocks on drive C: */ //ctr modified 32->16 + +#define LARGE_FAT_HASHSIZE 16 /* This value must be a power of two. +Number of 12 byte hash table entries //ctr modified 32->16 +committed for use on drive C: */ +#define SMALL_FAT_SIZE 2 /* Number of 520 byte blocks committed for +buffering fat blocks all other drives */ + +#define SMALL_FAT_HASHSIZE 2 /* This value must be a power of two. +Number of 12 byte hash table entries +committed for use on all other drivees */ + + + +#define NUSERFILES 10 /* Maximum Number of open Files at a time */ + +/* Directory Object Needs. Conservative guess is One CWD per user per drive + +One per file + one per User for directory traversal */ +/* This is calculated... do not change */ +#define NFINODES (32 + NUM_USERS*NDRIVES + NUM_USERS + NUSERFILES) +#define NDROBJS (32 + NUM_USERS*NDRIVES + NUM_USERS + NUSERFILES) + +RTFS_CFG *prtfs_cfg; /* The user must initialize this value to point +to a valid configuration block */ +static RTFS_CFG rtfs_cfg_core; /* The user must initialize this value to point */ +#if (ALLOC_FROM_HEAP) + /* Using malloc to allocate memory at startup. If malloc is not avaiable + but some other heap manager is, then change the calls to malloc() to +call your heap manager instead */ +#include +#else +/* Not using malloc to allocate memory so declare arrays that we can send +assign to the configuration block at startup. */ +DDRIVE __mem_drives_structures[NDRIVES]; +BLKBUFF __mem_block_pool[NBLKBUFFS]; +BLKBUFF * __mem_block_hash_table[BLKHASHSIZE]; +PC_FILE __mem_file_pool[NUSERFILES]; +DROBJ __mem_drobj_pool[NDROBJS]; +FINODE __mem_finode_pool[NFINODES]; +RTFS_SYSTEM_USER __rtfs_user_table[NUM_USERS]; + +FATBUFF KS_FAR __fat_buffer_0[SMALL_FAT_SIZE]; +FATBUFF KS_FAR __fat_buffer_1[SMALL_FAT_SIZE]; +FATBUFF KS_FAR __fat_buffer_2[LARGE_FAT_SIZE]; +FATBUFF KS_FAR __fat_buffer_3[SMALL_FAT_SIZE]; +FATBUFF KS_FAR __fat_buffer_4[SMALL_FAT_SIZE]; +FATBUFF KS_FAR __fat_buffer_5[SMALL_FAT_SIZE]; +FATBUFF KS_FAR __fat_buffer_6[SMALL_FAT_SIZE]; +FATBUFF KS_FAR __fat_buffer_7[SMALL_FAT_SIZE]; +FATBUFF KS_FAR __fat_buffer_8[SMALL_FAT_SIZE]; +FATBUFF KS_FAR __fat_buffer_9[SMALL_FAT_SIZE]; + +FATBUFF * KS_FAR __fat_hash_table_0[SMALL_FAT_HASHSIZE]; +FATBUFF * KS_FAR __fat_hash_table_1[SMALL_FAT_HASHSIZE]; +FATBUFF * KS_FAR __fat_hash_table_2[LARGE_FAT_HASHSIZE]; +FATBUFF * KS_FAR __fat_hash_table_3[SMALL_FAT_HASHSIZE]; +FATBUFF * KS_FAR __fat_hash_table_4[SMALL_FAT_HASHSIZE]; +FATBUFF * KS_FAR __fat_hash_table_5[SMALL_FAT_HASHSIZE]; +FATBUFF * KS_FAR __fat_hash_table_6[SMALL_FAT_HASHSIZE]; +FATBUFF * KS_FAR __fat_hash_table_7[SMALL_FAT_HASHSIZE]; +FATBUFF * KS_FAR __fat_hash_table_8[SMALL_FAT_HASHSIZE]; +FATBUFF * KS_FAR __fat_hash_table_9[SMALL_FAT_HASHSIZE]; + +byte * KS_FAR __fat_primary_cache_0[SMALL_FAT_HASHSIZE]; +byte * KS_FAR __fat_primary_cache_1[SMALL_FAT_HASHSIZE]; +byte * KS_FAR __fat_primary_cache_2[LARGE_FAT_HASHSIZE]; +byte * KS_FAR __fat_primary_cache_3[SMALL_FAT_HASHSIZE]; +byte * KS_FAR __fat_primary_cache_4[SMALL_FAT_HASHSIZE]; +byte * KS_FAR __fat_primary_cache_5[SMALL_FAT_HASHSIZE]; +byte * KS_FAR __fat_primary_cache_6[SMALL_FAT_HASHSIZE]; +byte * KS_FAR __fat_primary_cache_7[SMALL_FAT_HASHSIZE]; +byte * KS_FAR __fat_primary_cache_8[SMALL_FAT_HASHSIZE]; +byte * KS_FAR __fat_primary_cache_9[SMALL_FAT_HASHSIZE]; + +dword KS_FAR __fat_primary_index_0[SMALL_FAT_HASHSIZE]; +dword KS_FAR __fat_primary_index_1[SMALL_FAT_HASHSIZE]; +dword KS_FAR __fat_primary_index_2[LARGE_FAT_HASHSIZE]; +dword KS_FAR __fat_primary_index_3[SMALL_FAT_HASHSIZE]; +dword KS_FAR __fat_primary_index_4[SMALL_FAT_HASHSIZE]; +dword KS_FAR __fat_primary_index_5[SMALL_FAT_HASHSIZE]; +dword KS_FAR __fat_primary_index_6[SMALL_FAT_HASHSIZE]; +dword KS_FAR __fat_primary_index_7[SMALL_FAT_HASHSIZE]; +dword KS_FAR __fat_primary_index_8[SMALL_FAT_HASHSIZE]; +dword KS_FAR __fat_primary_index_9[SMALL_FAT_HASHSIZE]; + +#endif /* (ALLOC_FROM_HEAP) */ + +BOOLEAN pc_ertfs_config(void) /* __apifn__*/ +{ + /* Important: prtfs_cfg must point to a configuration block */ + prtfs_cfg = &rtfs_cfg_core; + + /* Important: the configuration block must be zeroed */ + rtfs_memset(prtfs_cfg, 0, sizeof(rtfs_cfg_core)); + + /* Set Configuration values */ + prtfs_cfg->cfg_NDRIVES = NDRIVES; + prtfs_cfg->cfg_NBLKBUFFS = NBLKBUFFS; + prtfs_cfg->cfg_BLK_HASHTBLE_SIZE = BLKHASHSIZE; + prtfs_cfg->cfg_NUSERFILES = NUSERFILES; + prtfs_cfg->cfg_NDROBJS = NDROBJS; + prtfs_cfg->cfg_NFINODES = NFINODES; + prtfs_cfg->cfg_NUM_USERS = NUM_USERS; + + /* Set FAT size configuration values for each drive */ + prtfs_cfg->cfg_FAT_BUFFER_SIZE[0] = SMALL_FAT_SIZE; + prtfs_cfg->cfg_FAT_BUFFER_SIZE[1] = SMALL_FAT_SIZE; + prtfs_cfg->cfg_FAT_BUFFER_SIZE[2] = LARGE_FAT_SIZE; + prtfs_cfg->cfg_FAT_BUFFER_SIZE[3] = SMALL_FAT_SIZE; + prtfs_cfg->cfg_FAT_BUFFER_SIZE[4] = SMALL_FAT_SIZE; + prtfs_cfg->cfg_FAT_BUFFER_SIZE[5] = SMALL_FAT_SIZE; + prtfs_cfg->cfg_FAT_BUFFER_SIZE[6] = SMALL_FAT_SIZE; + prtfs_cfg->cfg_FAT_BUFFER_SIZE[7] = SMALL_FAT_SIZE; + prtfs_cfg->cfg_FAT_BUFFER_SIZE[8] = SMALL_FAT_SIZE; + prtfs_cfg->cfg_FAT_BUFFER_SIZE[9] = SMALL_FAT_SIZE; + + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[0] = SMALL_FAT_HASHSIZE; + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[1] = SMALL_FAT_HASHSIZE; + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[2] = LARGE_FAT_HASHSIZE; + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[3] = SMALL_FAT_HASHSIZE; + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[4] = SMALL_FAT_HASHSIZE; + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[5] = SMALL_FAT_HASHSIZE; + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[6] = SMALL_FAT_HASHSIZE; + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[7] = SMALL_FAT_HASHSIZE; + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[8] = SMALL_FAT_HASHSIZE; + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[9] = SMALL_FAT_HASHSIZE; + + /* Core that must be provided by the user */ +#if (!ALLOC_FROM_HEAP) + + /* Not using malloc() so assign memory arrays to the configuration block */ + prtfs_cfg->mem_drives_structures = (DDRIVE *) &__mem_drives_structures[0]; + prtfs_cfg->mem_block_pool = (BLKBUFF *) &__mem_block_pool[0]; + prtfs_cfg->mem_block_hash_table = (BLKBUFF **) &__mem_block_hash_table[0]; + prtfs_cfg->mem_file_pool = (PC_FILE *) &__mem_file_pool[0]; + prtfs_cfg->mem_drobj_pool = (DROBJ *) &__mem_drobj_pool[0]; + prtfs_cfg->mem_finode_pool = (FINODE *) &__mem_finode_pool[0]; + prtfs_cfg->rtfs_user_table = (RTFS_SYSTEM_USER *) &__rtfs_user_table[0]; + + prtfs_cfg->fat_buffers[0] = (FATBUFF *) &__fat_buffer_0[0]; + prtfs_cfg->fat_buffers[1] = (FATBUFF *) &__fat_buffer_1[0]; + prtfs_cfg->fat_buffers[2] = (FATBUFF *) &__fat_buffer_2[0]; + prtfs_cfg->fat_buffers[3] = (FATBUFF *) &__fat_buffer_3[0]; + prtfs_cfg->fat_buffers[4] = (FATBUFF *) &__fat_buffer_4[0]; + prtfs_cfg->fat_buffers[5] = (FATBUFF *) &__fat_buffer_5[0]; + prtfs_cfg->fat_buffers[6] = (FATBUFF *) &__fat_buffer_6[0]; + prtfs_cfg->fat_buffers[7] = (FATBUFF *) &__fat_buffer_7[0]; + prtfs_cfg->fat_buffers[8] = (FATBUFF *) &__fat_buffer_8[0]; + prtfs_cfg->fat_buffers[9] = (FATBUFF *) &__fat_buffer_9[0]; + + prtfs_cfg->fat_hash_table[0] = (FATBUFF **) &__fat_hash_table_0[0]; + prtfs_cfg->fat_hash_table[1] = (FATBUFF **) &__fat_hash_table_1[0]; + prtfs_cfg->fat_hash_table[2] = (FATBUFF **) &__fat_hash_table_2[0]; + prtfs_cfg->fat_hash_table[3] = (FATBUFF **) &__fat_hash_table_3[0]; + prtfs_cfg->fat_hash_table[4] = (FATBUFF **) &__fat_hash_table_4[0]; + prtfs_cfg->fat_hash_table[5] = (FATBUFF **) &__fat_hash_table_5[0]; + prtfs_cfg->fat_hash_table[6] = (FATBUFF **) &__fat_hash_table_6[0]; + prtfs_cfg->fat_hash_table[7] = (FATBUFF **) &__fat_hash_table_7[0]; + prtfs_cfg->fat_hash_table[8] = (FATBUFF **) &__fat_hash_table_8[0]; + prtfs_cfg->fat_hash_table[9] = (FATBUFF **) &__fat_hash_table_9[0]; + + prtfs_cfg->fat_primary_cache[0] = (byte **) &__fat_primary_cache_0[0]; + prtfs_cfg->fat_primary_cache[1] = (byte **) &__fat_primary_cache_1[0]; + prtfs_cfg->fat_primary_cache[2] = (byte **) &__fat_primary_cache_2[0]; + prtfs_cfg->fat_primary_cache[3] = (byte **) &__fat_primary_cache_3[0]; + prtfs_cfg->fat_primary_cache[4] = (byte **) &__fat_primary_cache_4[0]; + prtfs_cfg->fat_primary_cache[5] = (byte **) &__fat_primary_cache_5[0]; + prtfs_cfg->fat_primary_cache[6] = (byte **) &__fat_primary_cache_6[0]; + prtfs_cfg->fat_primary_cache[7] = (byte **) &__fat_primary_cache_7[0]; + prtfs_cfg->fat_primary_cache[8] = (byte **) &__fat_primary_cache_8[0]; + prtfs_cfg->fat_primary_cache[9] = (byte **) &__fat_primary_cache_9[0]; + + prtfs_cfg->fat_primary_index[0] = (dword *) &__fat_primary_index_0[0]; + prtfs_cfg->fat_primary_index[1] = (dword *) &__fat_primary_index_1[0]; + prtfs_cfg->fat_primary_index[2] = (dword *) &__fat_primary_index_2[0]; + prtfs_cfg->fat_primary_index[3] = (dword *) &__fat_primary_index_3[0]; + prtfs_cfg->fat_primary_index[4] = (dword *) &__fat_primary_index_4[0]; + prtfs_cfg->fat_primary_index[5] = (dword *) &__fat_primary_index_5[0]; + prtfs_cfg->fat_primary_index[6] = (dword *) &__fat_primary_index_6[0]; + prtfs_cfg->fat_primary_index[7] = (dword *) &__fat_primary_index_7[0]; + prtfs_cfg->fat_primary_index[8] = (dword *) &__fat_primary_index_8[0]; + prtfs_cfg->fat_primary_index[9] = (dword *) &__fat_primary_index_9[0]; + return(TRUE); + +#else + /* Use Malloc do allocated ERTFS data */ + prtfs_cfg->mem_drives_structures = (DDRIVE *) malloc(prtfs_cfg->cfg_NDRIVES*sizeof(DDRIVE)); + prtfs_cfg->mem_block_pool = (BLKBUFF*) malloc(prtfs_cfg->cfg_NBLKBUFFS*sizeof(BLKBUFF)); + prtfs_cfg->mem_block_hash_table = (BLKBUFF **) malloc(prtfs_cfg->cfg_BLK_HASHTBLE_SIZE*sizeof(BLKBUFF *)); + prtfs_cfg->mem_file_pool = (PC_FILE *) malloc(prtfs_cfg->cfg_NUSERFILES* sizeof(PC_FILE)); + prtfs_cfg->mem_drobj_pool = (DROBJ *) malloc(prtfs_cfg->cfg_NDROBJS* sizeof(DROBJ)); + prtfs_cfg->mem_finode_pool = (FINODE *) malloc(prtfs_cfg->cfg_NFINODES* sizeof(FINODE)); + prtfs_cfg->rtfs_user_table = (RTFS_SYSTEM_USER *) malloc(prtfs_cfg->cfg_NUM_USERS * sizeof(RTFS_SYSTEM_USER)); + + prtfs_cfg->fat_buffers[0] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[0]*sizeof(FATBUFF)); + prtfs_cfg->fat_buffers[1] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[1]*sizeof(FATBUFF)); + prtfs_cfg->fat_buffers[2] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[2]*sizeof(FATBUFF)); + prtfs_cfg->fat_buffers[3] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[3]*sizeof(FATBUFF)); + prtfs_cfg->fat_buffers[4] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[4]*sizeof(FATBUFF)); + prtfs_cfg->fat_buffers[5] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[5]*sizeof(FATBUFF)); + prtfs_cfg->fat_buffers[6] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[6]*sizeof(FATBUFF)); + prtfs_cfg->fat_buffers[7] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[7]*sizeof(FATBUFF)); + prtfs_cfg->fat_buffers[8] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[8]*sizeof(FATBUFF)); + prtfs_cfg->fat_buffers[9] = (FATBUFF *) malloc(prtfs_cfg->cfg_FAT_BUFFER_SIZE[9]*sizeof(FATBUFF)); + + prtfs_cfg->fat_hash_table[0] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[0]*sizeof(FATBUFF *)); + prtfs_cfg->fat_hash_table[1] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[1]*sizeof(FATBUFF *)); + prtfs_cfg->fat_hash_table[2] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[2]*sizeof(FATBUFF *)); + prtfs_cfg->fat_hash_table[3] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[3]*sizeof(FATBUFF *)); + prtfs_cfg->fat_hash_table[4] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[4]*sizeof(FATBUFF *)); + prtfs_cfg->fat_hash_table[5] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[5]*sizeof(FATBUFF *)); + prtfs_cfg->fat_hash_table[6] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[6]*sizeof(FATBUFF *)); + prtfs_cfg->fat_hash_table[7] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[7]*sizeof(FATBUFF *)); + prtfs_cfg->fat_hash_table[8] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[8]*sizeof(FATBUFF *)); + prtfs_cfg->fat_hash_table[9] = (FATBUFF **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[9]*sizeof(FATBUFF *)); + + prtfs_cfg->fat_primary_cache[0] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[0]*sizeof(byte *)); + prtfs_cfg->fat_primary_cache[1] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[1]*sizeof(byte *)); + prtfs_cfg->fat_primary_cache[2] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[2]*sizeof(byte *)); + prtfs_cfg->fat_primary_cache[3] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[3]*sizeof(byte *)); + prtfs_cfg->fat_primary_cache[4] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[4]*sizeof(byte *)); + prtfs_cfg->fat_primary_cache[5] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[5]*sizeof(byte *)); + prtfs_cfg->fat_primary_cache[6] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[6]*sizeof(byte *)); + prtfs_cfg->fat_primary_cache[7] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[7]*sizeof(byte *)); + prtfs_cfg->fat_primary_cache[8] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[8]*sizeof(byte *)); + prtfs_cfg->fat_primary_cache[9] = (byte **) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[9]*sizeof(byte *)); + + prtfs_cfg->fat_primary_index[0] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[0]*sizeof(dword)); + prtfs_cfg->fat_primary_index[1] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[1]*sizeof(dword)); + prtfs_cfg->fat_primary_index[2] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[2]*sizeof(dword)); + prtfs_cfg->fat_primary_index[3] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[3]*sizeof(dword)); + prtfs_cfg->fat_primary_index[4] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[4]*sizeof(dword)); + prtfs_cfg->fat_primary_index[5] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[5]*sizeof(dword)); + prtfs_cfg->fat_primary_index[6] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[6]*sizeof(dword)); + prtfs_cfg->fat_primary_index[7] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[7]*sizeof(dword)); + prtfs_cfg->fat_primary_index[8] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[8]*sizeof(dword)); + prtfs_cfg->fat_primary_index[9] = (dword *) malloc(prtfs_cfg->cfg_FAT_HASHTBL_SIZE[9]*sizeof(dword)); + + /* Do some sanity checks */ + if (!prtfs_cfg->mem_drives_structures || + !prtfs_cfg->mem_block_pool || + !prtfs_cfg->mem_block_hash_table || + !prtfs_cfg->mem_file_pool || + !prtfs_cfg->mem_drobj_pool || + !prtfs_cfg->mem_finode_pool || + !prtfs_cfg->rtfs_user_table) + goto malloc_failed; + + if (!prtfs_cfg->fat_buffers[0] || + !prtfs_cfg->fat_buffers[1] || + !prtfs_cfg->fat_buffers[2] || + !prtfs_cfg->fat_buffers[3] || + !prtfs_cfg->fat_buffers[4] || + !prtfs_cfg->fat_buffers[5] || + !prtfs_cfg->fat_buffers[6] || + !prtfs_cfg->fat_buffers[7] || + !prtfs_cfg->fat_buffers[8] || + !prtfs_cfg->fat_buffers[9]) + goto malloc_failed; + + if (!prtfs_cfg->fat_hash_table[0] || + !prtfs_cfg->fat_hash_table[1] || + !prtfs_cfg->fat_hash_table[2] || + !prtfs_cfg->fat_hash_table[3] || + !prtfs_cfg->fat_hash_table[4] || + !prtfs_cfg->fat_hash_table[5] || + !prtfs_cfg->fat_hash_table[6] || + !prtfs_cfg->fat_hash_table[7] || + !prtfs_cfg->fat_hash_table[8] || + !prtfs_cfg->fat_hash_table[9]) + goto malloc_failed; + + if (!prtfs_cfg->fat_primary_cache[0] || + !prtfs_cfg->fat_primary_cache[1] || + !prtfs_cfg->fat_primary_cache[2] || + !prtfs_cfg->fat_primary_cache[3] || + !prtfs_cfg->fat_primary_cache[4] || + !prtfs_cfg->fat_primary_cache[5] || + !prtfs_cfg->fat_primary_cache[6] || + !prtfs_cfg->fat_primary_cache[7] || + !prtfs_cfg->fat_primary_cache[8] || + !prtfs_cfg->fat_primary_cache[9]) + goto malloc_failed; + + if (!prtfs_cfg->fat_primary_index[0] || + !prtfs_cfg->fat_primary_index[1] || + !prtfs_cfg->fat_primary_index[2] || + !prtfs_cfg->fat_primary_index[3] || + !prtfs_cfg->fat_primary_index[4] || + !prtfs_cfg->fat_primary_index[5] || + !prtfs_cfg->fat_primary_index[6] || + !prtfs_cfg->fat_primary_index[7] || + !prtfs_cfg->fat_primary_index[8] || + !prtfs_cfg->fat_primary_index[9]) + goto malloc_failed; + + + return(TRUE); + +malloc_failed: + if (prtfs_cfg->mem_drives_structures) free(prtfs_cfg->mem_drives_structures ); + if (prtfs_cfg->mem_block_pool) free(prtfs_cfg->mem_block_pool); + if (prtfs_cfg->mem_block_hash_table) free(prtfs_cfg->mem_block_hash_table); + if (prtfs_cfg->mem_file_pool) free(prtfs_cfg->mem_file_pool); + if (prtfs_cfg->mem_drobj_pool) free(prtfs_cfg->mem_drobj_pool); + if (prtfs_cfg->mem_finode_pool) free(prtfs_cfg->mem_finode_pool); + if (prtfs_cfg->rtfs_user_table) free(prtfs_cfg->rtfs_user_table); + if (prtfs_cfg->fat_buffers[0]) free(prtfs_cfg->fat_buffers[0]); + if (prtfs_cfg->fat_buffers[1]) free(prtfs_cfg->fat_buffers[1]); + if (prtfs_cfg->fat_buffers[2]) free(prtfs_cfg->fat_buffers[2]); + if (prtfs_cfg->fat_buffers[3]) free(prtfs_cfg->fat_buffers[3]); + if (prtfs_cfg->fat_buffers[4]) free(prtfs_cfg->fat_buffers[4]); + if (prtfs_cfg->fat_buffers[5]) free(prtfs_cfg->fat_buffers[5]); + if (prtfs_cfg->fat_buffers[6]) free(prtfs_cfg->fat_buffers[6]); + if (prtfs_cfg->fat_buffers[7]) free(prtfs_cfg->fat_buffers[7]); + if (prtfs_cfg->fat_buffers[8]) free(prtfs_cfg->fat_buffers[8]); + if (prtfs_cfg->fat_buffers[9]) free(prtfs_cfg->fat_buffers[9]); + if (prtfs_cfg->fat_hash_table[0]) free(prtfs_cfg->fat_hash_table[0]); + if (prtfs_cfg->fat_hash_table[1]) free(prtfs_cfg->fat_hash_table[1]); + if (prtfs_cfg->fat_hash_table[2]) free(prtfs_cfg->fat_hash_table[2]); + if (prtfs_cfg->fat_hash_table[3]) free(prtfs_cfg->fat_hash_table[3]); + if (prtfs_cfg->fat_hash_table[4]) free(prtfs_cfg->fat_hash_table[4]); + if (prtfs_cfg->fat_hash_table[5]) free(prtfs_cfg->fat_hash_table[5]); + if (prtfs_cfg->fat_hash_table[6]) free(prtfs_cfg->fat_hash_table[6]); + if (prtfs_cfg->fat_hash_table[7]) free(prtfs_cfg->fat_hash_table[7]); + if (prtfs_cfg->fat_hash_table[8]) free(prtfs_cfg->fat_hash_table[8]); + if (prtfs_cfg->fat_hash_table[9]) free(prtfs_cfg->fat_hash_table[9]); + if (prtfs_cfg->fat_primary_cache[0]) free(prtfs_cfg->fat_primary_cache[0]); + if (prtfs_cfg->fat_primary_cache[1]) free(prtfs_cfg->fat_primary_cache[1]); + if (prtfs_cfg->fat_primary_cache[2]) free(prtfs_cfg->fat_primary_cache[2]); + if (prtfs_cfg->fat_primary_cache[3]) free(prtfs_cfg->fat_primary_cache[3]); + if (prtfs_cfg->fat_primary_cache[4]) free(prtfs_cfg->fat_primary_cache[4]); + if (prtfs_cfg->fat_primary_cache[5]) free(prtfs_cfg->fat_primary_cache[5]); + if (prtfs_cfg->fat_primary_cache[6]) free(prtfs_cfg->fat_primary_cache[6]); + if (prtfs_cfg->fat_primary_cache[7]) free(prtfs_cfg->fat_primary_cache[7]); + if (prtfs_cfg->fat_primary_cache[8]) free(prtfs_cfg->fat_primary_cache[8]); + if (prtfs_cfg->fat_primary_cache[9]) free(prtfs_cfg->fat_primary_cache[9]); + if (prtfs_cfg->fat_primary_index[0]) free(prtfs_cfg->fat_primary_index[0]); + if (prtfs_cfg->fat_primary_index[1]) free(prtfs_cfg->fat_primary_index[1]); + if (prtfs_cfg->fat_primary_index[2]) free(prtfs_cfg->fat_primary_index[2]); + if (prtfs_cfg->fat_primary_index[3]) free(prtfs_cfg->fat_primary_index[3]); + if (prtfs_cfg->fat_primary_index[4]) free(prtfs_cfg->fat_primary_index[4]); + if (prtfs_cfg->fat_primary_index[5]) free(prtfs_cfg->fat_primary_index[5]); + if (prtfs_cfg->fat_primary_index[6]) free(prtfs_cfg->fat_primary_index[6]); + if (prtfs_cfg->fat_primary_index[7]) free(prtfs_cfg->fat_primary_index[7]); + if (prtfs_cfg->fat_primary_index[8]) free(prtfs_cfg->fat_primary_index[8]); + if (prtfs_cfg->fat_primary_index[9]) free(prtfs_cfg->fat_primary_index[9]); + + return(FALSE); +#endif + + +} + diff --git a/build/libraries/fatfs/ARM7/apideltr.c b/build/libraries/fatfs/ARM7/apideltr.c new file mode 100644 index 0000000..df7264d --- /dev/null +++ b/build/libraries/fatfs/ARM7/apideltr.c @@ -0,0 +1,249 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIDELTR.C - Contains user api level source code. + + The following routines are included: + + pc_deltree - Delete an entire directory tree. +*/ + +#include + +/**************************************************************************** +PC_DELTREE - Delete a directory tree. + + Description + + Delete the directory specified in name, all subdirectories of that + directory, and all files contained therein. Fail if name is not a + directory, or is read only. + + Returns + Returns TRUE if the directory was successfully removed. + + errno will be set to one of these values + + 0 - No error + PEINVALIDDRIVEID- Drive component of path is invalid + PEINVALIDPATH - Path specified by name is badly formed. + PENOENT - Can't find path specified by name. + PEACCES - Directory or one of its subdirectories is read only or + in use. + An ERTFS system error + +*****************************************************************************/ + +/* Remove a directory */ +BOOLEAN pc_deltree(byte *name) /*__apifn__*/ +{ + DROBJ *parent_obj; + DROBJ *pobj; + DROBJ *pdotdot; + DROBJ *pchild; + BOOLEAN ret_val; + DDRIVE *pdrive; + byte *path; + byte *filename; + byte fileext[4]; + int driveno; + int p_errno; + int dir_depth; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + parent_obj = 0; + pchild = 0; + pobj = 0; + pdotdot = 0; + ret_val = FALSE; + dir_depth = 1; + + p_errno = 0; + rtfs_set_errno(0); /* pc_deltree: clear error status */ + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(name); + if (driveno < 0) + { /* pc_deltree: errno was by check_drive */ + return(FALSE); + } + pdrive = pc_drno2dr(driveno); + + /* Use the buffers in the DRIVE structure. Access is locked via semaphore */ + path = (byte *)&(pdrive->pathname_buffer[0]); + filename = (byte *)&(pdrive->filename_buffer[0]); + + /* Get out the filename and d:parent */ + if (!pc_parsepath(path,filename,fileext,name)) + { + p_errno = PEINVALIDPATH; + goto errex; + } + /* Find the parent and make sure it is a directory \ */ + parent_obj = pc_fndnode(path); + if (!parent_obj) + goto errex; /* pc_fndnode set errno */ + + if (!pc_isadir(parent_obj) || pc_isavol(parent_obj)) + { + p_errno = PENOENT; + goto errex; + } + /* Find the file and init the structure */ + pobj = pc_get_inode(0, parent_obj, filename, (byte*)fileext, GET_INODE_MATCH); + if (!pobj) + goto errex; /* pc_get_inode set errno */ + + if ( !pc_isadir(pobj) || (pobj->finode->opencount > 1) || + (pobj->finode->fattribute & ARDONLY )) + { + p_errno = PEACCES; + goto errex; + } + + /* Search through the directory. look at all files */ + /* Call pc_get_inode with 0 to give us an obj */ + while (dir_depth > 0) + { + if (pchild) + pc_freeobj(pchild); + pchild = pc_get_inode(0, pobj, 0 , 0, GET_INODE_STAR); + if (pdotdot) { + pc_freeobj(pdotdot); + pdotdot = 0; + } + + if (pchild) do + { + /* delete all nodes which are not subdirs; + step into all subdirs and destroy their contents. */ + if (!(pc_isdot(pchild->finode->fname, pchild->finode->fext) ) ) + { + if (!(pc_isdotdot(pchild->finode->fname, pchild->finode->fext) ) ) + { + if (pc_isadir(pchild)) + { + if ( (pchild->finode->opencount > 1) || + (pchild->finode->fattribute&ARDONLY) ) + { + p_errno = PEACCES; + ret_val = FALSE; + goto errex; + } + dir_depth++; + pc_freeobj(pobj); + pobj = pchild; /* enter first subdir */ + pchild = 0; + goto start_over; + } + else + { + /* Be sure it is not the root. Since the root is an abstraction + we can not delete it plus Check access permissions */ + if ( pc_isroot(pchild) || (pchild->finode->opencount > 1) || + (pchild->finode->fattribute&(ARDONLY|AVOLUME|ADIRENT))) + { + p_errno = PEACCES; + ret_val = FALSE; + goto errex; + } + else + { + /* Remove the file */ + /* calculate max number of clusters to release. + Add bytespcluster-1 in case we are not on a cluster boundary */ + ret_val = pc_rmnode(pchild); + if (!ret_val) + goto errex; /* pc_rmnode sets errno */ + goto start_over; + } + } + } + else + { + if (pdotdot) + pc_freeobj(pdotdot); + pdotdot = pc_get_mom(pchild); + } + } + } + while (pc_get_inode(pchild, pobj, 0 , 0, GET_INODE_STAR)); + + if (get_errno() != PENOENT) + goto errex; /* pc_get_inode set errno */ + + /* dir empty; step out and delete */ + if (pobj) { + pc_freeobj(pobj); + pobj = 0; + } + if (pchild) { + pc_freeobj(pchild); + pchild = 0; + } + dir_depth--; + if (dir_depth > 0) + { + if (!pdotdot) + { + p_errno = PEINVALIDCLUSTER; + goto errex; + } + pchild = pc_get_inode(0, pdotdot, 0 , 0, GET_INODE_STAR); + if (pchild) + { + do + { + if (!(pc_isdot(pchild->finode->fname, pchild->finode->fext) ) ) + { + if (!(pc_isdotdot(pchild->finode->fname, pchild->finode->fext) ) ) + { + ret_val = pc_rmnode(pchild); /* remove the directory */ + if (!ret_val) + goto errex; /* pc_mnode set errno */ + break; + } + } + } while (pc_get_inode(pchild, pdotdot, 0, 0, GET_INODE_STAR)); + } + else + { + if (get_errno() != PENOENT) + goto errex; /* pc_get_inode set errno */ + } + pobj = pdotdot; + pdotdot = 0; + } + else + { + pobj = pc_get_inode(0, parent_obj, filename, (byte*)fileext, GET_INODE_MATCH); + if (!pobj) + goto errex; /* pc_mnode set errno */ + ret_val = pc_rmnode(pobj); /* Remove the directory */ + if (!ret_val) + goto errex; /* pc_mnode set errno */ + } +start_over: + ; + } +errex: + if (pdotdot) + pc_freeobj(pdotdot); + if (pchild) + pc_freeobj(pchild); + if (pobj) + pc_freeobj(pobj); + if (parent_obj) + pc_freeobj(parent_obj); + + if (p_errno) + rtfs_set_errno(p_errno); + if (!release_drive_mount_write(driveno))/* Release lock, unmount if aborted */ + ret_val = FALSE; + return(ret_val); +} + diff --git a/build/libraries/fatfs/ARM7/apienum.c b/build/libraries/fatfs/ARM7/apienum.c new file mode 100644 index 0000000..03ffb86 --- /dev/null +++ b/build/libraries/fatfs/ARM7/apienum.c @@ -0,0 +1,500 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* File APIENUM.C */ +/* pc_enumerate - List select all directory entries that match rules and +* allow a callback routine to process each on. +* +* Description - This routine traverses a subdirectory tree. It tests each +* directory entry to see if it matches user supplied selection criteria. +* if it does match the criteria a user supplied callback function is +* called with the full path name of the directory entry and a pointer +* to a DSTAT structure that contains detailed information about the +* directory entry. (see the manual page for a detailed description of the +* DSTAT structure. +* +* Selection criteria - Two arguments are used to determine the selection +* criteria. On is a flags word that specifies attributes the other is +* a pattern that specifies a wild card pattern. +* The flags argument contains a bitwise or of one or more of the following: +* MATCH_DIR - Select directory entries +* MATCH_VOL - Select volume labels +* MATCH_FILES - Select files +* MATCH_DOT - Select the '.' entry MATH_DIR must be true also +* MATCH_DOTDOT - Select the '..' entry MATCH_DIR must be true also +* +* The selection pattern is a standard wildcard pattern such as '*.*' or +* *.txt. +* Note: Patterns don't work the same for VFAT and DOS 8.3. If VFAT is +* enable the pattern *.* will return any file name that has a '.' in it +* in 8.3 systems it returns all files. +* +* Note: pc_enumerate() requires a fair amount of buffer space to function. +* Instead of allocating the space internally we require that the application +* pass two buffers of size EMAXPATH_BYTES in to the function. See below. +* +* Se +* Summary: +* +* int pc_enumerate( +* byte * from_pattern_buffer - pointer to a scratch buffer of size EMAXPATH_BYTES +* byte * spath_buffer - pointer to a scratch buffer of size EMAXPATH_BYTES +* byte * dpath_buffer - pointer to a scratch buffer of size EMAXPATH_BYTES +* byte * root_search - Root of the search IE C:\ or C:\USR etc. +* word match_flags - Selection flags (see above) +* byte match_pattern - Match pattern (see above) +* int maxdepth - Maximum depth of the traversal. +* Note: to scan only one level set this to +* 1. For all levels set it to 99 +* PENUMCALLBACK pcallback - User callback function. (see below) +* +* Return value +* pc_enumerate() returns 0 unless the callback function returns a non- +* zero value at any point. If the callback returns a non-zero value the +* scan terminates imediately and returns the returned value to the +* application. +* +* errno is set to one of the following +* pc_enumerate() does not set errno +* +* +* About the callback. +* +* The callback function is a function that returns an integer and is passed +* the fully qualified path to the current directory entry and a DSTAT +* structure. The callback fuction must return 0 if it wishes the scan to +* continue or any other integer value to stop the scan and return the +* callback's return value to the application layer. +* +* Examples +* +* The next two function implement a multilevel directory scan. +* int rdir_callback(byte *path, DSTAT *d) {printf("%s\n", path);return(0);} +* +* rdir(byte *path, byte *pattern) +* { +* pc_enumerate(from_path,from_pattern,spath,dpath,path, +* (MATCH_DIR|MATCH_VOL|MATCH_FILES), pattern, 99, rdir_callback); +* } +* +* Poor mans deltree package +* int delfile_callback(byte *path, DSTAT *d) { +* pc_unlink(path); return(0); +* } +* int deldir_callback(byte *path, DSTAT *d) { +* pc_rmdir(path); return(0); +* } +* +* +* deltree(byte *path) +* { +* int i; +* ==> First delete all of the files +* pc_enumerate(from_path,from_pattern,spath,dpath,path, +* (MATCH_FILES), "*",99, delfile_callback); +* i = 0; +* ==> Now delete all of the dirs.. deleting path won't work until the +* ==> tree is empty +* while(!pc_rmdir(path) && i++ < 50) +* pc_enumerate(from_path,from_pattern,spath,dpath,path, +* (MATCH_DIR), "*", 99, deldir_callback); +* } +* +*/ + +#include + +#if (!INCLUDE_CS_UNICODE) /* BUGBUG - Not doing ENUM yet */ +#define MATCH_DIR 0x01 +#define MATCH_VOL 0x02 +#define MATCH_FILES 0x04 +#define MATCH_DOT 0x08 +#define MATCH_DOTDOT 0x10 +typedef int (*PENUMCALLBACK)(byte *path, DSTAT *pstat); + +int pc_enumerate( + byte * from_path_buffer, + byte * from_pattern_buffer, + byte * spath_buffer, + byte * dpath_buffer, + byte * root_search, + word match_flags, + byte * match_pattern, + int maxdepth, + PENUMCALLBACK pcallback); + + +#if (VFAT) +/* UNICODE Fixed */ +/* #define ALL_FILES "*" */ +#define ALL_FILES "*" +#else +/* UNICODE Fixed */ +#define ALL_FILES "*.*" +/* #define ALL_FILES string_star_dot_star */ +#endif + +int get_parent_path(byte *parent, byte *path); +int dirscan_isdot(DSTAT *statobj); +int dirscan_isdotdot(DSTAT *statobj); + + +int pc_enumerate( /* __apifn__ */ + byte * from_path_buffer, + byte * from_pattern_buffer, + byte * spath_buffer, + byte * dpath_buffer, + byte * root_search, + word match_flags, + byte * match_pattern, + int maxdepth, + PENUMCALLBACK pcallback) +{ + int dir_index[30]; + dword dir_block[30]; + int depth; + DSTAT statobj; + int process_it; + int dodone; + int call_back; + int ret_val; +#if (!VFAT) + int i; + /* IN 8.3 systems we parse the match pattern into file and ext parts */ + byte filepat[9]; + byte extpat[9]; + pc_ascii_fileparse(filepat, extpat, (byte *)match_pattern); + for(i = 0; i < 8; i++) if (filepat[i]==' ') filepat[i]=0; + for(i = 0; i < 3; i++) if (extpat[i]==' ') extpat[i]=0; +#endif + + ret_val = 0; + pc_str2upper((byte *)from_path_buffer, (byte *) root_search); + + depth = 0; + + pc_mpath(from_pattern_buffer, from_path_buffer, (byte *)ALL_FILES); + + dir_index[0] = 0; + dir_block[0] = 0; + + do + { +step_into_dir: + dodone = 0; + + if (pc_gfirst(&statobj, (byte *)from_pattern_buffer)) + { + dodone = 1; + process_it = 0; + do + { + if (!dir_block[depth] || ((dir_block[depth] == + ((DROBJ *) (statobj.pobj))->blkinfo.my_block ) && + (dir_index[depth] == + ((DROBJ *) (statobj.pobj))->blkinfo.my_index ))) + { + process_it = 1; + } + if (process_it) + { + call_back = 1; + + /* Don't report directories if not requested */ + if ((statobj.fattribute & ADIRENT) && + !(match_flags & MATCH_DIR) ) + call_back = 0; + + /* Don't report volumes if not requested */ + if (call_back && (statobj.fattribute & AVOLUME) && + !(match_flags & MATCH_VOL) ) + call_back = 0; + + /* Don't report plain files if not requested */ + if (call_back && !(statobj.fattribute & (AVOLUME|ADIRENT)) && + !(match_flags & MATCH_FILES) ) + call_back = 0; + + /* Don't report DOT if not requested */ + if (call_back && dirscan_isdot(&statobj) && !(match_flags & MATCH_DOT)) + call_back = 0; + + /* Don't report DOTDOT if not requested */ + if (call_back && dirscan_isdotdot(&statobj) && !(match_flags & MATCH_DOTDOT)) + call_back = 0; + + if (call_back) + { + /* Take it if the pattern match work */ +#if (VFAT) + /* Under VFAT do a pattern match on the long file name */ + if (statobj.lfname[0]) + call_back = pc_patcmp_vfat(match_pattern, statobj.lfname, TRUE); + else + call_back = pc_patcmp_vfat(match_pattern, (byte *)(statobj.filename), TRUE); +#else + /* Non VFAT uses 8.3 matching conventions */ + pc_ascii_fileparse(filepat, extpat, (byte *)match_pattern); + call_back = + pc_patcmp_8((byte *)&statobj.fname[0], (byte *)&filepat[0] , TRUE); + call_back = call_back && + pc_patcmp_3((byte *)&statobj.fext[0], (byte *)&extpat[0] , TRUE); +#endif + } + /* Construct the full path */ + pc_mpath(dpath_buffer, from_path_buffer, (byte *)statobj.filename); + if (call_back && pcallback) + { + /* If the callback returns non zero he says quit */ + ret_val = pcallback(dpath_buffer, &statobj); + if (ret_val) + { + pc_gdone(&statobj); + goto ex_it; + } + } + } + /* If it is a subdirectory and we are still in our depth range + then process the subdirectory */ + if (process_it && (depth < maxdepth) && + !dirscan_isdot(&statobj) && + !dirscan_isdotdot(&statobj)) + { + /* Construct the full path */ + pc_mpath(spath_buffer, from_path_buffer, (byte *)statobj.filename); + + if (statobj.fattribute & (AVOLUME | ADIRENT)) + { + if (pc_gnext(&statobj)) + { + /* If path is a dir, mark our place and step in */ + dir_block[depth] = ((DROBJ *) (statobj.pobj))->blkinfo.my_block; + dir_index[depth] = ((DROBJ *) (statobj.pobj))->blkinfo.my_index; + dodone = 0; + pc_gdone(&statobj); + + } + else + { + dodone = 0; + pc_gdone(&statobj); + dir_index[depth] = -1; + } + + depth += 1; + dir_block[depth] = 0; + dir_index[depth] = 0; + + rtfs_cs_strcpy((byte *)from_path_buffer, (byte *)spath_buffer); + pc_mpath(from_pattern_buffer, from_path_buffer, (byte *)ALL_FILES); + + goto step_into_dir; + } + } + } while (pc_gnext(&statobj)); /* Get the next file in dir */ + + if (dodone) + pc_gdone(&statobj); + } + + if (!get_parent_path(from_path_buffer, from_path_buffer)) + break; + pc_mpath(from_pattern_buffer, from_path_buffer, (byte *)ALL_FILES); + depth--; + while (depth >= 0 && dir_index[depth] == -1) + { + if (!get_parent_path(from_path_buffer, from_path_buffer)) + break; + pc_mpath(from_pattern_buffer, from_path_buffer, (byte *)ALL_FILES); + depth--; + } + } while (depth >= 0); +ex_it: + return (ret_val); +} + +int get_parent_path(byte *parent, byte *path) /*__fn__*/ +{ + byte *last_backslash; + int size; + + last_backslash = 0; + + size = 0; + while (*path != '\0') + { + size++; + if (*path == '\\') + { + last_backslash = parent; + } + *(parent++) = *(path++); + } + /* This is to prevent catastrophie if caller ignores failure */ + *parent = '\0'; + + if (size < 3) + return (0); + + if (last_backslash) + *last_backslash = '\0'; + return (1); +} + + +/************************************************************************ +* * +* File system abstraction layer * +* * +************************************************************************/ + + +int dirscan_isdot(DSTAT *statobj) +{ + return(rtfs_strcmp((byte *)".", statobj->filename)==0); +} + +int dirscan_isdotdot(DSTAT *statobj) +{ + return(rtfs_strcmp((byte *)"..", statobj->filename)==0); +} + +#ifdef NOTDEF +/* The section from here to the bottom of the file contains code that +shows via example how to use the enumerate function. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void usage(byte *progname) +{ + RTFS_PRINTF("Usage: %s [options] [ ...]\n",progname); +} + + +rdir(byte *path, byte *pattern); +deltree(byte *path, byte *pattern); + + + + +#define FILTER ALL_FILES /* Default filter */ + +int main(int argc,byte **argv) +{ + byte src_path[EMAXPATH_BYTES]; + byte filter[20]; + byte op; + rtfs_kernel_init(); + + argc--; + argv++; + + if (argc < 2) + { + goto usage; + } + op = *argv[0]; + rtfs_cs_strcpy((byte *)filter,FILTER); + + argc--; argv++; + /* Process user options */ + rtfs_cs_strcpy(src_path,*argv); + + argc--; argv++; + if (argc) + { + rtfs_cs_strcpy(filter,*argv); + } + else + { + rtfs_cs_strcpy((byte *)filter,FILTER); + } + + if (op=='R') + { + rdir(src_path, filter); + } + else if (op == 'D') + { + deltree(src_path, filter); + } + else + { +usage: + RTFS_PRINTF("(R)dir or (D)eltree PATH [pattern]\n"); + } + + + + /* treeprintnew (src_path, filter, be_verbose); */ +} + +/* Examples */ +byte from_path[EMAXPATH_BYTES]; +byte from_pattern[EMAXPATH_BYTES]; +byte spath[EMAXPATH_BYTES]; +byte dpath[EMAXPATH_BYTES]; +/* Recursive DIR function */ +int rdir_callback(byte *path, DSTAT *d) +{ + RTFS_PRINTF("%s\n", path); + return(0); +} + +rdir(byte *path, byte *pattern) +{ + pc_enumerate(from_path,from_pattern,spath,dpath,path, + (MATCH_DIR|MATCH_VOL|MATCH_FILES), pattern, 99, rdir_callback); +} + +/* Poor mans deltree package */ +int delfile_callback(byte *path, DSTAT *d) +{ + RTFS_PRINTF("Deleting file %s\n", path); + pc_unlink(path); + return(0); +} +int deldir_callback(byte *path, DSTAT *d) +{ + RTFS_PRINTF("Deleting directory %s\n", path); + pc_rmdir(path); + return(0); +} + + +deltree(byte *path) +{ + int i; + /* First delete all of the files */ + pc_enumerate(from_path,from_pattern,spath,dpath,path, + (MATCH_FILES), "*",99, delfile_callback); + i = 0; + /* Now delete all of the dirs.. deleting the root of the path won't + work until the tree is empty */ + while(!pc_rmdir(path) && i++ < 50) + { + pc_enumerate(from_path,from_pattern,spath,dpath,path, + (MATCH_DIR), "*", 99, deldir_callback); + + } +} + +#endif +#endif /* (!INCLUDE_CS_UNICODE) BUGBUG - Not doing ENUM yet */ + diff --git a/build/libraries/fatfs/ARM7/apifilio.c b/build/libraries/fatfs/ARM7/apifilio.c new file mode 100644 index 0000000..deb4dd4 --- /dev/null +++ b/build/libraries/fatfs/ARM7/apifilio.c @@ -0,0 +1,1411 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIFILEIO.C - Contains user api level file IO source code. + + The following routines are included: + + po_open - Open a file. + po_read - Read bytes from a file. + po_write - Write Bytes to a file. + po_lseek - Move the file pointer. + po_close - Close a file and flush the file allocation table. + pc_fd2file - Map a file descriptor to a file structure. + pc_allocfile - Allocate a file structure. + pc_freefile - Release a file structure. + pc_free_all_fil - Release all file structures for a drive + _synch_file_ptrs - make sure file pointers are synchronyzed +*/ + +#include + +static void pc_freefile(PC_FILE *pfile); + +/**************************************************************************** +PO_OPEN - Open a file. + + Description + Open the file for access as specified in flag. If creating use mode to + set the access permissions on the file. + + Flag values are + + PO_BINARY - Ignored. All file access is binary + PO_TEXT - Ignored + PO_RDONLY - Open for read only + PO_RDWR - Read/write access allowed. + PO_WRONLY - Open for write only + PO_CREAT - Create the file if it does not exist. Use mode to + specify the permission on the file. + PO_EXCL - If flag contains (PO_CREAT | PO_EXCL) and the file already + exists fail and set xn_getlasterror() to EEXIST + PO_TRUNC - Truncate the file if it already exists + PO_NOSHAREANY - Fail if the file is already open. If the open succeeds + no other opens will succeed until it is closed. + PO_NOSHAREWRITE- Fail if the file is already open for write. If the open + succeeds no other opens for write will succeed until it + is closed. + + Mode values are + + PS_IWRITE - Write permitted + PS_IREAD - Read permitted. (Always true anyway) + + Returns + Returns a non-negative integer to be used as a file descriptor for + calling read/write/seek/close otherwise it returns -1. + + errno is set to one of the following + 0 - No error + PENOENT - Not creating a file and file not found + PEMFILE - Out of file descriptors + PEINVALIDPATH - Invalid pathname + PENOSPC - No space left on disk to create the file + PEACCES - Is a directory or opening a read only file for write + PESHARE - Sharing violation on file opened in exclusive mode + PEEXIST - Opening for exclusive create but file already exists + PEEXIST - Opening for exclusive create but file already exists + An ERTFS system error +****************************************************************************/ + +PCFD po_open(byte *name, word flag, word mode) /*__apifn__*/ +{ + PCFD fd; + PC_FILE *pfile; + CLUSTERTYPE cluster; + DROBJ *parent_obj; + DROBJ *pobj; + byte *path; + byte *filename; + byte fileext[4]; + int driveno; + BOOLEAN open_for_write; + dword clusters_to_release; +#if (RTFS_SHARE) + BOOLEAN sharing_error; +#endif + dword ltemp; + int p_errno; + DDRIVE *pdrive; + CHECK_MEM(PCFD, -1) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* po_open: clear error status */ + +#if (RTFS_SHARE) + sharing_error = FALSE; +#endif + parent_obj = 0; + + open_for_write = FALSE; + p_errno = 0; + +#if (RTFS_WRITE) + /* We will need to know this in a few places. */ + if(flag & (PO_WRONLY|PO_RDWR)) + open_for_write = TRUE; +#endif + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(name); + if (driveno < 0) + { + /* errno was set by check_drive */ + return(-1); + } + if ( (fd = pc_allocfile()) < 0 ) /* Grab a file */ + { + release_drive_mount(driveno); /* Release lock, unmount if aborted */ + rtfs_set_errno(PEMFILE); + return(-1); + } + /* Get the FILE. This will never fail */ + pfile = prtfs_cfg->mem_file_pool+fd; + pdrive = pc_drno2dr(driveno); + + /* Use the buffers in the DRIVE structure. Access is locked via semaphore */ + path = (byte *)&(pdrive->pathname_buffer[0]); + filename = (byte *)&(pdrive->filename_buffer[0]); + + /* Get out the filename and d:parent */ + if (!pc_parsepath(path,filename,fileext,name)) + { + p_errno = PEINVALIDPATH; + goto errex; + } + + /* Find the parent */ + /* pc_fndnode will set errno */ + parent_obj = pc_fndnode(path); + if (!parent_obj) + goto errex; + + if (!pc_isadir(parent_obj) || pc_isavol(parent_obj)) + { + p_errno = PENOENT; /* Path is not a directory */ + goto errex; + } + + pobj = pc_get_inode(0, parent_obj,filename,(byte*)fileext, GET_INODE_MATCH); + if (pobj) + { + /* If we goto exit: we want them linked so we can clean up */ + pfile->pobj = pobj; /* Link the file to the object */ +#if (RTFS_SHARE) + /* check the sharing conditions */ + sharing_error = FALSE; + if (pobj->finode->opencount != 1) + { + /* The file is already open by someone. Lets see if we are + compatible */ + /* 1. We do not want to share with anyone */ + if (flag & PO_NOSHAREANY) + sharing_error = TRUE; + /* 2. Someone else does not want to share */ + if (pobj->finode->openflags & OF_EXCLUSIVE) + sharing_error = TRUE; + /* 3. We want exclusive write but already open for write */ + if ( open_for_write && (flag & PO_NOSHAREWRITE) && + (pobj->finode->openflags & OF_WRITE)) + sharing_error = TRUE; + /* 4. We want open for write but it is already open for + exclusive */ + if ( (open_for_write) && + (pobj->finode->openflags & OF_WRITEEXCLUSIVE)) + sharing_error = TRUE; + /* 5. Open for trunc when already open */ + if (flag & PO_TRUNC) + sharing_error = TRUE; + } + if (sharing_error) + { + p_errno = PESHARE; + goto errex; + } +#endif /* RTFS_SHARE */ + if( pc_isadir(pobj) || pc_isavol(pobj) ) + { + p_errno = PEACCES; /* is a directory */ + goto errex; + } +#if (RTFS_WRITE) + if ( (flag & (PO_EXCL|PO_CREAT)) == (PO_EXCL|PO_CREAT) ) + { + p_errno = PEEXIST; /* Exclusive fail */ + goto errex; + } + if(open_for_write && (pobj->finode->fattribute & ARDONLY) ) + { + p_errno = PEACCES; /* read only file */ + goto errex; + } + if (flag & PO_TRUNC) + { + cluster = pc_finode_cluster(pobj->pdrive,pobj->finode); + ltemp = pobj->finode->fsize; + /* calculate clusters to release. + add (pobj->pdrive->bytespcluster-1) to include if we are + not on a cluster boundary */ + clusters_to_release = ltemp + + (dword) (pobj->pdrive->bytespcluster-1); + clusters_to_release = clusters_to_release >> (int) (pobj->pdrive->log2_secpalloc+9); + + pc_pfinode_cluster(pobj->pdrive,pobj->finode,0); + pobj->finode->fsize = 0L; + + /* Convert to native. Overwrite the existing inode.Set archive/date */ + if (!pc_update_inode(pobj, TRUE, TRUE)) + { + /* pc_update_inode has set errno */ + pc_pfinode_cluster(pobj->pdrive,pobj->finode,cluster); + pobj->finode->fsize = ltemp; + goto errex; + } + + /* Release the chain. FATOP will set erno if needed + - set min and max the same so it must delete exactly + this many clusters + Set min to 0 and max to 0xffffffff to eliminate range checking on the + cluster chain and force removal of all clusters + If freechain fails and it is not because of an invalid cluster, we + let this pass and continue. Any other error is either internal or + an IO error. */ + if (!FATOP(pobj->pdrive)->fatop_freechain(pobj->pdrive,cluster,clusters_to_release, clusters_to_release) + && get_errno() != PEINVALIDCLUSTER) + { + goto errex; + } + /* Flush the FAT, bail out on any IO errors */ + else if (! FATOP(pobj->pdrive)->fatop_flushfat(pobj->pdrive->driveno) ) + goto errex; + } +#endif + } + else /* File not found */ + { + if (get_errno() != PENOENT) + goto errex; +#if (RTFS_WRITE) + if (!(flag & PO_CREAT)) + { + /* pc_get_inode() has set errno to PENOENT or to an internal or IO error status */ + goto errex; /* File does not exist */ + } + rtfs_set_errno(0); /* Clear PENOENT */ + /* Do not allow create if write bits not set */ + if(!open_for_write) + { + p_errno = PEACCES; /* read only file */ + goto errex; + } + /* Create for read only if write perm not allowed */ + pobj = pc_mknode( parent_obj, filename, fileext, (byte) ((mode == PS_IREAD) ? ARDONLY : 0), 0); + if (!pobj) + { + /* pc_mknode has set errno */ + goto errex; + } + + pfile->pobj = pobj; /* Link the file to the object */ +#else /* Write not built in. Get out errno already set */ + goto errex; +#endif + } + /* Set the file sharing flags in the shared finode structure */ + /* clear flags if we just opened it . */ + if (pobj->finode->opencount == 1) + pobj->finode->openflags = 0; + + if (flag & PO_BUFFERED) + pobj->finode->openflags |= OF_BUFFERED; + +#if (RTFS_WRITE) + if (open_for_write) + { + pobj->finode->openflags |= OF_WRITE; + if (flag & PO_NOSHAREWRITE) + pobj->finode->openflags |= OF_WRITEEXCLUSIVE; + } + if (flag & PO_NOSHAREANY) + pobj->finode->openflags |= OF_EXCLUSIVE; + +#endif + pfile->flag = flag; /* Access flags */ + pfile->fptr = 0L; /* File pointer */ + + /* Set the cluster and block file pointers */ + if (!_synch_file_ptrs(pfile)) + goto errex; + + p_errno = 0; + if (parent_obj) + pc_freeobj(parent_obj); + + if (!release_drive_mount_write(driveno))/* Release lock, unmount if aborted */ + return(-1); + return(fd); +errex: + pc_freefile(pfile); + if (parent_obj) + pc_freeobj(parent_obj); + if (p_errno) + rtfs_set_errno(p_errno); + release_drive_mount_write(driveno);/* Release lock, unmount if aborted */ + return(-1); +} + + +/**************************************************************************** +PO_READ - Read from a file. + + Description + Attempt to read count bytes from the current file pointer of file at fd + and put them in buf. The file pointer is updated. + +Returns +Returns the number of bytes read or -1 on error. + +errno is set to one of the following +0 - No error +PEBADF - Invalid file descriptor +PEIOERRORREAD - Read error +An ERTFS system error +*****************************************************************************/ + + +int po_read(PCFD fd, byte *in_buff, int count) /*__apifn__*/ +{ + PC_FILE *pfile; + DDRIVE *pdrive; + word block_in_cluster; + word byte_offset_in_block; + dword n_bytes, block_to_read, n_read,ltemp, n_left, n_to_read, n_w_to_read; + CLUSTERTYPE next_cluster, n_clusters; + int p_errno, ret_val; + int end_of_chain; + CHECK_MEM(int, -1) /* Make sure memory is initted */ + + + ret_val = p_errno = 0; + rtfs_set_errno(0); /* po_read: clear errno */ + + /* return 0 bytes read if bad arguments */ + if (!count) + goto return_unlocked; + /* Get the file structure and semaphore lock the drive */ + if ( (pfile = pc_fd2file(fd, 0)) == 0) + { /* fd2file set errno */ + ret_val = -1; + goto return_unlocked; + } + pdrive = pfile->pobj->pdrive; + + if (pfile->fptr >= pfile->pobj->finode->fsize) /* Dont read if done */ + { + ret_val = 0; + goto return_locked; + } + /* Set the cluster and block file pointers if not already set */ + if (!_synch_file_ptrs(pfile)) + { /* _synch_file_ptrs set errno */ + ret_val = -1; + goto return_locked; + } + + /* Check if this write will wrap past 4 Gigabytes + if so truncate the count (size limitted to 4 gig - 1 */ + ltemp = pfile->fptr + count; + if (ltemp < pfile->fptr) + { + ltemp = 0xffffffff; + count = ltemp - pfile->fptr; + } + ltemp = (dword) count; + /* Truncate the read count if we need to */ + if ( (pfile->fptr + ltemp) >= pfile->pobj->finode->fsize) + ltemp = pfile->pobj->finode->fsize - pfile->fptr; + + n_left = ltemp; + n_read = 0; + + while (n_left) + { + block_in_cluster = (word) (pfile->fptr & pdrive->byte_into_cl_mask); + block_in_cluster >>= 9; + block_to_read = pfile->fptr_block + block_in_cluster; + + /* how many clusters are left */ + n_to_read = (n_left + 511) >> 9; + n_clusters = (CLUSTERTYPE) ((n_to_read+pdrive->secpalloc-1) >> pdrive->log2_secpalloc); + + /* how many contiguous clusters can we get ? <= n_clusters */ + end_of_chain = 0; + n_clusters = FATOP(pdrive)->fatop_get_chain(pdrive, pfile->fptr_cluster, + &next_cluster, n_clusters, &end_of_chain); + + /* Get_chain sets errno to PEDEVICE on fat access error PERESOURCE + for bad cluster values */ + if (!n_clusters) + { + /* get_chain already set IO error */ + ret_val = (int) -1; + goto return_locked; + } + + /* Are we inside a block */ + if ( (pfile->fptr & 0x1ffL) || (n_left < 512) ) + { + /* pc_load_file_buffer will write the file buffer if it's dirty */ + if (!pc_load_file_buffer(pfile, block_to_read)) + break; /* pc_load_file_buffer has set errno */ + byte_offset_in_block = (word) (pfile->fptr & 0x1ffL); + /* Copy source data to the local buffer */ + n_bytes = (512 - byte_offset_in_block); + if (n_bytes > n_left) + n_bytes = n_left; + if (in_buff) + copybuff(in_buff, &(pfile->pobj->finode->pfile_buffer->data[byte_offset_in_block]), (int)n_bytes); + /* If file is not buffered across calls, kill the buffer */ + if (!(pfile->pobj->finode->openflags & OF_BUFFERED)) + pc_load_file_buffer(pfile, 0); + + if (in_buff) + in_buff += n_bytes; + n_left = n_left - n_bytes; + pfile->fptr += n_bytes; + n_read += n_bytes; + + /* Are we on a cluster boundary ? */ + if (!(pfile->fptr & pdrive->byte_into_cl_mask)) + { + if (--n_clusters) /* If contiguous */ + { + pfile->fptr_block += pdrive->secpalloc; + pfile->fptr_cluster += (CLUSTERTYPE)1; + } + else + { + /* Check for corrupted file + We are about to advancing fptr_cluster by + making next_cluster the current cluster. + If the file pointer is less than the current file + size, but we are at the end of chain we know + that there is no next_cluster and the chain is + corrupted. It shorter than the file size indicates. + Reset the byte pointer to match the current + block and cluster pointers, set errno to + PEINVALIDCLUSTER, return -1 */ + if (pfile->fptr < pfile->pobj->finode->fsize && end_of_chain) + { + pfile->fptr -= n_bytes; + p_errno = PEINVALIDCLUSTER; + ret_val = -1; + goto return_locked; + } + else + { + pfile->fptr_cluster = next_cluster; + pfile->fptr_block = pc_cl2sector(pdrive, next_cluster); + } + } /* if (--nclusters) {} else {}; */ + } /* if (!(pfile->fptr & pdrive->byte_into_cl_mask)) */ + } /* if ( (pfile->fptr & 0x1ff) || (n_left < 512) ) */ + else + { + /* Read as many blocks as possible */ + /* How many blocks in the current chain */ + n_to_read = n_clusters << pdrive->log2_secpalloc; + /* subtract out any leading blocks */ + n_to_read = n_to_read - block_in_cluster; + /* How many blocks yet to read */ + ltemp = n_left >> 9; + /* take the smallest of the two */ + if (n_to_read > ltemp) + n_to_read = ltemp; + + if (n_to_read) + { + /* If we get here we need to read contiguous blocks */ + block_to_read = pfile->fptr_block + block_in_cluster; + ltemp = n_to_read; + while (ltemp) + { + if (ltemp > 0xffff) + n_w_to_read = 0xffff; + else + n_w_to_read = ltemp & 0xffff; + /* Flush the file block buffer if it was in our range */ + /* And then read the data */ + pdrive->drive_flags |= DRIVE_FLAGS_FILEIO; + if (!pc_sync_file_buffer(pfile, block_to_read, n_w_to_read, TRUE) || + (in_buff && !devio_read(pdrive->driveno, block_to_read, in_buff, (word)n_w_to_read, FALSE))) + { + pdrive->drive_flags &= ~DRIVE_FLAGS_FILEIO; + /* set errno to IO error unless devio set PEDEVICE */ + if (!get_errno()) + p_errno = PEIOERRORREAD; + ret_val = (int) n_read; + goto return_locked; + } + pdrive->drive_flags &= ~DRIVE_FLAGS_FILEIO; + n_bytes = n_w_to_read << 9; + if (in_buff) + in_buff += n_bytes; + block_to_read += n_w_to_read; + ltemp -= n_w_to_read; + } + n_bytes = n_to_read << 9; + n_left= n_left - n_bytes; + pfile->fptr += n_bytes; + n_read += n_bytes; + + /* if we advanced to a cluster boundary advance the + cluster pointer */ + /* ltemp ==s how many clusters we read */ + ltemp = (n_to_read+block_in_cluster) >> pdrive->log2_secpalloc; + + if (ltemp == n_clusters) + { + /* Check for corrupted file + We are about to advance fptr_cluster by + making next_cluster the current cluster. + If the file pointer is less than the current file + size, but we are at the end of chain we know + that there is no next_cluster and the chain is + corrupted. It shorter than the file size indicates. + Reset the byte pointer to match the current + block and cluster pointers, set errno to + PEINVALIDCLUSTER, return -1 */ + if (pfile->fptr < pfile->pobj->finode->fsize && end_of_chain) + { + pfile->fptr -= n_bytes; + p_errno = PEINVALIDCLUSTER; + ret_val = -1; + goto return_locked; + } + else + { + /* Changed Oct 25, 2005. Set eof flag if + the file pointer is at the ens of file */ + if (pfile->fptr == pfile->pobj->finode->fsize) + { + pfile->at_eof = TRUE; + } + pfile->fptr_cluster = next_cluster; + } + } + else + { + /* advance the pointer as many as we read */ + pfile->fptr_cluster = (CLUSTERTYPE) (pfile->fptr_cluster + ltemp); + } + pfile->fptr_block = pc_cl2sector(pdrive, pfile->fptr_cluster); + } + } + } /* while n_left */ + ret_val = (int) n_read; + +return_locked: + release_drive_mount(pdrive->driveno);/* Release lock, unmount if aborted */ +return_unlocked: + if (p_errno) + rtfs_set_errno(p_errno); + return(ret_val); +} + +/************************************************************************** +PO_LSEEK - Move file pointer + + Description + Move the file pointer offset bytes from the origin described by + origin. The file pointer is set according to the following rules. + + Origin Rule + PSEEK_SET offset from begining of file + PSEEK_CUR offset from current file pointer + PSEEK_END offset from end of file + + Attempting to seek beyond end of file puts the file pointer one + byte past eof. + + Returns + Returns the new offset or -1 on error. + + errno is set to one of the following + 0 - No error + PEBADF - Invalid file descriptor + PEINVALIDPARMS - Attempt to seek past EOF or to a negative offset + PEINVALIDCLUSTER- Files contains a bad cluster chain + An ERTFS system error +*****************************************************************************/ + +long po_lseek(PCFD fd, long offset, int origin) /*__apifn__*/ +{ + PC_FILE *pfile; + DDRIVE *pdrive; + long ret_val; + CHECK_MEM(long, -1) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* po_lseek: clear error status */ + /* Get the file structure and semaphore lock the drive */ + pfile = pc_fd2file(fd, 0); + if (!pfile) + { /* fd2file set errno */ + return(-1L); + } + + pdrive = pfile->pobj->pdrive; + /* Grab exclusive access to the drobj */ + /* + * Note the files finode is LOCKED below so the code need not be + * reentrant relative to the finode. + */ + /* Set the cluster and block file pointers if not already set */ + if (_synch_file_ptrs(pfile)) + { /* _synch_file_ptrs set errno */ + /* Call the internal seek routine that we share with po_trunc */ + ret_val = _po_lseek(pfile, offset, origin); + } + else + ret_val = -1; + + release_drive_mount(pdrive->driveno);/* Release lock, unmount if aborted */ + return(ret_val); +} + +/************************************************************************** +PO_ULSEEK - Move file pointer (unsigned) + + BOOLEAN po_ulseek(PCFD fd, dword offset, dword *pnew_offset, int origin) + + Description + Move the file pointer offset bytes from the origin described by + origin. The file pointer is set according to the following rules. + + Origin Rule + PSEEK_SET Positive unsigned 32 bit offset from begining of file + PSEEK_CUR Positive unsigned 32 bit offset from current file pointer + PSEEK_CUR_NEG Unsigned 32 bit offset subtracted from current file pointer + PSEEK_END Unsigned 32 bit offset subtracted from current file end + + + The new file pointer is returned in *pnew_offset + + Returns + TRUE - The seek was succesful and the new offset is in *pnew_offset. + FALSE - An error occured + + If FALSE is returned, errno is set to one of the following + + 0 - No error + PEBADF - Invalid file descriptor + PEINVALIDPARMS - Attempt to seek past EOF or to a negative offset + PEINVALIDCLUSTER- Files contains a bad cluster chain + An ERTFS system error +*****************************************************************************/ + +BOOLEAN po_ulseek(PCFD fd, dword offset, dword *pnew_offset, int origin) /*__apifn__*/ +{ + PC_FILE *pfile; + DDRIVE *pdrive; + BOOLEAN ret_val; + CHECK_MEM(BOOLEAN, FALSE) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* po_lseek: clear error status */ + /* Get the file structure and semaphore lock the drive */ + pfile = pc_fd2file(fd, 0); + if (!pfile) + { /* fd2file set errno */ + return(FALSE); + } + + pdrive = pfile->pobj->pdrive; + /* Grab exclusive access to the drobj */ + /* + * Note the files finode is LOCKED below so the code need not be + * reentrant relative to the finode. + */ + /* Set the cluster and block file pointers if not already set */ + if (_synch_file_ptrs(pfile)) + { /* _synch_file_ptrs set errno */ + /* Call the internal seek routine that we share with po_trunc */ + ret_val = _po_ulseek(pfile, offset, pnew_offset, origin); + } + else + ret_val = FALSE; + + release_drive_mount(pdrive->driveno);/* Release lock, unmount if aborted */ + return(ret_val); +} + +/************************************************************************** +_PO_ULSEEK - Move file pointer (internal) + +Description +Behaves as po_lseek but takes a file instead of a file descriptor. + + Attempting to seek beyond end of file puts the file pointer one + byte past eof. + + All setting up such as drive_enter and drobj_enter should have been done + before calling here. + + Called By: + + po_lseek and po_truncate. + + Returns + Returns the new offset or -1 on error. + + If the return value is -1 xn_getlasterror() will be set with one of the following: + + PENBADF - File descriptor invalid + PEINVAL - Seek to negative file pointer attempted. + +*****************************************************************************/ +/* +* Note: when this routine is caled the files finode is LOCKED so the code +* need not be reentrant relative to the finode. +*/ + + + + +BOOLEAN _po_ulseek(PC_FILE *pfile, dword offset, dword *new_offset, int origin) /*__fn__*/ +{ + dword file_pointer; + DDRIVE *pdrive; + BOOLEAN l_at_eof; + int log2_bytespcluster; + dword ltemp; + dword ltemp2; + CLUSTERTYPE n_clusters_to_seek; + CLUSTERTYPE n_clusters; + CLUSTERTYPE first_cluster, next_cluster; + dword ret_val; + dword alloced_size; + int end_of_chain; + int p_errno; + + p_errno = 0; + + pdrive = pfile->pobj->pdrive; + /* If file is zero sized. we are there */ + if (!(pfile->pobj->finode->fsize)) + { + *new_offset = 0; + ret_val = TRUE; + goto errex; + } + *new_offset = pfile->fptr; + ret_val = FALSE; + + if (origin == PSEEK_SET) /* offset from begining of file */ + file_pointer = offset; + else if (origin == PSEEK_CUR) /* offset from current file pointer */ + { + file_pointer = pfile->fptr; + file_pointer += offset; + /* See if it wraps past 4 Gig.. if so stop it at 4 gig */ + if (file_pointer < pfile->fptr) + file_pointer = 0xffffffff; + } + else if (origin == PSEEK_CUR_NEG) /* offset from current file pointer */ + { + file_pointer = pfile->fptr; + if (file_pointer > offset) + file_pointer -= offset; + else + file_pointer = 0; + + } + else if (origin == PSEEK_END) /* offset from end of file */ + { + file_pointer = pfile->pobj->finode->fsize; + if (file_pointer > offset) + file_pointer -= offset; + else + file_pointer = 0; + } + else /* Illegal origin */ + { + p_errno = PEINVALIDPARMS; + goto errex; + } + + if (file_pointer >= pfile->pobj->finode->fsize) + { + file_pointer = pfile->pobj->finode->fsize; + + /* If seeking to the end of file see if we are beyond the allocated size of + the file. If we are we set the at_eof flag so we know to try to move the + cluster pointer in case another file instance extends the file + Round the file size up to its cluster size by adding in clustersize-1 + and masking off the low bits */ + alloced_size = (pfile->pobj->finode->fsize + pdrive->byte_into_cl_mask) & + ~(pdrive->byte_into_cl_mask); + if (alloced_size < pfile->pobj->finode->fsize) + alloced_size = 0xffffffff; + + /* If the file pointer is beyond the space allocated to the file note it + since we may need to adjust this files cluster and block pointers + later if someone else extends the file behind our back */ + if (file_pointer >= alloced_size) + l_at_eof = TRUE; + else + l_at_eof = FALSE; + } + else + { + l_at_eof = FALSE; + } + + + log2_bytespcluster = (int) (pdrive->log2_secpalloc + 9); + + /* How many clusters do we need to seek */ + /* use the current cluster as the starting point if we can */ + if (file_pointer >= pfile->fptr) + { + first_cluster = pfile->fptr_cluster; + + ltemp = file_pointer; + + ltemp >>= log2_bytespcluster; + ltemp2 = pfile->fptr >> log2_bytespcluster; + /* If this algorithm was run twice n_clusters_to_seek would + end up -1. Which would still work but spin 64K times. + Thanks top Morgan Woodson of EMU systems for the patch */ + if (ltemp >= ltemp2) + n_clusters_to_seek = (CLUSTERTYPE) (ltemp - ltemp2); + else + n_clusters_to_seek = (word) 0; + } + else + { + /* seek from the beginning */ + first_cluster = pc_finode_cluster(pdrive,pfile->pobj->finode); + ltemp = file_pointer >> log2_bytespcluster; + n_clusters_to_seek = (CLUSTERTYPE) ltemp; + } + next_cluster = first_cluster; /* Set next cluster to first cluster so if n_clusters_to_seek is zero it's harmless. */ + while (n_clusters_to_seek) + { + end_of_chain = 0; + n_clusters = FATOP(pdrive)->fatop_get_chain(pdrive, first_cluster, + &next_cluster, n_clusters_to_seek, &end_of_chain); + if (!n_clusters) /* get_chain stops when it reaches n_clusters_to_seek */ + { + /* get_chain already set errno */ + ret_val = FALSE; + goto errex; + } + /* Check for corrupted file + If first cluster == next cluster it means that we started at + the last cluster in the chain and tried to seek foreward. This + indicates that the file length in the directory entry is longer + than the actual cluster chain. Check is not valid if the + fointer is at the EOF mark. */ + if (!l_at_eof && first_cluster == next_cluster) + { + p_errno = PEINVALIDCLUSTER; + ret_val = FALSE; + goto errex; + } + + /* How many left to seek */ + n_clusters_to_seek = (CLUSTERTYPE) (n_clusters_to_seek - n_clusters); + /* Check for corrupted file + If there are more clusters to seek but the cluster pointer + is at the end this means that the file size is + longer than the actual chain length so return + erro leave the byte block and cluster pointers alone + and set errno to PEINVALIDCLUSTER */ + if (n_clusters_to_seek && end_of_chain) + { + p_errno = PEINVALIDCLUSTER; + ret_val = FALSE; + goto errex; + } + else + first_cluster = next_cluster; + } + + pfile->fptr_cluster = next_cluster; + pfile->fptr_block = pc_cl2sector(pdrive, next_cluster); + pfile->fptr= file_pointer; + pfile->at_eof = l_at_eof; + + *new_offset = pfile->fptr; + ret_val = TRUE; +errex: + /* No only errors return through here. Everything does. */ + if (p_errno) + rtfs_set_errno(p_errno); + return(ret_val); +} +/************************************************************************** +_PO_LSEEK - Move file pointer (internal) + +Description +Behaves as po_lseek but takes a file instead of a file descriptor. + + Attempting to seek beyond end of file puts the file pointer one + byte past eof. + + All setting up such as drive_enter and drobj_enter should have been done + before calling here. + + Called By: + + po_lseek and po_truncate. + + Returns + Returns the new offset or -1 on error. + + If the return value is -1 xn_getlasterror() will be set with one of the following: + + PENBADF - File descriptor invalid + PEINVAL - Seek to negative file pointer attempted. + +*****************************************************************************/ +/* +* Note: when this routine is caled the files finode is LOCKED so the code +* need not be reentrant relative to the finode. +*/ + + +long _po_lseek(PC_FILE *pfile, long offset, int origin) /*__fn__*/ +{ + long ret_val; + int u_origin; + dword new_offset/*, u_offset*/; + + /*u_offset = offset;*/ + u_origin = origin; + + if (origin == PSEEK_CUR) /* offset from current file pointer */ + { + if (offset < 0) + { + offset = -offset; + /*u_offset = (dword) offset;*/ + u_origin = PSEEK_CUR_NEG; + } + } + else if (origin == PSEEK_END) /* offset from end of file */ + { + if (offset < 0) + { + offset = -offset; + /*u_offset = (dword) offset;*/ + } + } + if (!_po_ulseek(pfile, offset, &new_offset, u_origin)) + ret_val = -1L; + else + ret_val = (long) new_offset; + return(ret_val); +} + +/**************************************************************************** +PO_CLOSE - Close a file. + + Description + Close the file updating the disk and freeing all core associated with FD. + +Returns +Returns 0 if all went well otherwise it returns -1. + +errno is set to one of the following +0 - No error +PEBADF - Invalid file descriptor +An ERTFS system error +****************************************************************************/ + +int po_close(PCFD fd) /*__apifn__*/ +{ + PC_FILE *pfile; + int ret_val; + int driveno; + int p_errno; + CHECK_MEM(int, -1) /* Make sure memory is initted */ + + p_errno = 0; + ret_val = 0; + rtfs_set_errno(0); /* po_close() - clear errno */ + + /* Get the file structure and semaphore lock the drive */ + if ( (pfile = pc_fd2file(fd, 0)) == 0) + { + /* Add a check here to see if fd2file failed because it was + closed by pc_dskfree. If so we mark the file free here and + return success */ + if (get_errno() == PECLOSED) + { + OS_CLAIM_FSCRITICAL() + pfile = prtfs_cfg->mem_file_pool+fd; + pfile->is_free = TRUE; + OS_RELEASE_FSCRITICAL() + rtfs_set_errno(0); /* clear errno */ + return (0); + } + /* all other errors. fd2file set errno */ + return(-1); + } + else + { + driveno = pfile->pobj->pdrive->driveno; + +#if (RTFS_WRITE) + if (pfile->flag & ( PO_RDWR | PO_WRONLY ) ) + { + if (!_po_flush(pfile)) + { + /* _po_flush has set errno */ + ret_val = -1; + } + } +#endif + /* Release the FD and its core */ + pc_freefile(pfile); + if (!release_drive_mount_write(driveno))/* Release lock, unmount if aborted */ + return (-1); + } + if (p_errno) + rtfs_set_errno(p_errno); + return(ret_val); +} + +/**************************************************************************** +Miscelaneous File and file descriptor management functions + +These functions are private functions used by the po_ file io routines. + + pc_fd2file - + Map a file descriptor to a file structure. Return null if the file is + not open. If an error has occured on the file return NULL unless + allow_err is true. + + pc_allocfile - + Allocate a file structure an return its handle. Return -1 if no more + handles are available. + + pc_freefile - + Free all core associated with a file descriptor and make the descriptor + available for future calls to allocfile. + + pc_free_all_fil - +*****************************************************************************/ + +/* Map a file descriptor to a file structure. Return null if the file is +not open or the flags do not match (test for write access if needed). +Get the file structure and semaphore lock the drive +*/ + +PC_FILE *pc_fd2file(PCFD fd,int flags) /*__fn__*/ +{ + PC_FILE *pfile; + DDRIVE *pdrive; + + /* Get the file and associated drive structure with the crit sem locked */ + if (0 <= fd && fd < pc_nuserfiles()) + { + pfile = prtfs_cfg->mem_file_pool+fd; + OS_CLAIM_FSCRITICAL() + if (!pfile->is_free) + { + if (!pfile->pobj) + { + /* An event (probably card removal or failure) + closed the file. Set errno and return. The + user must call po_close to clear this condition */ + rtfs_set_errno(PECLOSED); + } + /* If flags == 0. Any access allowed. Otherwise at least one + bit in the file open flags must match the flags sent in */ + else if (!flags || (pfile->flag&flags)) + { + /* dereference pobj while critical semaphore is still locked */ + pdrive = pfile->pobj->pdrive; + OS_RELEASE_FSCRITICAL() + /* Claim the drive, double check that the file is still + good (was not closed out by a pc_dskfree() call) */ + OS_CLAIM_LOGDRIVE(pdrive->driveno) /* pc_fd2file - Register Drive in use */ + if (pfile->pobj) + return(pfile); + else + { + /* An event (probably card removal or failure) + closed the file. Set errno and return. The + user must call po_close to clear this condition */ + OS_RELEASE_LOGDRIVE(pdrive->driveno) /* pc_fd2file - clear Drive on error */ + rtfs_set_errno(PECLOSED); + return(0); + } + } + else + rtfs_set_errno(PEACCES); + } + else + rtfs_set_errno(PEBADF); + OS_RELEASE_FSCRITICAL() + } + else + rtfs_set_errno(PEBADF); + return(0); +} + +/* Assign zeroed out file structure to an FD and return the handle. Return +-1 on error. */ +PCFD pc_allocfile(void) /*__fn__*/ +{ + PC_FILE *pfile; + PCFD i; + + OS_CLAIM_FSCRITICAL() + pfile = prtfs_cfg->mem_file_pool; + for (i=0;iis_free) + { + rtfs_memset(pfile, 0, sizeof(PC_FILE)); + OS_RELEASE_FSCRITICAL() + return(i); + } + } + OS_RELEASE_FSCRITICAL() + return (-1); +} + +/* Free core associated with a file descriptor. Release the FD for later use */ +static void pc_freefile(PC_FILE *pfile) +{ +DROBJ *pobj; + OS_CLAIM_FSCRITICAL() + pobj = pfile->pobj; + pfile->is_free = TRUE; + OS_RELEASE_FSCRITICAL() + if (pobj) + pc_freeobj(pobj); +} + + +#define ENUM_FLUSH 1 +#define ENUM_TEST 2 +#define ENUM_FREE 3 + +/* Release all file descriptors associated with a drive and free up all core +associated with the files called by pc_free_all_fil, pc_flush_all_fil, +pc_test_all_fil. The drive semaphore must be locked before this call is +entered. +*/ +int pc_enum_file(DDRIVE *pdrive, int chore) /*__fn__*/ +{ + PC_FILE *pfile; + DROBJ *pobj; + PCFD i; + int dirty_count; + + dirty_count = 0; + for (i=0; i < pc_nuserfiles(); i++) + { + OS_CLAIM_FSCRITICAL() + pfile = prtfs_cfg->mem_file_pool+i; + if (pfile && !pfile->is_free && pfile->pobj && pfile->pobj->pdrive == pdrive) + { + OS_RELEASE_FSCRITICAL() +#if (RTFS_WRITE) + if (chore == ENUM_FLUSH) + { + if (!_po_flush(pfile)) + return(-1); + } +#endif + if (chore == ENUM_TEST) + { + if (pfile->needs_flush) + dirty_count += 1; + } + if (chore == ENUM_FREE) + { + /* Mark the file closed here. po_close must release it */ + OS_CLAIM_FSCRITICAL() + pobj = pfile->pobj; + pfile->pobj = 0; + OS_RELEASE_FSCRITICAL() + if (pobj) + pc_freeobj(pobj); + } + } + else + { + OS_RELEASE_FSCRITICAL() + } + } + return(dirty_count); +} + +/* Release all file descriptors associated with a drive and free up all core +associated with the files called by dsk_close */ +void pc_free_all_fil(DDRIVE *pdrive) /*__fn__*/ +{ + pc_enum_file(pdrive, ENUM_FREE); +} + +/* Flush all files on a drive */ +BOOLEAN pc_flush_all_fil(DDRIVE *pdrive) /*__fn__*/ +{ + if (pc_enum_file(pdrive, ENUM_FLUSH) == 0) + return(TRUE); + else + return(FALSE); +} + +/* Test the dirty flag for all files */ +int pc_test_all_fil(DDRIVE *pdrive) /*__fn__*/ +{ + return(pc_enum_file(pdrive, ENUM_TEST)); +} + + + +/* Synchronize file pointers. Read write Seek and close all call here. +This fixes the following BUGS: +1. If a file is created and left open and then opened again with a new +file handle before any writing takes place. Neither file will get +its fptr_cluster set correctly initially. The first one to write +would get set up correctly but the other would not. Thus if fptr_cluster +is zero we see if we can set it. +2. If one file seeked to the end of the file or has written to the end of +the file its file pointer will point beyond the last cluster in the +chain, the next call to write will notice the fptr is beyond the +file size and extend the file by allocating a new cluster to the +chain. During this time the cluster/block and byte offsets are +out of synch. If another instance extends the file during this time +the next call to write will miss this condition since fptr is not +>= fsize any more. To fix this we note in the file when this +condition is true AND, afterwards each time we work with the file +we see if the file has grown and adjust the cluster pointer and block +pointer if needed. +*/ +/* +* Note: The finode owned by the file is always locked when this routine is +* called so the routine does not need to be reentrant with respect to +* the finode. Note too that pfile is not a shared structure so the +* routine does not have to be reentrant with respect to it either. +*/ + +BOOLEAN _synch_file_ptrs(PC_FILE *pfile) /*__fn__*/ +{ + CLUSTERTYPE clno; + + if (!pfile->fptr_cluster) + { + pfile->fptr_cluster = pc_finode_cluster(pfile->pobj->pdrive,pfile->pobj->finode); + + if (pfile->fptr_cluster) + pfile->fptr_block = pc_cl2sector(pfile->pobj->pdrive, pfile->fptr_cluster); + else + pfile->fptr_block = 0; + } + if (pfile->at_eof) + { + if (pfile->fptr_cluster) + { + clno = FATOP(pfile->pobj->pdrive)->fatop_clnext(pfile->pobj->pdrive, pfile->fptr_cluster); + if (clno == 0) /* clnext detected an error */ + return(FALSE); + else if (clno != 0xffffffff) + { + pfile->fptr_cluster = clno; + pfile->fptr_block = pc_cl2sector(pfile->pobj->pdrive, pfile->fptr_cluster); + pfile->at_eof = FALSE; + } + } + } + return(TRUE); +} + +/* pc_flush_file_buffer(PC_FILE *pfile) - +* If the finode structure that the file points to contains a file buffer +* that has been modified, but not written to disk, write the data to disk. +*/ + +BOOLEAN pc_flush_file_buffer(PC_FILE *pfile) +{ +BLKBUFF *pfile_buffer; + pfile_buffer = pfile->pobj->finode->pfile_buffer; + /* write it if it changed */ + if (pfile_buffer && pfile->pobj->finode->file_buffer_dirty) + { + dword save_drive_filio; + save_drive_filio = pfile_buffer->pdrive->drive_flags & DRIVE_FLAGS_FILEIO; + pfile_buffer->pdrive->drive_flags |= DRIVE_FLAGS_FILEIO; + if (!(devio_write(pfile_buffer->pdrive->driveno,pfile_buffer->blockno, + pfile_buffer->data, (int) 1, FALSE) )) + { + if (!save_drive_filio) + pfile_buffer->pdrive->drive_flags &= ~DRIVE_FLAGS_FILEIO; + /* set errno to IO error unless devio set PEDEVICE */ + if (!get_errno()) + rtfs_set_errno(PEIOERRORWRITEBLOCK); /* device write error */ + return(FALSE); + } + if (!save_drive_filio) + pfile_buffer->pdrive->drive_flags &= ~DRIVE_FLAGS_FILEIO; + /* Clear dirty condition */ + pfile->pobj->finode->file_buffer_dirty = 0; + } + return(TRUE); +} + +/* pc_load_file_buffer(PC_FILE *pfile, dword new_blockno) +* +* If new_blockno is 0, and the finode structure that the file points to +* contains a file buffer, then flush and then discard the file_buffer. +* If new_blockno is not 0, and the finode structure that the file points to +* contains a file buffer, then flush and then load the file_buffer with the +* contents at new_blockno. +* +*/ + + +BOOLEAN pc_load_file_buffer(PC_FILE *pfile, dword new_blockno) +{ +BLKBUFF *pfile_buffer; + + pfile_buffer = pfile->pobj->finode->pfile_buffer; + if (pfile_buffer) + { + /* See if we already have it */ + if (pfile_buffer->blockno == new_blockno) + return(TRUE); + /* If not, write it if it changed */ + if (!pc_flush_file_buffer(pfile)) + { + pfile->pobj->finode->pfile_buffer = 0; + pc_free_scratch_blk(pfile_buffer); + return(FALSE); + } + pfile->pobj->finode->pfile_buffer = 0; + pc_free_scratch_blk(pfile_buffer); + } + if (new_blockno) + { + dword save_drive_filio; + pfile_buffer = pc_scratch_blk(); + if (!pfile_buffer) + return(FALSE); /* pc_scratch_blk set errno */ + pfile_buffer->blockno = new_blockno; + pfile_buffer->pdrive = pfile->pobj->pdrive; + save_drive_filio = pfile_buffer->pdrive->drive_flags & DRIVE_FLAGS_FILEIO; + pfile_buffer->pdrive->drive_flags |= DRIVE_FLAGS_FILEIO; + if (!(devio_read(pfile_buffer->pdrive->driveno,pfile_buffer->blockno, + pfile_buffer->data, (int) 1, FALSE) )) + { + if (!save_drive_filio) + pfile_buffer->pdrive->drive_flags &= ~DRIVE_FLAGS_FILEIO; + /* set errno to IO error unless devio set PEDEVICE */ + if (!get_errno()) + rtfs_set_errno(PEIOERRORREADBLOCK); /* device read error */ + pfile->pobj->finode->pfile_buffer = 0; + pc_free_scratch_blk(pfile_buffer); + return(FALSE); + } + if (!save_drive_filio) + pfile_buffer->pdrive->drive_flags &= ~DRIVE_FLAGS_FILEIO; + pfile->pobj->finode->pfile_buffer = pfile_buffer; + } + return(TRUE); +} + +/* pc_sync_file_buffer(PC_FILE *pfile, +* dword start_block, dword nblocks, BOOLEAN doflush) +* If the finode structure that the file points to contains a file buffer +* that is in the range of start_block to start_block+nblocks, +* Then: +* If doflush is true and the buffer has been modified, write the data +* to disk. +* Discard the buffer +*/ +BOOLEAN pc_sync_file_buffer(PC_FILE *pfile, dword start_block, dword nblocks, BOOLEAN doflush) +{ +BLKBUFF *pfile_buffer; + pfile_buffer = pfile->pobj->finode->pfile_buffer; + if (pfile_buffer) + { + if (pfile_buffer->blockno >= start_block && pfile_buffer->blockno < (start_block+nblocks)) + { + /* If not flushing make sure dirty status is clear so we don't write */ + if (!doflush) + pfile->pobj->finode->file_buffer_dirty = 0; + /* discard buffer, pc_load_file_buffer will write it if it's dirty */ + return(pc_load_file_buffer(pfile, 0)); + } + } + return(TRUE); +} diff --git a/build/libraries/fatfs/ARM7/apifilmv.c b/build/libraries/fatfs/ARM7/apifilmv.c new file mode 100644 index 0000000..23c22cd --- /dev/null +++ b/build/libraries/fatfs/ARM7/apifilmv.c @@ -0,0 +1,292 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIFILEMV.C - Contains user api level source code. + + The following routines are included: + + pc_mv - Rename a file. + pc_unlink - Delete a file. +*/ + +#include + +/*************************************************************************** + PC_MV - Rename a file. + + Description + Renames the file in path (name) to newname. Fails if name is invalid, + newname already exists or path not found. + + 01-07-99 - Rewrote to support moving files between subdirectories + no longer supports renaming subdirectories or volumes + Returns + Returns TRUE if the file was renamed. Or no if the name not found. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid or they are not the same + PEINVALIDPATH - Path specified by old_name or new_name is badly formed. + PEACCESS - File or directory in use, or old_name is read only + PEEXIST - new_name already exists + An ERTFS system error +***************************************************************************/ + +/* Rename a file */ +BOOLEAN pc_mv(byte *old_name, byte *new_name) /*__apifn__*/ +{ + int old_driveno; + DROBJ *old_obj; + DROBJ *old_parent_obj; + byte *path; + byte *filename; + byte fileext[4]; + int new_driveno; + DROBJ *new_obj; + DROBJ *new_parent_obj; + BOOLEAN ret_val; + CLUSTERTYPE cluster; + DDRIVE *pdrive; + + int p_errno; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + p_errno = 0; + + /* Drives must be the same */ + if ( !pc_parsedrive( &old_driveno, old_name ) || + !pc_parsedrive( &new_driveno, new_name ) || + old_driveno != new_driveno) + { + rtfs_set_errno(PEINVALIDDRIVEID); + return(FALSE); + } + + /* Get the drive and make sure it is mounted */ + old_driveno = check_drive_name_mount(old_name); + if (old_driveno < 0) + { + /* errno was set by check_drive */ + return(FALSE); + } + rtfs_set_errno(0); /* pc_mv: clear error status */ + + old_obj = 0; + old_parent_obj = 0; + new_obj = 0; + new_parent_obj = 0; + ret_val = FALSE; + + pdrive = pc_drno2dr(old_driveno); + + /* Use the buffers in the DRIVE structure. Access is locked via semaphore */ + path = (byte *)&(pdrive->pathname_buffer[0]); + filename = (byte *)&(pdrive->filename_buffer[0]); + + /* Get out the filename and d:parent */ + if (!pc_parsepath(path, filename,fileext,old_name)) + { + p_errno = PEINVALIDPATH; + goto errex; + } + /* Find the parent and make sure it is a directory */ + old_parent_obj = pc_fndnode(path); + if (!old_parent_obj) + goto errex; /* pc_fndinode - set errno */ + + if (!pc_isadir(old_parent_obj)) + { + p_errno = PENOENT; + goto errex; + } + /* Find the file */ + old_obj = pc_get_inode(0, old_parent_obj, + filename, (byte*)fileext, GET_INODE_MATCH); + if (!old_obj) + goto errex; /* pc_get_inode - set errno */ + + /* Be sure it exists and is a normal directory or file and is not open */ + if (pc_isroot(old_obj) || (old_obj->finode->opencount > 1) || + (old_obj->finode->fattribute&(ARDONLY|AVOLUME))) + { + p_errno = PEACCES; + goto errex; + } + + /* At this point old_obj contains the file we are renaming */ + + /* See if the new directory entry already exists */ + new_obj = pc_fndnode(new_name); + if (new_obj) + { + p_errno = PEEXIST; + goto errex; + } + rtfs_set_errno(0); /* pc_mv - clear errno condition after failed pc_fndnode */ + + /* Get out the filename and d:parent */ + if (!pc_parsepath(path,filename,fileext,new_name)) + { + p_errno = PEINVALIDPATH; + goto errex; + } + /* Find the parent and make sure it is a directory */ + new_parent_obj = pc_fndnode(path); + if (!new_parent_obj || !pc_isadir(new_parent_obj) || pc_isavol(new_parent_obj)) + goto errex; + + + /* The cluster value old */ + cluster = pc_finode_cluster(old_obj->pdrive,old_obj->finode); + + /* Create the new entry and assign cluster to it. If it is a directory + .. will be linked correctly */ + new_obj = pc_mknode( new_parent_obj, filename, fileext, old_obj->finode->fattribute, cluster); + if (!new_obj) + goto errex; + + /* Copy the old directory entry stuf over */ + new_obj->finode->fattribute = old_obj->finode->fattribute; + new_obj->finode->ftime = old_obj->finode->ftime; + new_obj->finode->fdate = old_obj->finode->fdate; + new_obj->finode->fsize = old_obj->finode->fsize; + + /* Update the new inode. Do not set archive bit or change date */ + if (!pc_update_inode(new_obj, FALSE, FALSE)) + goto errex; + + /* Set the old cluster value to zero */ + pc_pfinode_cluster(old_obj->pdrive,old_obj->finode,0); + /* Delete the old but won't delete any clusters */ + if (!pc_rmnode(old_obj)) + goto errex; + + p_errno = 0; + ret_val = TRUE; + + /* Good conditions fall through here, error exits jump to here */ +errex: + if (old_parent_obj) + pc_freeobj(old_parent_obj); + if (old_obj) + pc_freeobj(old_obj); + if (new_parent_obj) + pc_freeobj(new_parent_obj); + if (new_obj) + pc_freeobj(new_obj); + /* Set errno if we have one and not set by lower level already */ + if ((p_errno) && !get_errno()) + rtfs_set_errno(p_errno); + if (!release_drive_mount_write(old_driveno))/* Release lock, unmount if aborted */ + ret_val = FALSE; + return(ret_val); +} + + +/**************************************************************************** + PC_UNLINK - Delete a file. + + Description + Delete the file in name. Fail if not a simple file,if it is open, + does not exist or is read only. + + Returns + Returns TRUE if it successfully deleted the file. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PEINVALIDPATH - Path specified badly formed. + PENOENT - Can't find file to delete + PEACCESS - File in use, is read only or is not a simple file. + An ERTFS system error +***************************************************************************/ + +/* Delete a file */ +BOOLEAN pc_unlink(byte *name) /*__apifn__*/ +{ + DROBJ *pobj; + DROBJ *parent_obj; + BOOLEAN ret_val; + DDRIVE *pdrive; + byte *path; + byte *filename; + byte fileext[4]; + int driveno; + int p_errno; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + ret_val = FALSE; + parent_obj = 0; + pobj = 0; + p_errno = 0; + rtfs_set_errno(0); /* pc_unlink: clear error status */ + + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(name); + if (driveno < 0) + { + /* errno was set by check_drive */ + return(FALSE); + } + + pdrive = pc_drno2dr(driveno); + /* Use the buffers in the DRIVE structure. Access is locked via semaphore */ + path = (byte *)&(pdrive->pathname_buffer[0]); + filename = (byte *)&(pdrive->filename_buffer[0]); + + /* Get out the filename and d:parent */ + if (!pc_parsepath(path, filename,fileext,name)) + { + p_errno = PEINVALIDPATH; + goto errex; + } + + /* Find the parent and make sure it is a directory */ + parent_obj = pc_fndnode(path); + if (!parent_obj) + goto errex; /* pc_fndnode set errno */ + if (!pc_isadir(parent_obj) || pc_isavol(parent_obj)) + { + p_errno = PEACCES; + goto errex; + } + + /* Find the file */ + pobj = pc_get_inode(0, parent_obj, filename, (byte*)fileext, GET_INODE_MATCH); + /* if pc_get_inode() fails it sets errno to PENOENT or to an internal or IO error status */ + if (pobj) + { + /* Be sure it is not the root. Since the root is an abstraction + we can not delete it plus Check access permissions */ + if ( pc_isroot(pobj) || (pobj->finode->opencount > 1) || + (pobj->finode->fattribute&(ARDONLY|AVOLUME|ADIRENT))) + { + p_errno = PEACCES; + ret_val = FALSE; + goto errex; + } + else + { /* pc_rmnode sets errno */ + ret_val = pc_rmnode(pobj); + } + } + +errex: + if (pobj) + pc_freeobj(pobj); + if (parent_obj) + { + pc_freeobj(parent_obj); + } + /* Set errno if we have one and not set by lower level already */ + if ((p_errno) && !get_errno()) + rtfs_set_errno(p_errno); + if (!release_drive_mount_write(driveno))/* Release lock, unmount if aborted */ + ret_val = FALSE; + return(ret_val); +} diff --git a/build/libraries/fatfs/ARM7/apifrmat.c b/build/libraries/fatfs/ARM7/apifrmat.c new file mode 100644 index 0000000..d57c155 --- /dev/null +++ b/build/libraries/fatfs/ARM7/apifrmat.c @@ -0,0 +1,783 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 2000 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +#include + +/* */ +/* New format logic */ +/* */ +/* */ +/* */ +void get_format_parameters(dword nblocks, int *psector_p_alloc, int *pnum_root_entries); +word pc_fat_size(word nreserved, word cluster_size, word n_fat_copies, + word root_sectors, dword volume_size, + int *nibs_per_entry); + + +void pc_calculate_chs(dword total, dword *cylinders, int *heads, int *secptrack) +{ + dword c, h, s; + + if (total >= 255 * 255 * 63) + { + /* this block is just an optimization of the loop below */ + s = 63; + h = 255; + c = total / (255 * 63); + if (c > 1023) + c = 1023; + } + else + { + c = h = s = 1; + if (total >= 63 * 63 * 63) + s = 63; + while (c < 1023 && (c + 1) * h * s <= total) + { + if (h > c || h == 255) + c++; + else if (s > h || s == 63) + h++; + else + s++; + } + } + + if (cylinders) + *cylinders = c; + if (heads) + *heads = (int) h; + if (secptrack) + *secptrack = (int) s; +} + + +/* */ +/*1. Floppy driver must return partition info. Must format */ +/*2. other drivers must return info. must format. See ATAPI */ + + +/* 10-24-2000 added LBA formatting. Submit to main tree */ + + +/*************************************************************************** + PC_GET_MEDIA_PARMS - Get media parameters. + +Description + Queries the drive s associated device driver for a description of the + installed media. This information is used by the pc_format_media, + pc_partition_media and pc_format_volume routines. The application may + use the results of this call to calculate how it wishes the media to + be partitioned. + + Note that the floppy device driver uses a back door to communicate + with the format routine through the geometry structure. This allows us + to not have floppy specific code in the format routine but still use the + exact format parameters that DOS uses when it formats a floppy. + + See the following definition of the pgeometry structure: + typedef struct dev_geometry { + int dev_geometry_heads; -- - Must be < 256 + int dev_geometry_cylinders; -- - Must be < 1024 + int dev_geometry_secptrack; -- - Must be < 64 + BOOLEAN fmt_parms_valid; -- If the device io control call sets this + -- TRUE then it it telling the applications + -- layer that these format parameters should + -- be used. This is a way to format floppy + -- disks exactly as they are fromatted by dos. + FMTPARMS fmt; + } DEV_GEOMETRY; + +typedef struct dev_geometry *PDEV_GEOMETRY; + + + + +Returns + Returns TRUE if it was able to get the parameters otherwise + it returns FALSE. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PEDEVICEFAILURE - Device driver get device geometry request failed + PEINVALIDPARMS - Device driver returned bad values +****************************************************************************/ + +BOOLEAN pc_get_media_parms(byte *path, PDEV_GEOMETRY pgeometry) /* __apifn__*/ +{ +int driveno; +dword total; +DDRIVE *pdr; + CHECK_MEM(BOOLEAN, 0) + + rtfs_set_errno(0); /* pc_get_media_parms: clear error status */ + /* Make sure its a valid drive number */ + driveno = pc_parse_raw_drive(path); + if (driveno < 0) + { +inval: + rtfs_set_errno(PEINVALIDDRIVEID); /* pc_get_media_parms: invlid drive ID */ + return(FALSE); + } + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr || !(pdr->drive_flags&DRIVE_FLAGS_VALID) ) + goto inval; + + /* Ask - for device geometry */ + if (pdr->dev_table_perform_device_ioctl(driveno, DEVCTL_GET_GEOMETRY, (void *) pgeometry)) + { + rtfs_set_errno(PEDEVICEFAILURE); /* pc_get_media_parms: device get geometry failed */ + return (FALSE); + } + + /* The geometry consists of cylinders, heads, and sectors + per track. + The ATA spec says that the maximum values for these are + 65536 cylinders, 16 heads, and 255 sectors per track. + However, the MBR spec says the maximum values are + 1023 cylinders, 255 heads, and 63 sectors per track. + So, we create new values for cylinders, heads, and sectors + per track that work for the MBR but come as close as we + can get to the real disk size. + */ + total = pgeometry->dev_geometry_lbas ? pgeometry->dev_geometry_lbas : (pgeometry->dev_geometry_cylinders * pgeometry->dev_geometry_heads * pgeometry->dev_geometry_secptrack); + pc_calculate_chs(total, &pgeometry->dev_geometry_cylinders, &pgeometry->dev_geometry_heads, &pgeometry->dev_geometry_secptrack); + + return(TRUE); +} + +/*************************************************************************** + PC_FORMAT_MEDIA - Device level format + +Description + This routine performs a device level format on the specified + drive. + +Returns + Returns TRUE if it was able to perform the operation otherwise + it returns FALSE. + + Note: The the logical drive must be claimed before this routine is + called and later released by the caller. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PEDEVICEFAILURE - Device driver format request failed +****************************************************************************/ + +BOOLEAN pc_format_media(byte *path, PDEV_GEOMETRY pgeometry) /* __apifn__*/ +{ +int driveno; +DDRIVE *pdr; + + CHECK_MEM(BOOLEAN, 0) + + rtfs_set_errno(0); /* pc_format_media: clear error status */ + /* Make sure its a valid drive number */ + driveno = pc_parse_raw_drive(path); + if (driveno < 0) + { +inval: + rtfs_set_errno(PEINVALIDDRIVEID); /* pc_format_media: invalid drive id */ + return(FALSE); + } + + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr || !(pdr->drive_flags&DRIVE_FLAGS_VALID) ) + { + goto inval; + } + + /* Make sure that is it closed up */ + pc_dskfree(driveno); + + /* Format the device geometry */ + if (pdr->dev_table_perform_device_ioctl(driveno, DEVCTL_FORMAT, (void *) pgeometry)) + { + rtfs_set_errno(PEDEVICEFAILURE); /* pc_format_media: driver format failed */ + return(FALSE); + } + return(TRUE); +} + + +/*************************************************************************** + PC_PARTITION_MEDIA - Write partition table + +Description + A partition list is provided (a list of partition sizes + in units of LBA s) by the user. + +Returns + Returns TRUE if it was able to perform the operation otherwise + it returns FALSE. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PEINVALIDPARMS - Inconsistent or missing parameters + PEIOERRORWRITE - Error writing partition table + An ERTFS system error +****************************************************************************/ + +BOOLEAN pc_partition_media(byte *path, PDEV_GEOMETRY pgeometry, dword * partition_list) /* __apifn__*/ +{ +int driveno; +BOOLEAN ret_val; +BLKBUFF *buf; +byte * pbuf; +dword partition_size; +int partition_number; +int nibs_per_entry; +PTABLE part; +DDRIVE *pdr; +int root_entries; +int secpalloc; +/*word secpfat;*/ +int root_sectors; +word utemp; +word utemp2; +dword dwTemp; +dword starting_lba; +word cyl; +byte head,sec; +dword disk_size; +word starting_cylinder; + + + CHECK_MEM(BOOLEAN, 0) + ret_val = FALSE; + rtfs_set_errno(0); /* pc_partition_media: clear error status */ + /* Make sure it s a valid drive number */ + driveno = pc_parse_raw_drive(path); + if (driveno < 0) + { +inval: + rtfs_set_errno(PEINVALIDDRIVEID); + return(ret_val); + } + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr) + goto inval; + + if (!pgeometry || !partition_list || !partition_list) + { + rtfs_set_errno(PEINVALIDPARMS); /* pc_partition_media: bad arguments */ + return(ret_val); + } + /* Zero the partition table to start */ + rtfs_memset(&part, 0, sizeof(part)); + starting_cylinder = 0; + +/* 10-24-2000 - New code to support lba formatting */ + /* The first partition starts at cylinder=0, head=1, sector=1 */ + if (pgeometry->dev_geometry_lbas) + starting_lba = (dword) pgeometry->dev_geometry_secptrack; + else + { + starting_lba = 0; /* Not used */ +/* 10-24-2000 - This was the original code */ + disk_size = (dword) pgeometry->dev_geometry_cylinders; + disk_size = disk_size * (dword) pgeometry->dev_geometry_heads; + disk_size = disk_size * (dword) pgeometry->dev_geometry_secptrack; + + /* The first partition starts at 1 */ + starting_cylinder = 1; + } + + + for (partition_number = 0; partition_number < 4; partition_number++) + { + partition_size = (dword) *(partition_list+partition_number); + if (!partition_size) + break; /* End of list */ + if (pgeometry->dev_geometry_lbas) + { +/* 10-24-2000 - New code to support lba formatting */ + /* Apparently, we must still align to cylinders even in LBA mode */ + dwTemp = (dword) pgeometry->dev_geometry_heads; + dwTemp *= (dword) pgeometry->dev_geometry_secptrack; + partition_size = ((partition_size / dwTemp) * dwTemp) - (starting_lba % dwTemp); + } + else + { +/* 10-24-2000 - This was the original code */ + /* Multiply time # heads and secptrack */ + partition_size = partition_size * (dword) pgeometry->dev_geometry_heads; + partition_size = partition_size * (dword) pgeometry->dev_geometry_secptrack; + } + /* Look up root_entries and cluster size */ + get_format_parameters(partition_size, &secpalloc, &root_entries); + if( secpalloc == -1 ) + { + /* Media capacity is too large */ + rtfs_set_errno(PEDEVICEUNKNOWNMEDIA); + return( FALSE ); + } + root_sectors = (int) (root_entries/16); + + /* Calculate sectors per fat */ + /*secpfat = */pc_fat_size( (word)1 /* reserved */, (word)secpalloc, + (word)2 /*numfats*/, (word)root_sectors /* root sectors */, + partition_size, &nibs_per_entry); + + /* Now fill in the partition entry 0 */ + part.ents[partition_number].boot = 0x80; /* Set this to 0x80 for bootable */ + if (pgeometry->dev_geometry_lbas) + { +/* 10-24-2000 - New code to support lba formatting */ + /* Do CHS */ + /* SECTOR */ + dwTemp = (starting_lba % pgeometry->dev_geometry_secptrack) + 1; + sec = (byte) dwTemp; + /* HEAD */ + dwTemp = starting_lba / pgeometry->dev_geometry_secptrack; + dwTemp = dwTemp % pgeometry->dev_geometry_heads; + head = (byte) dwTemp; + /* CYLINDER */ + dwTemp = starting_lba / pgeometry->dev_geometry_secptrack; + dwTemp = dwTemp / pgeometry->dev_geometry_heads; + if (dwTemp > 1023) + dwTemp = 1023; + cyl = (word) dwTemp; + + /* Load the starting CHS */ + part.ents[partition_number].s_head = head; + utemp = (word)((cyl & 0xff) << 8); /* Low 8 bit to hi bite */ + utemp2 = (word)((cyl >> 2) & 0xc0); /* Hi 2 bits to bits 6 + 7 */ + utemp |= utemp2; + utemp |= sec; + fr_WORD((byte *)&(part.ents[partition_number].s_cyl), utemp); + + /* Load the starting LBA */ + fr_DWORD((byte *)&(part.ents[partition_number].r_sec), starting_lba); + } + else + { +/* 10-24-2000 - This was the original code */ + part.ents[partition_number].s_head = 1; /* Start at head 1 */ + fr_WORD((byte *) &(part.ents[partition_number].s_cyl), (word)starting_cylinder); + } + + if (partition_size > (dword) 0xffff) + { + if (nibs_per_entry == 8) + { +/* 10-24-2000 - New code to support lba formatting */ + if (pgeometry->dev_geometry_lbas) + part.ents[partition_number].p_typ = 0x0c; /* DOS 32 bit LBA mode (was: 0x0b) */ + else +/* 10-24-2000 - This was the original code */ + part.ents[partition_number].p_typ = 0x0b; /* DOS 32 bit */ + } + else + part.ents[partition_number].p_typ = 6; /* Huge */ + } + else + { + if (nibs_per_entry == 4) + part.ents[partition_number].p_typ = 4; /* DOS 16 bit */ + else + part.ents[partition_number].p_typ = 1; /* DOS 12 bit */ + } + if (pgeometry->dev_geometry_lbas) + { +/* 10-24-2000 - New code to support lba formatting */ + /* Load the LBA size of the partition */ + fr_DWORD((byte *)&(part.ents[partition_number].p_size), partition_size); + + /* Advance the starting_lba */ + starting_lba += partition_size; + + /* Do CHS */ + /* SECTOR */ + dwTemp = ((starting_lba-1) % pgeometry->dev_geometry_secptrack) + 1; + sec = (byte) dwTemp; + /* HEAD */ + dwTemp = (starting_lba-1) / pgeometry->dev_geometry_secptrack; + dwTemp = dwTemp % pgeometry->dev_geometry_heads; + head = (byte) dwTemp; + /* CYLINDER */ + dwTemp = (starting_lba-1) / pgeometry->dev_geometry_secptrack; + dwTemp = dwTemp / pgeometry->dev_geometry_heads; + if (dwTemp > 1023) + dwTemp = 1023; + cyl = (word) dwTemp; + + /* Load the ending CHS */ + part.ents[partition_number].e_head = head; + utemp = (word)((cyl & 0xff) << 8); /* Low 8 bit to hi bite */ + utemp2 = (word)((cyl >> 2) & 0xc0); /* Hi 2 bits to bits 6 + 7 */ + utemp |= utemp2; + utemp |= sec; + fr_WORD((byte *)&(part.ents[partition_number].e_cyl), utemp); + } + else + { +/* 10-24-2000 - This was the original code */ + /* Ending head is NHEADS-1 since heads go from 0 to n */ + part.ents[partition_number].e_head = (byte) (pgeometry->dev_geometry_heads-1); + /* Ending cylinder is in the top ten bits, secptrack in lower 6 ?? */ + /* Relative sector starting */ + fr_DWORD((byte *)&(part.ents[partition_number].r_sec), (dword) pgeometry->dev_geometry_secptrack*starting_cylinder); + + /* Set up for the next partition and use new value to calculate ending cyl */ + starting_cylinder = (word)((word)starting_cylinder + (word) *(partition_list+partition_number)); + utemp = (word)(((starting_cylinder-1) & 0xff) << 8); /* Low 8 bit to hi bite */ + utemp2 = (word)(((starting_cylinder-1) >> 2) & 0xc0); /* Hi 2 bits to bits 6 + 7 */ + utemp |= utemp2; + utemp |= (word) pgeometry->dev_geometry_secptrack; + + fr_WORD((byte *)&(part.ents[partition_number].e_cyl), utemp); + /* And partition size */ + fr_DWORD((byte *)&(part.ents[partition_number].p_size), partition_size); + } + + } /* for (partition_number = 0; partition_number < 4;.. */ + + /* Now for the signature */ + fr_WORD((byte *)&(part.signature), 0xAA55); + /* Grab some working space */ + buf = pc_scratch_blk(); + if (!buf) + { + /* pc_scratch_blk set errno */ + return(ret_val); + } + + pbuf = buf->data; + rtfs_memset(pbuf, 0, 512); + /* Now copy the partition into a block */ + /* The info starts at buf[1be] */ + /* Don't use sizeof here since the structure does not pack to exact size */ + copybuff((pbuf + 0x1be), &part, 0x42); + /* try the write */ + if (!devio_write(driveno, 0, pbuf, 1, TRUE)) + { + if (!get_errno()) + rtfs_set_errno(PEIOERRORWRITE); /* pc_partition_media: write failed */ + } + else + ret_val = TRUE; + + pc_free_scratch_blk(buf); + return(ret_val); +} + + +/*************************************************************************** + PC_FORMAT_VOLUME - Format a volume + +Description + This routine formats the volume referred to by drive letter. + drive structure is queried to determine if the device is parttioned + or not. If the device is partitioned then the partition table is read + and the volume within the partition is formatted. If it is a non + partitioned device the device is formatted according to the supplied + pgeometry parameters. The pgeometry parameter contains the the media + size in HCN format. It also contains a + + Note: The the logical drive must be claimed before this routine is + called and later released by the caller. + +Returns + Returns TRUE if it was able to perform the operation otherwise + it returns FALSE. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PEIOERRORREADMBR- Partitioned device. IO error reading + PEINVALIDMBR - Partitioned device has no master boot record + PEINVALIDMBROFFSET - Requested partition has no entry in master boot record + PEINVALIDPARMS - Inconsistent or missing parameters + PEIOERRORWRITE - Error writing during format + An ERTFS system error +****************************************************************************/ + +BOOLEAN pc_format_volume(byte *path, PDEV_GEOMETRY pgeometry) /* __apifn__*/ +{ +DDRIVE *pdr; +BOOLEAN raw_mode_io = TRUE; +FMTPARMS fmt; +int driveno; +dword partition_size = 0; +dword ltemp; +word n_cyls = 0; +int root_entries; +int secpalloc; +word secpfat; +int root_sectors; +int nibs_per_entry,partition_status; + + CHECK_MEM(BOOLEAN, 0) + rtfs_set_errno(0); /* pc_format_volume: clear error status */ + /* Make sure it s a valid drive number */ + driveno = pc_parse_raw_drive(path); + if (driveno < 0 || !pgeometry) + { + rtfs_set_errno(PEINVALIDDRIVEID); /* pc_format_volume: bad arguments */ + return(FALSE); + } + pdr = pc_drno_to_drive_struct(driveno); + + /* Check the drive structure for format strategy */ + if (!pdr || !(pdr->drive_flags&DRIVE_FLAGS_VALID)) + { + rtfs_set_errno(PEINVALIDDRIVEID); /* pc_format_volume: bad arguments */ + return(FALSE); + } + /* Make sure all is flushed */ + pc_dskfree(driveno); + + /* If the format parms were provided by the driver format now */ + if (pgeometry->fmt_parms_valid) + { + if( ((pgeometry->fmt.numcyl * pgeometry->fmt.numhead * pgeometry->fmt.secptrk) / pgeometry->fmt.secpalloc) > 0xFFFF) { + return(pc_mkfs32(driveno, &pgeometry->fmt, TRUE)); /* TRUE == RAW IO */ //ctr modified + }else{ + return(pc_mkfs16(driveno, &pgeometry->fmt, TRUE)); /* TRUE == RAW IO */ + } + } + + /* If the device is partitioned read the partition table into the + drive structure. + */ + partition_status = 0; + if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED) + { + partition_status = pc_read_partition_table(driveno, pdr); + if (partition_status == READ_PARTION_OK) + { + partition_size = pdr->partition_size; + ltemp = partition_size / + (dword)(pgeometry->dev_geometry_heads * pgeometry->dev_geometry_secptrack); + n_cyls = (word) ltemp; + raw_mode_io = FALSE; + } + else if (partition_status != READ_PARTION_NO_TABLE || pdr->partition_number != 0) + { + /* pc_format_volume: errno set to PEDEVICE by pc_read_partition_table */ + return(FALSE); + } + } + + if (!(pdr->drive_flags & DRIVE_FLAGS_PARTITIONED) || partition_status == READ_PARTION_NO_TABLE) + { + partition_size = (dword) pgeometry->dev_geometry_cylinders; + partition_size = partition_size * (dword) pgeometry->dev_geometry_heads; + partition_size = partition_size * (dword) pgeometry->dev_geometry_secptrack; + n_cyls = (word) pgeometry->dev_geometry_cylinders; + raw_mode_io = TRUE; + } + + /* Look up root_entries and cluster size */ + get_format_parameters(partition_size, &secpalloc, &root_entries); + if( secpalloc == -1 ) + { + /* Media capacity is too large */ + rtfs_set_errno(PEDEVICEUNKNOWNMEDIA); + return( FALSE ); + } + root_sectors = (int) (root_entries/16); + + /* Calculate sectors per fat */ + secpfat = pc_fat_size( (word)1 /* reserved */, (word)secpalloc, + (word)2 /*numfats*/, (word)root_sectors /* root sectors */, + partition_size, &nibs_per_entry); + + rtfs_strcpy(&fmt.oemname[0], + rtfs_strtab_user_string(USTRING_SYS_OEMNAME) ); + fmt.physical_drive_no = (byte) driveno; + fmt.binary_volume_label = BIN_VOL_LABEL; + rtfs_strcpy(fmt.text_volume_label, + rtfs_strtab_user_string(USTRING_SYS_VOLUME_LABEL) ); + + + fmt.secpalloc = (byte) secpalloc; + fmt.numfats = (byte) 2; + fmt.secptrk = (word) pgeometry->dev_geometry_secptrack; + fmt.numhead = (word) pgeometry->dev_geometry_heads; + fmt.numcyl = (word) n_cyls; + if (nibs_per_entry == 8) /* FAT32 Format */ + { + fmt.secreserved = (word) 32; + if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED) + { + if (pgeometry->dev_geometry_lbas) + /* 10-24-2000 - New code to support lba formatting */ + fmt.numhide = (unsigned long) 0; /*PS Does not work as fmt.secptrk here */ + else + /* 10-24-2000 - This was the original code */ + fmt.numhide = (unsigned long) fmt.secptrk; + } + else + fmt.numhide = (unsigned long) pgeometry->fmt.numhide; //ctr modified + fmt.secpfat = (word) 0; + fmt.numroot = (word) 0; + fmt.mediadesc = (byte) 0xF8; + return(pc_mkfs32(driveno, &fmt, raw_mode_io)); + } + else + { + fmt.secreserved = (word) 1; + fmt.numhide = pgeometry->fmt.numhide; //ctr modified + fmt.secpfat = (word) secpfat; + fmt.numroot = (word) root_entries; + fmt.mediadesc = (byte) 0xF8; + return(pc_mkfs16(driveno, &fmt, raw_mode_io)); + } +} + + +/***************************************************************************** + PC_MKFS - Make a file system on a disk MS-DOS 4.0 version + + Description + Given a drive number and a format parameter block. Put an MS-DOS + file system on the drive: + The disk MUST already have a low level format. All blocks on the drive + should be intitialize with E5s or zeros. + + see pcmkfs in the samples directory. + + Some common parameters. Note: For other drive types use debug to get the + parameters from block zero after FORMAT has been run. + + 360 720 20M 80M (DRIVE SIZE) +oemname ===== UP TO YOU. ONLY 8 Chars matter. Right filled with spaces +secpalloc 2 2 4 8 +secreserved 1 1 1 1 +numfats 2 2 2 2 +numroot 0x70 0x70 0x200 0x200 +mediadesc 0xFD 0xF9 0xF8 0xF8 +secpfat 2 3 44 88 +secptrk 9 9 0x11 0x11 +numhead 2 2 4 8 +numcyl 40 80 612 1224 + + Note: If pc_mkfs is called secpfat equal zero, secpfat will be calculated + internally. + +Returns + Returns TRUE if the filesystem disk was successfully initialized. +See Also: + pcmkfs.c format utility program +****************************************************************************/ + + +word pc_fat_size(word nreserved, word cluster_size, word n_fat_copies, + word root_sectors, dword volume_size, + int *nibs_per_entry) /*__fn__*/ +{ + + dword fat_size; + dword total_clusters; + word entries_per_block; + +#if (FAT32) + if ((root_sectors == 0) || ((volume_size>>11) >= 512))/* FAT32 Format */ + { + fat_size = (volume_size + 128*cluster_size) / (128*cluster_size + 1); + *nibs_per_entry = 8; + return ((word)fat_size); + } +#endif + + /* Calulate total cluster size. Assuming zero size fat: + We round up to the nearest cluster boundary */ + total_clusters = volume_size - nreserved - root_sectors; + total_clusters /= cluster_size; + + /* Calculate the number of fat entries per block in the FAT. If + < 4087 clusters total the fat entries are 12 bits hence 341 + will fit. else 256 will fit + we add in n_fat_copies * 12 here since it take 12 blocks to represent + 4087 clusters in 3 nibble form. So we add in the worst case FAT size + here to enhance the accuracy of our guess of the total clusters. + */ + + if (total_clusters <= (dword) (4087 + (n_fat_copies * 12)) ) + { + entries_per_block = 341; + *nibs_per_entry = 3; + } + else + { + entries_per_block = 256; + *nibs_per_entry = 4; + } + + fat_size = (total_clusters + entries_per_block - 1)/entries_per_block; + + return((word) fat_size); +} + +/* Choose format parameters based on the number of blocks in the volume */ +void get_format_parameters(dword nblocks, int *psectors_per_alloc, int *pnum_root_entries) +{ + int sectors_per_alloc; + int num_root_entries; + +#if (FAT32) + /* FAT12 */ + if (nblocks <= 1000) /* <= .5 MEG */ + {sectors_per_alloc = 1; num_root_entries = 32;} + else if (nblocks <= 10240) /* <= 5 MEG */ + {sectors_per_alloc = 8; num_root_entries = 512;} + else if (nblocks <= 32768) /* <= 16 MEG */ + {sectors_per_alloc = 4; num_root_entries = 512;} + + /* FAT16 */ + else if (nblocks <= 262144) /* <= 128 MEG */ + {sectors_per_alloc = 4; num_root_entries = 512;} + else if (nblocks <= 524288) /* <= 256 MEG */ + {sectors_per_alloc = 8; num_root_entries = 512;} + else if (nblocks <= 1048576) /* <= 512 MEG */ + {sectors_per_alloc = 16; num_root_entries = 512;} + + /* FAT32 */ + else if (nblocks <= 16777216) /* <= 8 GIG */ + {sectors_per_alloc = 8; num_root_entries = 512;} + else if (nblocks <= 33554432) /* <= 16 GIG */ + {sectors_per_alloc = 16; num_root_entries = 512;} + else if (nblocks <= 67108864) /* <= 32 GIG */ + {sectors_per_alloc = 32; num_root_entries = 512;} + else /* > 32 GIG */ + {sectors_per_alloc = 64; num_root_entries = 512;} +#else + /* FAT12 */ + if (nblocks <= 1000) /* <= .5 MEG */ + {sectors_per_alloc = 1; num_root_entries = 32;} + else if (nblocks <= 10240) /* <= 5 MEG */ + {sectors_per_alloc = 8; num_root_entries = 512;} + else if (nblocks <= 32768) /* <= 16 MEG */ + {sectors_per_alloc = 4; num_root_entries = 512;} + + /* FAT16 */ + else if (nblocks <= 262144) /* <= 128 MEG */ + {sectors_per_alloc = 4; num_root_entries = 512;} + else if (nblocks <= 524288) /* <= 256 MEG */ + {sectors_per_alloc = 8; num_root_entries = 512;} + else if (nblocks <= 1048576) /* <= 512 MEG */ + {sectors_per_alloc = 16; num_root_entries = 512;} + else if (nblocks <= 2097152) /* <= 1 GIG */ + {sectors_per_alloc = 32; num_root_entries = 512;} + else if (nblocks <= 4194304) /* <= 2 GIG */ + {sectors_per_alloc = 64; num_root_entries = 512;} + else /* error */ + {sectors_per_alloc = -1; num_root_entries = -1;} +#endif /* (FAT32) */ + + *psectors_per_alloc = sectors_per_alloc; + *pnum_root_entries = num_root_entries; +} + + diff --git a/build/libraries/fatfs/ARM7/apigetwd.c b/build/libraries/fatfs/ARM7/apigetwd.c new file mode 100644 index 0000000..21fcb8d --- /dev/null +++ b/build/libraries/fatfs/ARM7/apigetwd.c @@ -0,0 +1,188 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIGETWD.C - Contains user api level source code. + + The following routines are included: + + pc_pwd - Get string representation of current working dir. + +*/ + +#include + +/***************************************************************************** + PC_PWD - Return a string containing the current working directory for + a drive. + + Description + Fill in a string with the full path name of the current working directory. + Return FALSE if something went wrong. + If *drive is null or an invalid drive specifier the default drive is used. + + + Returns + Returns the path name in path. The function returns TRUE on success + no on failure. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + An ERTFS system error +****************************************************************************/ +static BOOLEAN pc_l_pwd(byte *, DROBJ *); +static BOOLEAN pc_gm_name(byte *path, DROBJ *pmom, DROBJ *pdotdot); + +BOOLEAN pc_pwd(byte *drive, byte *path) /*__apifn__*/ +{ + int driveno; + DDRIVE *pdrive; + DROBJ *pobj; + BOOLEAN ret_val; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + ret_val = FALSE; + rtfs_set_errno(0); /* pc_pwd: clear error status */ + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(drive); + if (driveno < 0) + { + /* errno was set by check_drive */ + goto return_error; + } + + /* Find the drive */ + pdrive = pc_drno2dr(driveno); + if (pdrive) + { + pobj = pc_get_cwd(pdrive); + /* pc_get_cwd sets errno */ + if (pobj) + { + ret_val = pc_l_pwd(path, pobj); + } + } + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + +return_error: /* Does not have to be an error to get here */ + return(ret_val); +} + +static BOOLEAN pc_l_pwd(byte *path, DROBJ *pobj) /*__fn__*/ +{ +#define OBJDEPTH 32 + DROBJ *parentlist[OBJDEPTH]; /* List of drobjs to the top */ + DROBJ *dotdotlist[OBJDEPTH]; /* List of drobjs to the top */ + int objcnt; + BOOLEAN ret_val; + int i; + DROBJ *tpobj; + ret_val = FALSE; + + /* If it is the root we are done */ + if (pc_isroot(pobj)) + { + CS_OP_ASSIGN_ASCII(path,'\\'); + CS_OP_INC_PTR(path); + CS_OP_TERM_STRING(path); + pc_freeobj(pobj); + return(TRUE); + } + + /* Build a list of drobjs for directories and a drobj for the .. in + the directories */ + tpobj = pobj; + /* Zero the object lists in case we terminate incorrectly */ + for (objcnt = 0; objcnt < OBJDEPTH; objcnt++) + dotdotlist[objcnt] = parentlist[objcnt] = 0; + + for (objcnt = 0; objcnt < OBJDEPTH; objcnt++) + { + /* get dotdot */ + tpobj = pc_get_inode(0, tpobj, 0, 0, GET_INODE_DOTDOT); + dotdotlist[objcnt] = tpobj; + if (!tpobj) + goto clean_and_go; + /* Get the parent directory */ + tpobj = pc_get_mom(tpobj); + parentlist[objcnt] = tpobj; + if (!tpobj) + goto clean_and_go; + if (pc_isroot(tpobj)) + break; + } + + if (objcnt >= OBJDEPTH) + { + objcnt = OBJDEPTH-1; + goto clean_and_go; + } + + /* Start at the root and work backwards in the list of parents getting + the names. (note: the get name algorithm needs the parent of the + current directory and the .. of the directory */ + for (i = objcnt; i >= 0; i--) + { + CS_OP_ASSIGN_ASCII(path,'\\'); + CS_OP_INC_PTR(path); + CS_OP_TERM_STRING(path); + if (!pc_gm_name(path , parentlist[i],dotdotlist[i])) + goto clean_and_go; + path = CS_OP_GOTO_EOS(path); + } + ret_val = TRUE; + +clean_and_go: + for (i = objcnt; i >= 0; i--) + { + if (parentlist[i]) + pc_freeobj(parentlist[i]); + if (dotdotlist[i]) + pc_freeobj(dotdotlist[i]); + } + pc_freeobj(pobj); + + return(ret_val); +} + +static BOOLEAN pc_gm_name(byte *path, DROBJ *parent_obj, DROBJ *pdotdot) /*__fn__*/ +{ + DROBJ *pchild; + CLUSTERTYPE clusterno; + CLUSTERTYPE fcluster; + BOOLEAN ret_val; + + ret_val = FALSE; + + clusterno = pc_sec2cluster(pdotdot->pdrive, pdotdot->blkinfo.my_frstblock); + pchild = pc_get_inode(0, parent_obj, 0, 0, GET_INODE_STAR); + if (pchild) + { + do + { + fcluster = pc_finode_cluster(pdotdot->pdrive,pchild->finode); + if (fcluster == clusterno) + { + /* Get a long file name if none revert to the 8.3 name */ + if (!pc_get_lfn_filename(pchild,path)) + pc_cs_mfile (path, pchild->finode->fname, pchild->finode->fext); + ret_val = TRUE; + break; + } + } + while (pc_get_inode(pchild, parent_obj, 0, 0, GET_INODE_STAR)); + } + + if (pchild) + { + pc_freeobj(pchild); + } + + return(ret_val); +} + diff --git a/build/libraries/fatfs/ARM7/apigfrst.c b/build/libraries/fatfs/ARM7/apigfrst.c new file mode 100644 index 0000000..23e44a2 --- /dev/null +++ b/build/libraries/fatfs/ARM7/apigfrst.c @@ -0,0 +1,307 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIGFRST.C - Contains user api level source code. + + The following routines are included: + + pc_gfirst - Get stats on the first file to match a pattern. + pc_gnext - Get stats on the next file to match a pattern. + pc_gdone - Free resources used by pc_gfirst/pc_gnext. + pc_upstat - Copy directory entry info to a user s stat buffer + +*/ + +#include + +/*************************************************************************** + PC_GFIRST - Get first entry in a directory to match a pattern. + + Description + Given a pattern which contains both a path specifier and a search pattern + fill in the structure at statobj with information about the file and set + up internal parts of statobj to supply appropriate information for calls + to pc_gnext. + + Examples of patterns are: + D:\USR\RELEASE\NETWORK\*.C + D:\USR\BIN\UU*.* + D:MEMO_?.* + D:*.* + + Returns + Returns TRUE if a match was found otherwise FALSE. (see also the pcls.c + utility.) + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PEINVALIDPATH - Path specified badly formed. + PENOENT - Not found, no match + An ERTFS system error + +****************************************************************************/ + +void pc_upstat(DSTAT *statobj); + +BOOLEAN pc_gfirst(DSTAT *statobj, byte *name) /*__apifn__*/ +{ + byte *mompath; + byte *filename; + byte fileext[4]; + int driveno; + DDRIVE *pdrive; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* po_gfirst: clear error status */ + + rtfs_memset((byte *) statobj,0,sizeof(*statobj)); +/* statobj->pobj = 0; */ +/* statobj->pmom = 0; */ + + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(name); + if (driveno < 0) + { + /* errno was set by check_drive */ + return(FALSE); + } + pdrive = pc_drno2dr(driveno); + /* Use the buffers in the DRIVE structure. Access is locked via semaphore */ + mompath = (byte *)&(pdrive->pathname_buffer[0]); + filename = (byte *)&(pdrive->filename_buffer[0]); + + /* Get out the filename and d:parent */ + if (!pc_parsepath(mompath,filename,fileext,name)) + { + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + rtfs_set_errno(PEINVALIDPATH); /* pc_gfirst: Bad path name */ + return(FALSE); + } + + /* Save the pattern. we will need it in pc_gnext */ + copybuff(statobj->pname, filename, FILENAMESIZE_BYTES); + copybuff(statobj->pext, fileext, 4); + /* Copy over the path. we will need it later */ + copybuff(statobj->path, mompath, EMAXPATH_BYTES); + /* Find the file and init the structure */ + statobj->pmom = (void *) pc_fndnode(mompath); + /* pc_fndnode will set errno */ + if (statobj->pmom) + /* Found it. Check access permissions */ + { + if(pc_isadir((DROBJ *)(statobj->pmom))) + { + /* Now find pattern in the directory */ + statobj->pobj = (void *) pc_get_inode(0, (DROBJ *)(statobj->pmom), filename, (byte*) fileext, GET_INODE_WILD); + if (statobj->pobj) + { + /* And update the stat structure */ + pc_upstat(statobj); + + /* remember the drive number. used by gnext et al. */ + statobj->driveno = driveno; +/* 9-20-94 release the FINODE and allocate a dummy. This will keep everyone + who expects the drobj to own a finode happy but will not leave the + finode open which locks out unlink et al */ + pc_freei(((DROBJ *)(statobj->pobj))->finode); /* Release the current */ + ((DROBJ *)(statobj->pobj))->finode = pc_alloci(); +/* END 9-20-94 */ + /* Remember the unique number associated with the drive + mount. If the drive is closed before we call gnext or + gdone we'll know about it because this number won't match */ + statobj->drive_opencounter = pdrive->drive_opencounter; + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + return(TRUE); + } + else + { + /* pc_gfirst: if statobj->pobj is 0 pc_get_inode() has set errno to PENOENT or + to an internal or IO error status + if PENOENT set we will clear errno */ + if (get_errno() == PENOENT) + rtfs_set_errno(0); /* pc_gfirst: file not found in directory + set errno to zero and return FALSE */ + } + } + else + rtfs_set_errno(PEINVALIDDIR); /* pc_gfirst: Path not a directory, report not found */ + } + /* If it gets here we had a problem */ + if (statobj->pmom) + pc_freeobj((DROBJ *)statobj->pmom); + rtfs_memset((byte *) statobj,0,sizeof(*statobj)); + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + + return(FALSE); +} + +/**************************************************************************** + PC_GNEXT - Get next entry in a directory that matches a pattern. + + Description + Given a pointer to a DSTAT structure that has been set up by a call to + pc_gfirst(), search for the next match of the original pattern in the + original path. Return TRUE if found and update statobj for subsequent + calls to pc_gnext. + + Returns + Returns TRUE if a match was found otherwise FALSE. + + errno is set to one of the following + 0 - No error + PEINVALIDPARMS - statobj argument is not valid + PENOENT - Not found, no match (normal termination of scan) + An ERTFS system error +****************************************************************************/ + +BOOLEAN pc_gnext(DSTAT *statobj) /*__apifn__*/ +{ + DROBJ *nextobj; + DDRIVE *pdrive; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + /* see if the drive is still mounted. Do not use pmom et al. since they + may be purged */ + if (!statobj || !statobj->pmom) + { + rtfs_set_errno(PEINVALIDPARMS); /* pc_gnext: statobj is not valid */ + return(FALSE); + } + if (!check_drive_number_mount(statobj->driveno)) + return(FALSE); + pdrive = pc_drno2dr(statobj->driveno); + if (statobj->drive_opencounter != pdrive->drive_opencounter) + { + rtfs_set_errno(PEINVALIDPARMS); /* pc_gnext: statobj is not valid */ + release_drive_mount(statobj->driveno);/* Release lock, unmount if aborted */ + return(FALSE); + } + + rtfs_set_errno(0); /* po_gnext: clear error status */ + + /* Now find the next instance of pattern in the directory */ + nextobj = pc_get_inode((DROBJ *)(statobj->pobj), (DROBJ *)(statobj->pmom), + statobj->pname, statobj->pext, GET_INODE_WILD); + if (nextobj) + { + statobj->pobj = (void *)nextobj; + /* And update the stat structure */ + pc_upstat(statobj); +/* 9-20-94 release the FINODE and allocate a dummy. This will keep everyone + who expects the drobj to own a finode happy but will not leave the + finode open which locks out unlink et al */ + pc_freei(((DROBJ *)(statobj->pobj))->finode); /* Release the current */ + ((DROBJ *)(statobj->pobj))->finode = pc_alloci(); +/* END 9-20-94 */ + release_drive_mount(statobj->driveno);/* Release lock, unmount if aborted */ + return(TRUE); + } + else + { + if (get_errno() == PENOENT) + rtfs_set_errno(0); /* get_inode: file not found in directory + set errno to zero and return FALSE */ + /* pc_gnext: nextobj is 0 pc_get_inode() has set errno to PENOENT or to an internal or IO error status */ + release_drive_mount(statobj->driveno);/* Release lock, unmount if aborted */ + return(FALSE); + } +} + + +/*************************************************************************** + PC_GDONE - Free internal resources used by pc_gnext and pc_gfirst. + + Description + Given a pointer to a DSTAT structure that has been set up by a call to + pc_gfirst() free internal elements used by the statobj. + + NOTE: You MUST call this function when done searching through a + directory. + + Returns + Nothing + + errno is set to one of the following + 0 - No error + PEINVALIDPARMS - statobj argument is not valid +****************************************************************************/ + +void pc_gdone(DSTAT *statobj) /*__apifn__*/ +{ + DDRIVE *pdrive; + VOID_CHECK_MEM() /* Make sure memory is initted */ + /* see if the drive is still mounted. Do not use pmom et al. since they + may be purged */ + /* see if the drive is still mounted. Do not use pmom et al. since they + may be purged */ + if (!statobj || !statobj->pmom) + { + return; + } + if (!check_drive_number_mount(statobj->driveno)) + return; + pdrive = pc_drno2dr(statobj->driveno); + if (statobj->drive_opencounter != pdrive->drive_opencounter) + { + release_drive_mount(statobj->driveno);/* Release lock, unmount if aborted */ + return; + } + if (statobj->pobj) + pc_freeobj((DROBJ *)statobj->pobj); + if (statobj->pmom) + pc_freeobj((DROBJ *)statobj->pmom); + release_drive_mount(statobj->driveno);/* Release lock, unmount if aborted */ + rtfs_memset((byte *) statobj,0,sizeof(*statobj)); +} +/**************************************************************************** + PC_UPSTAT - Copy private information to public fields for a DSTAT struc. + + Description + Given a pointer to a DSTAT structure that contains a pointer to an + initialized DROBJ structure, load the public elements of DSTAT with + name filesize, date of modification et al. (Called by pc_gfirst & + pc_gnext) + + Returns + Nothing + + +****************************************************************************/ + +/* Copy internal stuf so the outside world can see it */ +void pc_upstat(DSTAT *statobj) /*__fn__*/ +{ + DROBJ *pobj; + FINODE *pi; + pobj = (DROBJ *)(statobj->pobj); + + pi = pobj->finode; + + copybuff( statobj->fname, pi->fname, 8); + statobj->fname[8] = CS_OP_ASCII('\0'); + copybuff( statobj->fext, pi->fext, 3); + statobj->fext[3] = CS_OP_ASCII('\0'); + /* put null termed file.ext into statobj */ + pc_ascii_mfile((byte *)statobj->filename, (byte *)statobj->fname, + (byte *)statobj->fext); + + statobj->fattribute = pi->fattribute; + statobj->ftime = pi->ftime; + statobj->fdate = pi->fdate; + statobj->fsize = pi->fsize; + /* Get the lfn value for this object. If none available make + sure it is a NULL string in ASCII or UNICODE */ + if (!pc_get_lfn_filename(pobj, (byte *)statobj->lfname)) + { + statobj->lfname[0] = statobj->lfname[1] = 0; + pc_cs_mfile((byte *)statobj->lfname, (byte *)statobj->fname, + (byte *)statobj->fext); + } +} + + diff --git a/build/libraries/fatfs/ARM7/apiinfo.c b/build/libraries/fatfs/ARM7/apiinfo.c new file mode 100644 index 0000000..69c470b --- /dev/null +++ b/build/libraries/fatfs/ARM7/apiinfo.c @@ -0,0 +1,286 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIINFO.C - Contains user api level source code. + + The following routines are included: + + pc_set_default_drive - Set the default drive number. + pc_free - Calculate and return the free space on a disk. + pc_isdir - Determine if a path is a directory. + pc_isvol - Determine if a path is a volume + pc_get_attributes - Get File Attributes + pc_getdfltdrvno - Get the default drive number. +*/ + +#include + +/*************************************************************************** + PC_SET_DEFAULT_DRIVE - Set the current default drive. + + Description + Use this function to set the current default drive that will be used + when a path specifier does not contain a drive specifier. + Note: The default default is zero (drive A:) + + + Returns + Return FALSE if the drive is out of range. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID - Driveno is incorrect +****************************************************************************/ + +/* Set the currently stored default drive */ +BOOLEAN pc_set_default_drive(byte *drive) /*__apifn__*/ +{ +int drive_no; + + rtfs_set_errno(0); /* pc_set_default_drive: clear error status */ + /* get drive no */ + drive_no = pc_parse_raw_drive(drive); + if ( ( drive_no < 0) || !pc_validate_driveno(drive_no)) + { + rtfs_set_errno(PEINVALIDDRIVEID);/* pc_set_default_drive: invalid argument */ + return(FALSE); + } + else + { + rtfs_get_system_user()->dfltdrv_set = 1; + rtfs_get_system_user()->dfltdrv = drive_no; + return(TRUE); + } +} + +/**************************************************************************** + PC_FREE - Count the number of free bytes remaining on a disk + + Description + Given a path containing a valid drive specifier count the number + of free bytes on the drive. The function also takes two additional + argument that point to location that will be loaded with the + total number of blocks on the drive and the total number of + free clusters + + + Returns + The number of free bytes or zero if the drive is full, -1 if not open, + or out of range. + + dword *blocks_total - Contains the total block count + dword *blocks_free - Contain the total count of free blocks. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID - Driveno is incorrect + An ERTFS system error +*****************************************************************************/ + +/* Return # free bytes on a drive */ +long pc_free(byte *path, dword *blocks_total, dword *blocks_free) /*__apifn__*/ +{ + int driveno; + DDRIVE *pdr; + long bytes_free; + CHECK_MEM(long, 0) /* Make sure memory is initted */ + + + rtfs_set_errno(0); /* po_free: clear error status */ + + /* assume failure to start */ + bytes_free = -1; + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(path); + /* if error check_drive errno was set by check_drive */ + if (driveno >= 0) + { + pdr = pc_drno2dr(driveno); + if (!pdr) + { + rtfs_set_errno(PEINVALIDDRIVEID); /* pc_free: no valid drive present */ + bytes_free = -1; + } + else + { + *blocks_free = pdr->known_free_clusters; + *blocks_free *= pdr->secpalloc; /* Size of each fat */ + *blocks_total = pdr->maxfindex - 1; /* Number of entries in the fat */ + *blocks_total *= pdr->secpalloc ; /* Size of each fat */ + bytes_free = *blocks_free * 512; + } + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + } + return(bytes_free); +} + +/***************************************************************************** + PC_ISDIR - Test if a path name is a directory + + Description + Test to see if a path specification ends at a subdirectory or a + file. + Note: \ is a directory. + + Returns + Returns TRUE if it is a directory. +****************************************************************************/ + + +#define ISDIR 1 +#define ISVOL 2 + +BOOLEAN pc_is(int op, byte *path) /*__fn__*/ +{ + DROBJ *pobj; + BOOLEAN ret_val = FALSE; + int driveno; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_isdir/pc_isvol: clear errno */ + + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(path); + /* if check_drive failed errno was set by check_drive */ + if (driveno >= 0) + { + pobj = pc_fndnode(path); + /* pc_isdir/pc_isvol: if pc_fndnode fails it will set errno */ + if (pobj) + { + if (op == ISDIR) + ret_val = pc_isadir(pobj); + else if (op == ISVOL) + ret_val = pc_isavol(pobj); + pc_freeobj(pobj); + } + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + } + return (ret_val); +} +/***************************************************************************** + PC_ISDIR - Test if a path name is a directory + + Description + Test to see if a path specification ends at a subdirectory. + + Returns + Returns TRUE if it is a directory. + + errno is set to one of the following + 0 - No error + PENOENT - Path not found + An ERTFS system error +****************************************************************************/ + + +BOOLEAN pc_isdir(byte *path) /*__apifn__*/ +{ + return(pc_is(ISDIR, path)); +} + +/***************************************************************************** + PC_ISVOL - Test if a path name is a volume entry + + Description + Test to see if a path specification ends at a volume label + + Returns + Returns TRUE if it is a volume + + errno is set to one of the following + 0 - No error + PENOENT - Path not found + An ERTFS system error +****************************************************************************/ + +BOOLEAN pc_isvol(byte *path) /*__apifn__*/ +{ + return(pc_is(ISVOL, path)); +} + +/***************************************************************************** + pc_get_attributes - Get File Attributes + + Description + Given a file or directory name return the directory entry attributes + associated with the entry. + + The following values are returned: + + BIT Nemonic + 0 ARDONLY + 1 AHIDDEN + 2 ASYSTEM + 3 AVOLUME + 4 ADIRENT + 5 ARCHIVE + 6-7 Reserved + + Returns + Returns TRUE if successful otherwise it returns FALSE + + errno is set to one of the following + 0 - No error + PENOENT - Path not found + An ERTFS system error +****************************************************************************/ + +BOOLEAN pc_get_attributes(byte *path, byte *p_return) /*__apifn__*/ +{ + DROBJ *pobj; + BOOLEAN ret_val = FALSE; + int driveno; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_get_attributes: clear errno */ + + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(path); + if (driveno < 0) + { + /* pc_get_attributes: if check_drive failed errno was set by check_drive */ + return (FALSE); + } + pobj = pc_fndnode(path); + /* if pc_fndnode fails it will set errno */ + if (pobj) + { + *p_return = pobj->finode->fattribute; + pc_freeobj(pobj); + ret_val = TRUE; + } + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + return (ret_val); +} + +/*************************************************************************** + PC_GETDFLTDRVNO - Return the current default drive. + + Description + Use this function to get the current default drive when a path specifier + does not contain a drive specifier. + + see also pc_setdfltdrvno() + + Returns + Return the current default drive. + + pc_getdfltdrvno() does not set errno +*****************************************************************************/ + +/* Return the currently stored default drive */ +int pc_getdfltdrvno(void) /*__apifn__*/ +{ + CHECK_MEM(int, 0) /* Make sure memory is initted */ + if (!rtfs_get_system_user()->dfltdrv_set) + return(prtfs_cfg->default_drive_id); + else + return(rtfs_get_system_user()->dfltdrv); +} + diff --git a/build/libraries/fatfs/ARM7/apiinit.c b/build/libraries/fatfs/ARM7/apiinit.c new file mode 100644 index 0000000..df93a00 --- /dev/null +++ b/build/libraries/fatfs/ARM7/apiinit.c @@ -0,0 +1,510 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS inc, 1996 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ + +/****************************************************************************** + PC_ERTFS_INIT() - Configure ERTFS drive letter/device mapping and initialize + device drivers. + + Returns + TRUE If ertfs memory and system resource init functions succeed + FALSE If ertfs memory or system resource init functions fail + + The user must modify this code to assign parameters and bind device + driver entry points to ERTFS drive structures. The source code contains + examples for configuring each of the stock device drivers. + + This routine initializes ERTFS resources and binds device drivers to drive + letters so it must must be called before any other calls to the ERTFS + API are made. + + + Device driver configuration block. + + The ERTFS drive structure (struct ddrive) contains a section that must be + initialized by the user. + + The following fields must be initialized - + + register_file_address - IO address pointer that may be used by the + device driver to access the controller. For the stock set of drivers + only the IDE/ATAPI driver use this parameter. + interrupt_number - Interrupt vector number that the controller + will interrupt on. + drive_flags - The user sets bits in this field to tell ERTFS + and the device driver about the drive s properties. The device driver + uses this field to keep track of the device status. + + These two bits may be set by the user in the main body of the + the pc_ertfs_init routine. + + DRIVE_FLAGS_PARTITIONED - Set this bit if the device is a partitioned + device. + DRIVE_FLAGS_PCMCIA - Set this bit if the device is a pcmcia device. + ERTFS does not look at this bit. It is used ony by the device drivers. + + DRIVE_FLAGS_FAILSAFE - Set this bit if you want pc_ertfs_init() + to call pro_failsafe_auto_init for this drive to automatically + enable failsafe operating mode. + Note: pro_failsafe_auto_init() is implemented inside the file + prapifs.c. By default the function automatically fails unless + the prapifs.c is modified and the compile time constant + named INCLUDE_FAILSAFE_AUTO_INIT is set to one. pro_failsafe_auto_init() + as implemented supports only one drive at a time. It places the + device in autorestore, autorecover and autocommit + mode. To change the default behavior or to support more than one device + you must modify the source code. + + These bits are set by device driver. + + DRIVE_FLAGS_VALID - Set by the driver in the WARMSTART IO Control + call if initialization succeeded. If this flags is not set then ERTFS + will not call the driver again. + + DRIVE_FLAGS_FORMAT - Set by the driver in the WARMSTART IO Control + call if the device requires formatting before it may be used. If this flags is set + then ERTFS will format the device during the rtfs_init phase. + + DRIVE_FLAGS_REMOVABLE - Set by the device driver init routine if the + device is removable. If this bit is set then the driver must implement + the DEVCTL_CHECKSTATUS ioctrl function. + + DRIVE_FLAGS_INSERTED - This bit is used by the device driver to maintain + state information. This bit is used by the PCMCIA drivers in the + following way. The default value is ZERO. If this value is zero when the + check status IOCTRL call is made then the driver attempts to map the + device through the pcmcia controller. The pcmcia management interrupt + routine clears this bit when a card removal occurs. + + partition_number - If DRIVE_FLAGS_PARTITIONED is set in the + drive_flags field then this must be initialized with the partition + number. 0,1,2 etc. + pcmcia_slot_number - If DRIVE_FLAGS_PCMCIA is set then this tells which + pcmcia slot. Must be (0 or 1) for the supplied PCMCIA subsystem. + pcmcia_cfg_opt_value - This is the value to be place in the card s + configuration option register by the pcmcia driver after power up. + controller_number - This is set by the user in init and used by + driver to access controller specific information. For example the ide + device driver supports two controllers. This value is used by the driver + to store controller specific data and calculate controller specific + addresses and values. + logical_unit_number - This is a logical unit number that is used only + by the device driver. For example the IDE driver uses logical unit + number 1 to access the slave drive. The floppy disk uses logical + unit 1 to access the secondary floppy. + driveno - This is the drive number that the user wishes ERTFS to access + this device as. 0 = A:, 1 = B: etc. + + + dev_table_drive_io - This is the io function for the device. The + function must perform the requested read or write operation and + return the correct status (TRUE for success, FALSE for failure). + + By example here is the ide drive io function. + + BOOLEAN ide_io(driveno, sector, buffer, count, BOOLEAN reading) + int driveno - 0 to prtfs_cfg->cfg_NDRIVES == A:,B:,.. the driver calls + pc_drno_to_drive_struct(driveno) to access the drive structure. + dword sector- Starting sector number to read or write + void *buffer- Buffer to read to write from. + word count - Number of sectors to transfer + reading - True for a read request, False for a write request. + + dev_table_perform_device_ioctl - This is the control interface to the + device driver. + + By example here is the ide drive io control function. + + int ide_perform_device_ioctl(driveno, opcode, pargs) + int driveno - 0 to prtfs_cfg->cfg_NDRIVES == A:,B:,.. the driver calls + pc_drno_to_drive_struct(driveno) to access the drive structure. + int opcode - One of the following: + DEVCTL_CHECKSTATUS + DEVCTL_WARMSTART + DEVCTL_POWER_RESTORE + DEVCTL_POWER_LOSS + DEVCTL_GET_GEOMETRY + DEVCTL_FORMAT + DEVCTL_REPORT_REMOVE + void * pargs - void pointer to arguments. See details below. + + Here are detailed descriptions of each OPCODE and its relation + to pargs. + + DEVCTL_CHECKSTATUS - Return the status of device. ERTFS calls + the driver with this opcode before it performs IO. + + Note: For the CHECKSTATUS call. ERTFS will pass a BOOLEAN value + to the driver to indicate if the volume in question has unwritten + cached data that will be lost if the driver returns anything but + DEVTEST_NOCHANGE. If this value is true then the driver may + make more aggresive efforts to correct media changes. For example + by asking the user to re-insert the correct diskette or card. + + The driver must return one of the following values: + DEVTEST_NOCHANGE - The device is available and no media change + has been detected since the previous CHECKSTATUS call. + DEVTEST_NOMEDIA - The device contains no media. + DEVTEST_UNKMEDIA - The device contains media but the driver + can not read or write to it. + DEVTEST_CHANGED - The device is available to but a media change + has been detected since the previous CHECKSTATUS call. ERTFS will + close out the current logical volume and re-mount the device. + + DEVCTL_WARMSTART - The device driver is called with this opcode + once at startup. For non removable media the device should verify + that the device is accessable and if so set the DRIVE_FLAGS_VALID + bit. For removable media the device should verify that the device + controller is accessable and if so set the DRIVE_FLAGS_VALID and + DRIVE_FLAGS_REMOVABLE bits. pargs is not used. + + DEVCTL_POWER_RESTORE - Tell the device driver that system power + has been restored. The driver should attempt to restore the + device to the "UP" state. ERTFS never calls the driver with this + opcode. System integrators may call it to inform the driver of + power restore. pargs is not used. + + DEVCTL_POWER_LOSS - Tell the device driver that system power + has been lost or will soon be lost. The driver should attempt to + go into low power mode. ERTFS never calls the driver with this + opcode. System integrators may call it to inform the driver of + power loss. pargs is not used. + + DEVCTL_GET_GEOMETRY - ERTFS calls this routine to get the values + it will use to partition and format volumes. + pargs points to a structure of type DEV_GEOMETRY. The driver + should fill in the values as defined below. + typedef struct dev_geometry { + - Geometry in HCN format + int dev_geometry_heads; - Must be < 256 + int dev_geometry_cylinders; - Must be < 1024 + int dev_geometry_secptrack; - Must be < 64 + BOOLEAN fmt_parms_valid; If the device io control call sets + this TRUE then it it telling the + the format parameters are valid + and should be used. This is a way to + format floppy disks exactly as they + are formatted by dos. + FMTPARMS fmt; + } DEV_GEOMETRY; + + DEVCTL_FORMAT - ERTFS calls this routine to physically + format the device. pargs points to the DEV_GEOMETRY structure + that was returned from the GET_GEOMETRY call. The device driver + should physically format the device if needed. + + DEVCTL_REPORT_REMOVE - This can be called by an external + interrupt to tell the driver that the device has been removed. + The pcmcia management interrupt calls this and the pcmcia aware + drivers, PCMSRAM.C and IDE_DRV.C, clear the DRIVE_FLAGS_INSERTED + bit in the drive structure. They later query this from the + CHECKSTATUS code. pargs is not used. + +*/ +#include +#include /* twl modified */ +//#include /* ctr modified */ +#include +#include /* For included devices */ + +/*ctr modified(delete prototype definition of the all driver here.)*/ + +int auto_format_disk(DDRIVE *pdr, byte *drivename); +void drno_to_string(byte *pname, int drno); +void print_device_names(void); + + +#if (STORE_DEVICE_NAMES_IN_DRIVE_STRUCT) +/*ctr modified*/ +#define STORE_DEVICE_NAME(NAME) rtfs_strcpy(current_pdr->device_name, (byte *)NAME); +#else +#define STORE_DEVICE_NAME(NAME) +#endif + + +/*------------------ctr modified start------------------*/ +//BOOLEAN i_rtfs_begin_attach( void); +BOOLEAN i_rtfs_attach( int driveno, DDRIVE* pdr, char* dev_name); +BOOLEAN i_rtfs_end_attach( void); + +extern BOOLEAN rtfs_first_attach; //attach.cのattach APIがまだ未使用ならTRUE + +int enabled_drivers; +DDRIVE* current_pdr; + +BOOLEAN rtfs_init( void) +{ + int j; + DDRIVE* pdr; + + /* Call the user supplied configuration function */ + prtfs_cfg = 0; + /* pc_ertfs_init: Can't used rtfs_set_errno(p_errno); */ + if (!pc_ertfs_config()) /*apicnfig.c*/ + { + return(FALSE); + } + + if (!pc_memory_init()) /*rtkernfn.c*/ + { + return(FALSE); + } + /* Allocate semaphores for all drives here and assign them to the + drive structures. If an allocation fails, fail to initialize */ + pdr = prtfs_cfg->mem_drives_structures; + for (j = 0; j < prtfs_cfg->cfg_NDRIVES; j++, pdr++) + { + /* make sure this drive has a semaphore associated with it */ + pdr->access_semaphore = rtfs_port_alloc_mutex(); + if (!pdr->access_semaphore) + return(FALSE); + } + /*---------- ctr modified ----------*/ + for( j=0; j<26; j++) { + prtfs_cfg->drno_to_dr_map[j] = 0; + } + /*----------------------------------*/ + + enabled_drivers = 0; + current_pdr = prtfs_cfg->mem_drives_structures; + + rtfs_first_attach = FALSE; //ctr modified + + // RTCの初期化 +// OSAPI_RTCINIT(); //ctr modified + + return( TRUE); +} +/*--- ctr modified ---*/ +/*SDK_WEAK_SYMBOL BOOL rtcInit( void) +{ + return( FALSE); +}*/ +/*--------------------*/ + +/*---------------------------------------------------------------------------* + Name: rtfs_attach + + Description: + + Arguments: driveno : ドライブ番号 + + Returns: + *---------------------------------------------------------------------------*/ +SDK_WEAK_SYMBOL BOOLEAN i_rtfs_attach( int driveno, DDRIVE* pdr, char* dev_name) +{ + return( FALSE); +/* if (++enabled_drivers > prtfs_cfg->cfg_NDRIVES) { + osTPrintf( "%d / %d\n", enabled_drivers, prtfs_cfg->cfg_NDRIVES); + return( FALSE); + } + + current_pdr->driveno = driveno; + current_pdr->dev_table_drive_io = pdr->dev_table_drive_io; + current_pdr->dev_table_perform_device_ioctl = pdr->dev_table_perform_device_ioctl; + STORE_DEVICE_NAME(dev_name) + current_pdr->register_file_address = pdr->register_file_address; + current_pdr->interrupt_number = pdr->interrupt_number; + current_pdr->drive_flags = pdr->drive_flags; + current_pdr->partition_number = pdr->partition_number; + current_pdr->pcmcia_slot_number = pdr->pcmcia_slot_number; + current_pdr->controller_number = pdr->controller_number; + current_pdr->logical_unit_number = pdr->logical_unit_number; + + current_pdr++; + return( TRUE);*/ +} + +/*---------------------------------------------------------------------------* + Name: rtfs_detach + + Description: + + Arguments: driveno : ドライブ番号 + + Returns: + *---------------------------------------------------------------------------*/ +/*BOOLEAN rtfs_detach( int driveno) +{ + DDRIVE *target_pdr; + + target_pdr = prtfs_cfg->drno_to_dr_map[pdr->driveno]; + if( target_pdr->dev_table_drive_io == default_pdr->dev_table_drive_io) { + return( FALSE); //既にdetach済み + } + + target_pdr->driveno = driveno; + target_pdr->dev_table_drive_io = default_pdr->dev_table_drive_io; + target_pdr->dev_table_perform_device_ioctl = default_pdr->dev_table_perform_device_ioctl; + rtfs_strcpy(target_pdr->device_name, (byte *)"DEFAULT\0"); //STORE_DEVICE_NAME(dev_name) + target_pdr->register_file_address = default_pdr->register_file_address; + target_pdr->interrupt_number = default_pdr->interrupt_number; + target_pdr->drive_flags = default_pdr->drive_flags; + target_pdr->partition_number = default_pdr->partition_number; + target_pdr->pcmcia_slot_number = default_pdr->pcmcia_slot_number; + target_pdr->controller_number = default_pdr->controller_number; + target_pdr->logical_unit_number = default_pdr->logical_unit_number; + + return( TRUE); +}*/ + + +BOOLEAN i_rtfs_end_attach( void) +{ +// current_pdr = prtfs_cfg->mem_drives_structures; + return( FALSE); +} + +/*----------------ctr modified end---------------*/ + + +BOOLEAN pc_ertfs_init(void) /* __apifn__ */ +{ +int j; +DDRIVE *pdr; +//int drives_used; +byte drname[8]; /* Temp buffer for displaying drive letters as strings */ +int default_drive; + + if( rtfs_first_attach == FALSE) { return( TRUE); } //ctr modified + + pdr = prtfs_cfg->mem_drives_structures; + +// drives_used = 0; + + //ctr modified : erase attach codes of some drivers here. + + /* End User initialization section */ + + print_device_names(); + + pdr= prtfs_cfg->mem_drives_structures; + default_drive = 27; /* greater than maximum legal value */ + for (j = 0; j < prtfs_cfg->cfg_NDRIVES; j++, pdr++) + { + if (pdr->dev_table_drive_io) + { + prtfs_cfg->drno_to_dr_map[pdr->driveno] = pdr; /* MAPS DRIVE structure to DRIVE: */ + if (pdr->dev_table_perform_device_ioctl(pdr->driveno, DEVCTL_WARMSTART, (void *) 0) != 0) + { + prtfs_cfg->drno_to_dr_map[pdr->driveno] = 0; /* It is not there. */ + /* so forget it */ + continue; + } +#if (INCLUDE_FAILSAFE_CODE) + /* Call the the ertfs pro failsafe autoinit function */ + if (pdr->drive_flags&DRIVE_FLAGS_FAILSAFE) + { + if (!pro_failsafe_auto_init(pdr)) //マニュアル参照 + { + prtfs_cfg->drno_to_dr_map[pdr->driveno] = 0; /* Forget it, not there. */ + continue; + } + } +#endif + /* Set the default drive to the lowest assigned drive letter */ + if (pdr->driveno < default_drive) + { + default_drive = pdr->driveno; + } + } + } + + /* If there are no valid devices return failure + otherwise use the lowest valid drive id as the system's default drive*/ + if (default_drive == 27) + return(FALSE); + prtfs_cfg->default_drive_id = default_drive; + + RTFS_PRINT_STRING_1(USTRING_RTFSINIT_05,PRFLG_NL); /* "Autoformatting RAM Devices\n" */ + RTFS_PRINT_STRING_1(USTRING_RTFSINIT_06,PRFLG_NL); /* "==========================\n" */ + + pdr= prtfs_cfg->mem_drives_structures; + for (j = 0; j < prtfs_cfg->cfg_NDRIVES; j++, pdr++) + { + if ( pdr->drive_flags&DRIVE_FLAGS_VALID && pdr->drive_flags&DRIVE_FLAGS_FORMAT) + { + OS_CLAIM_LOGDRIVE(pdr->driveno) /* Autoformat Register drive in use */ + drno_to_string(drname, pdr->driveno); + RTFS_PRINT_STRING_2(USTRING_RTFSINIT_07, drname,0); /* "Autoformatting Drive Id - " */ +#if (STORE_DEVICE_NAMES_IN_DRIVE_STRUCT) + RTFS_PRINT_STRING_2(USTRING_RTFSINIT_08, pdr->device_name,PRFLG_NL); /* " as Device: " */ +#endif + if (auto_format_disk(pdr, drname) != 0) + { + RTFS_PRINT_STRING_2(USTRING_RTFSINIT_09, drname,PRFLG_NL); /* "Autoformatting Drive Id - \n" */ + } + OS_RELEASE_LOGDRIVE(pdr->driveno) /* Autoformat release */ + } + } + RTFS_PRINT_STRING_1(USTRING_RTFSINIT_10, PRFLG_NL); /* " = " */ + return(TRUE); +need_more_drives: + RTFS_PRINT_STRING_1(USTRING_RTFSINIT_11, PRFLG_NL); /* "NDRIVES is too small to mount all devices" */ + return(FALSE); +} + + +int auto_format_disk(DDRIVE *pdr, byte *drivename) +{ +DEV_GEOMETRY geometry; + + /* check media and clear change conditions */ + if (!check_drive_number_present(pdr->driveno)) + return(-1); + + /* This must be called before calling the later routines */ + if (!pc_get_media_parms(drivename, &geometry)) + return(-1); + + /* Call the low level media format. Do not do this if formatting a + volume that is the second partition on the drive */ + if (!pc_format_media(drivename, &geometry)) + return(-1); + + if (!pc_format_volume(drivename, &geometry)) + return(-1); + return (0); +} +void drno_to_string(byte *pname, int drno) +{ +byte c,*p; + p = pname; + c = (byte) ('A' + drno); + CS_OP_ASSIGN_ASCII(p,c); + CS_OP_INC_PTR(p); + CS_OP_ASSIGN_ASCII(p,':'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); +} + +void print_device_names(void) +{ +int j; +DDRIVE *pdr; +byte drname[8]; + + RTFS_PRINT_STRING_1(USTRING_RTFSINIT_01,PRFLG_NL); /* "ERTFS Device List" */ + RTFS_PRINT_STRING_1(USTRING_RTFSINIT_02,PRFLG_NL); /* "=================" */ +#if (!STORE_DEVICE_NAMES_IN_DRIVE_STRUCT) + RTFS_PRINT_STRING_1(USTRING_RTFSINIT_03,PRFLG_NL); /* "Name Logging Disabled" */ +#else + + pdr = prtfs_cfg->mem_drives_structures; + for (j = 0; j < prtfs_cfg->cfg_NDRIVES; j++, pdr++) + { + if (pdr->dev_table_drive_io) + { + drno_to_string(drname, pdr->driveno); + RTFS_PRINT_STRING_2(USTRING_RTFSINIT_12, pdr->device_name,0); /* "Device name : " */ + RTFS_PRINT_STRING_2(USTRING_RTFSINIT_13, drname,PRFLG_NL); /* " Is mounted on " */ + } + } + RTFS_PRINT_STRING_1(USTRING_RTFSINIT_04,PRFLG_NL); /* " = " */ +#endif +} diff --git a/build/libraries/fatfs/ARM7/apimkdir.c b/build/libraries/fatfs/ARM7/apimkdir.c new file mode 100644 index 0000000..920d29b --- /dev/null +++ b/build/libraries/fatfs/ARM7/apimkdir.c @@ -0,0 +1,307 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIMKDIR.C - Contains user api level source code. + + The following routines are included: + + pc_mkdir - Create a directory. + pc_rmdir - Delete a directory. + +*/ + +#include + +/*************************************************************************** + PC_MKDIR - Create a directory. + +Description + Create a sudirectory in the path specified by name. Fails if a + file or directory of the same name already exists or if the path + is not found. + + +Returns + Returns TRUE if it was able to create the directory, otherwise + it returns FALSE. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PEINVALIDPATH - Path specified badly formed. + PENOENT - Path to new directory not found + PEEXIST - File or directory of this name already exists + An ERTFS system error +****************************************************************************/ + +BOOLEAN pc_mkdir(byte *name) /*__apifn__*/ +{ + DROBJ *pobj; + DROBJ *parent_obj; + byte *path; + byte *filename; + byte fileext[4]; + BOOLEAN ret_val; + int driveno; + DDRIVE *pdrive; + int p_errno; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + parent_obj = 0; + pobj = 0; + p_errno = 0; + ret_val = FALSE; + rtfs_set_errno(0); /* pc_mkdir: clear error status */ + + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(name); + if (driveno < 0) + { + /* errno was set by check_drive */ + return(FALSE); + } + + pdrive = pc_drno2dr(driveno); + + /* Use the buffers in the DRIVE structure. Access is locked via semaphore */ + path = (byte *)&(pdrive->pathname_buffer[0]); + filename = (byte *)&(pdrive->filename_buffer[0]); + + /* Get out the filename and d:parent */ + if (!pc_parsepath(path,filename,fileext,name)) + { + p_errno = PEINVALIDPATH; + goto errex; + } + /* Find the parent and make sure it is a directory \ */ + /* pc_fndnode set errno */ + parent_obj = pc_fndnode(path); + if (!parent_obj) + goto errex; + + /* Lock the parent */ + if (!pc_isadir(parent_obj) || pc_isavol(parent_obj)) + { + p_errno = PENOENT; /* Path is not a directory */ + goto errex; + } + /* Fail if the directory exists */ + pobj = pc_get_inode(0, parent_obj, filename,(byte*) fileext, GET_INODE_MATCH); + if (pobj) + { + p_errno = PEEXIST; /* Exclusive fail */ + goto errex; + } + else + { + if (get_errno() != PENOENT) /* If pobj is NULL we abort on abnormal errors */ + goto errex; + rtfs_set_errno(0); /* pc_mkdir: clear PENOENT error status */ + pobj = pc_mknode( parent_obj,filename, fileext, ADIRENT, 0); + if (pobj) + { + p_errno = 0; + ret_val = TRUE; + } + else + { + /* pc_mknode has set errno */ + goto errex; + } + } + +errex: + if (pobj) + pc_freeobj(pobj); + if (parent_obj) + { + pc_freeobj(parent_obj); + } + if (p_errno) + rtfs_set_errno(p_errno); + + /*---------- ctr modified ----------*/ + if( pdrive) { + if( pdrive->dev_table_perform_device_ioctl) { + pdrive->dev_table_perform_device_ioctl( driveno, + DEVCTL_FLUSH, + NULL); + } + } + /*----------------------------------*/ + + if (!release_drive_mount_write(driveno))/* Release lock, unmount if aborted */ + ret_val = FALSE; + return(ret_val); +} + + +/**************************************************************************** + PC_RMDIR - Delete a directory. + +Description + + Delete the directory specified in name. Fail if name is not a directory, + is read only or contains more than the entries . and .. + + Returns + Returns TRUE if the directory was successfully removed. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PEINVALIDPATH - Path specified badly formed. + PENOENT - Directory not found + PEACCESS - Directory is in use or is read only + An ERTFS system error +*****************************************************************************/ + +/* Remove a directory */ +BOOLEAN pc_rmdir(byte *name) /*__apifn__*/ +{ + DROBJ *parent_obj; + DROBJ *pobj; + DROBJ *pchild; + BOOLEAN ret_val; + byte *path; + byte *filename; + byte fileext[4]; + int driveno; + DDRIVE *pdrive; + int p_errno; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + parent_obj = 0; + pchild = 0; + pobj = 0; + + rtfs_set_errno(0); /* pc_rmdir: clear error status */ + ret_val = FALSE; + p_errno = 0; + + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(name); + if (driveno < 0) + { + /* errno was set by check_drive */ + return(FALSE); + } + pdrive = pc_drno2dr(driveno); + + /* Use the buffers in the DRIVE structure. Access is locked via semaphore */ + path = (byte *)&(pdrive->pathname_buffer[0]); + filename = (byte *)&(pdrive->filename_buffer[0]); + + /* Get out the filename and d:parent */ + if (!pc_parsepath(path,filename,fileext,name)) + { + p_errno = PEINVALIDPATH; + goto errex; + } + /* Don't allow removal of . or .. PEACCES (by definition we are busy */ + if (pc_isdot(filename,fileext) || pc_isdotdot(filename,fileext)) + { + p_errno = PEACCES; + goto errex; + } + /* Find the parent and make sure it is a directory \ */ + parent_obj = pc_fndnode(path); + if (!parent_obj) + goto errex; /* pc_fndnode set errno */ + + if (!pc_isadir(parent_obj)) + { + p_errno = PEACCES; + goto errex; + } + + /* Find the file and init the structure */ + pobj = pc_get_inode(0, parent_obj, filename, (byte*)fileext,GET_INODE_MATCH); + if (!pobj) + { + /* pc_get_inode() has set errno to PENOENT or to an internal or IO error status */ + goto errex; + } + + if ( !pc_isadir(pobj) || (pobj->finode->opencount > 1) || + (pobj->finode->fattribute & ARDONLY )) + { + p_errno = PEACCES; + goto errex; + } + + /* Search through the directory. look at all files */ + /* Any file that is not . or .. is a problem */ + /* Call pc_get_inode with 0 to give us an obj */ + pchild = pc_get_inode(0, pobj, 0, 0, GET_INODE_STAR); + if (pchild) + { + do + { + if (!(pc_isdot(pchild->finode->fname, pchild->finode->fext) ) ) + if (!(pc_isdotdot(pchild->finode->fname, pchild->finode->fext) ) ) + { + p_errno = PEACCES; + ret_val = FALSE; + goto errex; + } + } + while (pc_get_inode(pchild, pobj, 0, 0, GET_INODE_STAR)); + } + /* If either of the above calls to pc_get_inode() failed due to + an error other than PENOENT then we have an error condition + Continue if the error was PEINVALIDCLUSTER that means that + the chain is corrupted. In this case proceed to delete the + directory too + + */ + p_errno = get_errno(); + if (!p_errno || (p_errno == PENOENT)) /* normal scan termination */ + { + p_errno = 0; + rtfs_set_errno(0); /* Clear errno so others can use it */ + ret_val = pc_rmnode(pobj); + } + else if (p_errno == PEINVALIDCLUSTER) /* termination because of bad + cluster */ + { + /* Clear p_errno, errno is already PEINVALIDCLUSTER, if rmnode + overwrites errno that will be the value, otherwise rmnode and + children will set errno to another value */ + p_errno = 0; + pc_rmnode(pobj); + ret_val = FALSE; + + } + else /* Scan terminated due to IO error or internal error */ + ret_val = FALSE; + +errex: + if (pchild) + pc_freeobj(pchild); + if (pobj) + pc_freeobj(pobj); + if (parent_obj) + pc_freeobj(parent_obj); + if (p_errno) + rtfs_set_errno(p_errno); + + /*---------- ctr modified ----------*/ + if( pdrive) { + if( pdrive->dev_table_perform_device_ioctl) { + pdrive->dev_table_perform_device_ioctl( driveno, + DEVCTL_FLUSH, + NULL); + } + } + /*----------------------------------*/ + + if (!release_drive_mount_write(driveno))/* Release lock, unmount if aborted */ + ret_val = FALSE; + return(ret_val); +} diff --git a/build/libraries/fatfs/ARM7/apirealt.c b/build/libraries/fatfs/ARM7/apirealt.c new file mode 100644 index 0000000..ac7d470 --- /dev/null +++ b/build/libraries/fatfs/ARM7/apirealt.c @@ -0,0 +1,1090 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIREALT.C - Contains user level real time and special user level + file and FAT manipuation code. + + The following routines are included: + + pc_custer_size - Return the number of bytes per cluster for a drive + po_extend_file - Extend a file by with contiguous clusters. + po_chsize - Truncate or extend an open file. + pc_get_file_extents - Get the list of block segments that make up a file + pc_raw_read - Read blocks directly from a disk + pc_raw_write - Write blocks directly to a disk + pc_get_free_list - Get a list free cluster segments on the drive + +*/ + +#include + +/* PC_CLUSTER_SIZE - Return the number of bytes per cluster for a drive + + Description + This function will return the cluster size mounted device + named in the argument. + + + Returns + The cluster size or zero if the device is not mounted. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive name is invalid +*****************************************************************************/ +int pc_cluster_size(byte *drive) /*__apifn__*/ +{ +int driveno; +int ret_val; +DDRIVE *pdrive; +CHECK_MEM(int, 0) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_cluster_size: clear error status */ + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(drive); + if (driveno < 0) + { + /* errno was set by check_drive */ + return(0); + } + pdrive = pc_drno2dr(driveno); + ret_val = pdrive->bytespcluster; + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + return(ret_val); +} + +/**************************************************************************** + +Name: + + po_extend_file - Extend a file by with contiguous clusters. + +Summary: + + BOOLEAN po_extend_file(PCFD fd, dword_bytes, dword_*new_bytes, dword start_cluster, + int method) + + Description + Given a file descriptor, n_bytes bytes and method, extend the file and + update the file size. + If the file can be extended by n_bytes contiguous bytes it will be done. + Otherwise if there is no contiguos region left on the disk that can + contain n_bytes the routine DOES NOT extend the file but does return + the length of the next largest contiguous region that is avaliable. + With this scheme an application can request to extend the file by a + given contiguous amount. If that is not possible the routine will return + the largest contiguous region available, the application can then decide + if it wishes to use this region. Please read the notes below for an + important discussion about the limitations of this routine. + + The special method PC_FIXED_FIT may be used to extend the file beginning + at a specific cluster. With this method, start_cluster must be provided. + It is used as the starting point for the allocation. With this method it + is possible to precisely assign locations on the disk to file section. + This may be used for example to create interleaved files where several + files share a disk segment with interleaving clusters. + + + Method may be one of the following: + PC_FIRST_FIT - The first chain in which the extension will fit + PC_BEST_FIT - The smallest chain in which the extension will fit + PC_WORST_FIT - The largest chain in which the extension will fit + PC_FIXED_FIT - Extend n_clusters from start cluster + + Please note the following issues and limitations. + + . PC_FIRST_FIT is significantly faster than the others + . If the current end of file is not on a cluster boundary + then the region to be tested will start at the cluster + immediatley following the last cluster in the file and the + routine will allocate from the segment that starts with + that cluster or it will return the number of contiguous + bytes available starting at that cluster. + . If possible you should allocate space in contiguouos regions + that are a multiple of the drive s cluster size. + . If the PC_FIXED_FIT option is selected a start cluster must + must be supplied, n_bytes must be an even multiple of cluster + size and the file to extend must be either zero + sized or the end of file must be on a cluster boundary. + + Returns + FALSE if an error occured. + TRUE if an error did not occur. + + Returns n_bytes in *new_bytes if the file was extended. Otherwise it + returns the largest contiguous chain of bytes available in *new_bytes. + If it n_bytes is not returned the files was not extended. + + errno is set to one of these values + 0 - No error + PEBADF - Invalid file descriptor + PEACCES - File is read only + PEINVALIDPARMS - Invalid or inconsistent arguments + An ERTFS system error +*****************************************************************************/ + +BOOLEAN po_extend_file(PCFD fd, dword n_bytes, dword *new_bytes, dword start_cluster, int method) /* __apifn__ */ +{ + BOOLEAN ret_val; + CLUSTERTYPE clno; + dword n_alloced; + dword ltemp; + CLUSTERTYPE n_clusters; + CLUSTERTYPE largest_chain; + CLUSTERTYPE first_cluster; + CLUSTERTYPE last_cluster_in_chain; + CLUSTERTYPE i; + dword alloced_size; + dword new_alloced_size; + dword new_file_size; + dword range_check; + dword clusters_in_chain; + + PC_FILE *pfile; + DDRIVE *pdr; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + if (!n_bytes) + { + ret_val = TRUE; + *new_bytes = 0; + goto return_error; + } + + rtfs_set_errno(0); /* po_extend_file: clear error status */ + + /* Assume error to start */ + ret_val = FALSE; + /* Get the file structure and semaphore lock the drive */ + pfile = pc_fd2file(fd, PO_WRONLY|PO_RDWR); + /* Make sure we have write privileges. Make sure we got a count */ + if (!pfile) + { /* fd2file set errno */ + goto return_error; + } + first_cluster = 0; + /* From here on we exit through alloc_done so we will unlock these resources */ + pdr = pfile->pobj->pdrive; + /* Make sure our file pointer is ok */ + if (!_synch_file_ptrs(pfile)) + { /* synch file pointer set errno */ + goto alloc_done; + } + new_file_size = pfile->pobj->finode->fsize + n_bytes; + /* Fail if it exceeds MAX_FILE_SIZE or it wraps 32 bit offset max */ + if (new_file_size > RTFS_MAX_FILE_SIZE || new_file_size < pfile->pobj->finode->fsize) + { + rtfs_set_errno(PETOOLARGE); /* po_extend_file: new size too large */ + goto alloc_done; + } + new_alloced_size = (new_file_size + pdr->byte_into_cl_mask) & + ~(pdr->byte_into_cl_mask); + if (new_alloced_size < new_file_size) + new_alloced_size = 0xffffffff; + + /* Round the file size up to its cluster size by adding in clustersize-1 + and masking off the low bits */ + alloced_size = (pfile->pobj->finode->fsize + pdr->byte_into_cl_mask) & + ~(pdr->byte_into_cl_mask); + if (alloced_size < pfile->pobj->finode->fsize) + alloced_size = 0xffffffff; + + if (method == PC_FIXED_FIT) + { + /* Check for errors. If the old size was not on a cluster + boundary or the new size is not on a cluster boundary + or now start cluster was provided then its an error + for the FIXED_FIT option + */ + if ( ( new_alloced_size != new_file_size) || + (alloced_size != pfile->pobj->finode->fsize) || + !start_cluster) + { + /* If using fixed fit and not on a cluster boundary + it is an error */ + rtfs_set_errno(PEINVALIDPARMS); /* po_extend_file: fixed fit and not on a cluster boundary */ + goto alloc_done; + } + } + n_clusters = pc_chain_length(pdr, new_file_size) - + pc_chain_length(pdr, pfile->pobj->finode->fsize); + + if (n_clusters) + { /* We need space so try to allocate */ + /* Find the end of the files chain */ + last_cluster_in_chain = pfile->fptr_cluster; + clno = pfile->fptr_cluster; + { + dword ltemp,fptr_mod_cluster; + /* Round fptr down to cluster boundary */ + fptr_mod_cluster = pfile->fptr & ~(pdr->byte_into_cl_mask); + /* Subtract from the rounded up alloced_size value */ + ltemp = alloced_size-fptr_mod_cluster; + clusters_in_chain = ltemp >> (int) (pfile->pobj->pdrive->log2_secpalloc+9); + } + if (clusters_in_chain) + { + if ((clno < 2) || (clno > pdr->maxfindex) ) + { + rtfs_set_errno(PEINVALIDCLUSTER); + goto alloc_done; + } + range_check = 0; + while (clno != 0xffffffff) + { + if (++range_check > clusters_in_chain) + break; + last_cluster_in_chain = clno; + clno = FATOP(pdr)->fatop_clnext(pdr , clno); /* File */ + if (!clno) /* clnext set errno */ + goto alloc_done; + } + if (range_check != clusters_in_chain) + { + rtfs_set_errno(PEINVALIDCLUSTER); + goto alloc_done; + } + if (get_errno()) + goto alloc_done; /* If FATOP set errno we have trouble */ + } + n_alloced = 0; + largest_chain = 0; + if ((method == PC_FIXED_FIT) || (alloced_size != pfile->pobj->finode->fsize)) + { + /* If the end of file is not on a cluster boundary + see how many are avalailable that are contiguous + with the last cluster in our file */ + /* Also have special code here since FIXED_FIT Works this way */ + if (method == PC_FIXED_FIT) + clno = (CLUSTERTYPE) start_cluster; + else + clno = (CLUSTERTYPE) (last_cluster_in_chain + 1); + + ltemp = n_clusters; + /* Walk up the FAT linearly. try for n_clusters */ + while (ltemp) + { + if (!FATOP(pdr)->fatop_faxx(pdr, clno, &i) || i != 0) /* File */ + break; + else + { + largest_chain++; + ltemp--; + clno++; + } + } + if (get_errno()) + goto alloc_done; /* If FATOP set errno we have trouble */ + if (!ltemp) + { + /* We got a contiguous region */ + n_alloced = n_clusters; + largest_chain = 0; + /* That starts here */ + if (method == PC_FIXED_FIT) + first_cluster = (CLUSTERTYPE) start_cluster; + else + first_cluster = (CLUSTERTYPE) last_cluster_in_chain + 1; + } + } + else + { + /* Now allocate clusters. To find the free space we look in three + regions until we find space: + 1 we look from the last cluster in the file to the end of the fat + (skip 1 if there is no chain) + 2 we look from the beginning of the data area to the end of the fat + 3 we look from the beginning of the fat area to the end of the fat + */ + + clno = last_cluster_in_chain; + if (!clno) + clno = pdr->free_contig_base; + while (clno) + { + n_alloced = pc_find_contig_clusters(pdr, clno, &first_cluster, n_clusters, method); + if (n_alloced == 0xfffffffful) + { + /* Fat operations have set errno */ + ret_val = FALSE; + goto alloc_done; + } + else if ((CLUSTERTYPE)n_alloced >= n_clusters) + break; /* We got our chain */ + else + { + /* We did not get enough space. keep track of the biggest chain. + Do not need to store first_cluster since we will not alocate chains + smaller than what we need */ + if (largest_chain < (CLUSTERTYPE)n_alloced) + largest_chain = (CLUSTERTYPE)n_alloced; + } + /* If we were searching between from the end of the file and end of fat + look from the beginning of the file data area */ + if (clno == last_cluster_in_chain) + { + if (last_cluster_in_chain == pdr->free_contig_base) + clno = 2; + else + clno = pdr->free_contig_base; + } + /* If we were searching between the beginning of the file data area + and end of fat look from the fat */ + else if (clno == pdr->free_contig_base) + { + if (pdr->free_contig_base == 2) + break; + else + clno = 2; + } + else /* We have looked everywhere. No luck */ + break; + } + } + + if ((CLUSTERTYPE)n_alloced < n_clusters) + { + /* We did not get what we asked for so we return the biggest free + contiguous chain available in bytes plus whatever is left + in the last cluster */ + *new_bytes = (largest_chain << (pdr->log2_secpalloc+9)); + if (alloced_size > pfile->pobj->finode->fsize) /* Avoid wrap */ + *new_bytes += (alloced_size - pfile->pobj->finode->fsize); + goto alloc_done; + } + /* else */ + + /* We found a large enough contiguos group of clusters */ + /* Turn them into a chain */ + clno = first_cluster; + for (i = 0; i < (n_clusters-1); i++, clno++) + { + /* Link the current cluster to the next one */ + /* If it fails FATOP will set errno */ + if (!FATOP(pdr)->fatop_pfaxx(pdr, clno, (CLUSTERTYPE) (clno+1) )) /* File */ + goto alloc_done; + /* 2-19-99 - Bug fix. was not decrementing free cluster count */ + pdr->known_free_clusters = (long)(pdr->known_free_clusters - 1); + } + + /* Terminate the list */ + if (!FATOP(pdr)->fatop_pfaxxterm(pdr, clno)) /* File */ + { /* If it fails FATOP will set errno */ + goto alloc_done; + } + else + { + /* 2-19-99 - Bug fix. was not decrementing free cluster count */ + pdr->known_free_clusters = (long)(pdr->known_free_clusters - 1); + } + if (last_cluster_in_chain) + { + /* The file already has clusters in it. Append our new chain */ + if (!FATOP(pdr)->fatop_pfaxx(pdr, last_cluster_in_chain, first_cluster)) /* File */ + { /* If it fails FATOP will set errno */ + goto alloc_done; + } + } + else + { + /* Put our chain into the directory entry */ + pc_pfinode_cluster(pfile->pobj->pdrive,pfile->pobj->finode,first_cluster); + /* Use synch_pointers to set our file pointers up */ + pfile->fptr_cluster = 0; /* This is already true but... */ + pfile->fptr_block = 0; + pfile->fptr = 0; + } + /* Flush the fat */ + if (!FATOP(pdr)->fatop_flushfat(pdr->driveno)) + { /* If it fails FATOP will set errno */ + goto alloc_done; + } + } + + /* If we get here it works and we should update the file size */ + *new_bytes = n_bytes; + ret_val = TRUE; + + pfile->pobj->finode->fsize = new_file_size; + /* Write the directory entry. Set archive & date */ + if (!pc_update_inode(pfile->pobj, TRUE, TRUE) ) + { /* Update inode set errno */ + ret_val = FALSE; + goto alloc_done; + } + /* call synch to take care of both the eof condition and the case where + we just alloced the beginning of the chain */ + if (!_synch_file_ptrs(pfile)) + { /* synch file pointer set errno */ + ret_val = FALSE; + goto alloc_done; + } + /* All code exits through here. ret_val determines if the function was + successful. If -1 it is an error. */ +alloc_done: + if (!release_drive_mount_write(pdr->driveno))/* Release lock, unmount if aborted */ + ret_val = FALSE; +return_error: + return(ret_val); +} + +/****************************************************************************** + PC_FIND_CONTIG_CLUSTERS - Find at least MIN_CLUSTER clusters. + + + Description + Using the provided method, search the FAT from start_pt to the + end for a free contiguous chain of at least MIN_CLUSTERS. If less + than MIN_CLUSTERS are found the largest free chain in the region is + returned. + + There are three possible methods: + PC_FIRST_FIT - The first free chain >= MIN_CLUSTERS is returned + PC_BEST_FIT - The smallest chain >= MIN_CLUSTERS is returned + PC_WORST_FIT - The largest chain >= MIN_CLUSTERS is returned + + Choose the method that will work best for you. + + Note: PC_FIRST_FIT is significantly faster faster than the others + + NOTE: The chain is not created. The caller must convert the + clusters to an allocated chain. + + Returns + Returns the number of contiguous clusters found up to MIN_CLUSTERS. + *pchain contains the cluster number at the beginning of the chain. + On error return 0xffff + Example: + Get the largest free chain on the disk: + large = pc_find_contig_clusters(pdr, 2, &chain, 0xffff, PC_FIRST_FIT); + +*****************************************************************************/ +/* Note: the FAT is locked before this call is made */ + +dword pc_find_contig_clusters(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE *pchain, CLUSTERTYPE min_clusters, int method) /* __apifn__ */ +{ +CLUSTERTYPE i; +CLUSTERTYPE value; +CLUSTERTYPE best_chain; +CLUSTERTYPE best_size; +CLUSTERTYPE chain_start; +CLUSTERTYPE chain_size; +CLUSTERTYPE largest_size; +CLUSTERTYPE largest_chain; +CLUSTERTYPE endpt; + + best_chain = 0; + best_size = 0; + chain_start = 0; + chain_size = 0; + largest_size = 0; + largest_chain = 0; + endpt = pdr->maxfindex; + + for (i = startpt; i <= endpt; i++) + { + if (!FATOP(pdr)->fatop_faxx(pdr, i, &value) ) /* File */ + return(0xfffffffful); /* IO error .. oops */ + if (value == 0) + { + /* Cluster is free. Run some tests on it. */ + if (chain_start) + { + /* We are in a contiguous region already. Bump the count */ + chain_size++; + } + else + { + /* Just starting a contiguous region */ + chain_size = 1; + chain_start = i; + } + /* If using first fit see if we crossed the threshold */ + if (method == PC_FIRST_FIT) + { + if (chain_size >= min_clusters) + { + best_chain = chain_start; + best_size = chain_size; + break; + } + } + } /* if value == 0*/ + /* Did we just finish scanning a contiguous chain ?? */ + if (chain_size && ((value != 0) || (i == endpt)) ) + { + /* Remember the largest chain */ + if (chain_size > largest_size) + { + largest_size = chain_size; + largest_chain = chain_start; + } + if (method == PC_BEST_FIT) + { + if (chain_size == min_clusters) + { + /* The chain is exactly the size we need take it. */ + best_chain = chain_start; + best_size = chain_size; + break; + } + if (chain_size > min_clusters) + { + if (!best_chain || (chain_size < best_size)) + { + /* Chain is closest to what we need so far note it. */ + best_size = chain_size; + best_chain = chain_start; + } + } + } /* if BEST_FIT */ + else if (method == PC_WORST_FIT) + { + if (chain_size >= min_clusters) + { + if (!best_chain || chain_size > best_size) + { + best_size = chain_size; + best_chain = chain_start; + } + } + } /* if WORST_FIT */ +/* +* else if (method == PC_BEST_FIT) +* ; +*/ + chain_size = 0; + chain_start = 0; + } /* if (chain_size && ((value != 0) || (i == endpt)) ) */ + } /* for (i = startpt; i <= endpt; i++) */ + + /* If we have a best chain return it here. Else return the largest chain */ + if (best_chain) + { + *pchain = best_chain; + return((dword)best_size); + } + else + { + *pchain = largest_chain; + return((dword)largest_size); + } + +} + +/************************************************************************** + PO_CHSIZE - Truncate or extend an open file. + + Description + Given a file handle and a new file size either extend the file or + truncate it. If the current file pointer is still within the range + of the file it is unmoved, otherwise it is moved to the end of file. + + Note: + This is not an ATOMIC file system operation. It uses other API calls + po_lseek, po_truncate and po_write to size, truncate and extend the + file. + + Returns + Returns 0 on suucess -1 on error + + errno is set with one of these values + 0 - No error + PEBADF - Invalid file descriptor + PEACCES - File is read only + PEINVALIDPARMS - Invalid or inconsistent arguments + An ERTFS system error +*****************************************************************************/ + +int po_chsize(PCFD fd, dword offset) /*__apifn__*/ +{ + dword orig_fp; + dword eof_fp; + dword n_to_extend; + dword n_to_try; + dword ltemp; + int ret_val; + + rtfs_set_errno(0); /* po_chsize: clear error status. routines that we call will set errno on errors */ + + if (!po_ulseek(fd, 0, &orig_fp, PSEEK_CUR)) + return(-1); + if (!po_ulseek(fd, 0,&eof_fp, PSEEK_END)) + return(-1); + + /* If size is unchanged just return */ + if (eof_fp == offset) + { + ret_val = 0; + } + /* If offset < eof we truncate */ + else if (eof_fp > offset) + { + if (!po_truncate(fd, offset)) + return(-1); + /* Restore the file pointer if we can */ + if (orig_fp < offset) + po_ulseek(fd, orig_fp,<emp, PSEEK_SET); + return(0); + } + else + { + if (offset > RTFS_MAX_FILE_SIZE) + { + rtfs_set_errno(PETOOLARGE); /* po_extend_file: new size too large */ + return(-1); + } + + /* Have to extend the file */ + n_to_extend = offset - eof_fp; + ret_val= 0; /* Assume it worked at first */ + while (n_to_extend) + { + /* Try to extend the file. po_extend_file will either extend the + file and return the value we sent in or it will not extend the + file and return a hint of what should work */ + n_to_try = n_to_extend; + while (n_to_try) + { + if (!po_extend_file(fd, n_to_try, <emp, 0, PC_FIRST_FIT)) + { + ret_val = -1; + n_to_extend = 0; /* Force break from the outer loop */ + break; + } + else + { + if (ltemp == n_to_try) + { + n_to_extend -= ltemp;/* We extended by n_to_try */ + n_to_try = 0; /* Break from inner loop */ + } + else + n_to_try = ltemp; /* We could not extend but ltemp + should to work so try again*/ + } + } + } + } + if (!ret_val) + { + /* restore seek pointer and return */ + if (!po_ulseek(fd, orig_fp, <emp, PSEEK_SET) || ltemp != orig_fp) + return(-1); + } + return(ret_val); +} + + +/*************************************************************************** +Name: + + pc_get_file_extents - Get the list of block segments that make up a file + + +Summary: + + #include + + int pc_get_file_extents(PCFD fd, int infolistsize, + FILESEGINFO *plist, BOOLEAN raw) + + Where FILESEGINFO is a structure defined as: + + typedef struct fileseginfo { + long block; Block number of the current extent + long nblocks; Number of blocks in the extent + } FILESEGINFO; + + And infolistsize is the number of elements in the storage pointed to + by plist. + + If raw is TRUE the blocks are reported as block offsets from the physical + base of the drive. Otherwise the block offset origin is the begining of + the partition. Set raw to TRUE if you will be using the resultant list + to set up DMA transfers to or from the disk. + + Description: + This routines traverse the cluster chain of the open file fd and logs + into the list at plist the block location and length in blocks of each + segment of the file. + The block numbers and block length information can then be used to + read and write the file directly using pc_raw_read() and pc_raw_write() + or the information may be used to set up DMA transfers to or from the + raw block locations. + If the file contains more extents than will fit in plist as indicated + by infolistsize then the list is not updated beyond infolistsize elements + but the count is updated and returned so the list size may be adjusted + and the routine may be called again. + + Returns + Returns the number of extents in the file or -1 on error. + + errno is set to one of the following + + 0 - No error + PEBADF - Invalid file descriptor + An ERTFS system error +****************************************************************************/ + +int pc_get_file_extents(PCFD fd, int infolistsize, FILESEGINFO *plist, BOOLEAN raw) /* __apifn__ */ +{ + int p_errno; + int end_of_chain; + int ret_val; + int n_segments; + CLUSTERTYPE first_cluster; + CLUSTERTYPE next_cluster; + CLUSTERTYPE n_clusters_to_seek; + CLUSTERTYPE n_clusters; + long partition_start; + PC_FILE *pfile; + DDRIVE *pdrive; + CHECK_MEM(int, -1) /* Make sure memory is initted */ + + p_errno = 0; + rtfs_set_errno(0); /* pc_get_file_extents: clear error status. */ + + /* Get the file structure and semaphore lock the drive */ + pfile = pc_fd2file(fd, 0); + if (!pfile) + { /* pc_fd2file set errno */ + ret_val = -1; + goto return_error; + } + pdrive = pfile->pobj->pdrive; + + partition_start = pdrive->partition_base; + + ret_val = 0; /* Assume Zero segments to start */ + if (pfile->pobj->finode->fsize) + { + n_segments = 0; + /* How many clusters in the file */ + n_clusters_to_seek = (CLUSTERTYPE) + ((pfile->pobj->finode->fsize+(pdrive->secpalloc<<9)-1) >> (pdrive->log2_secpalloc+9)); + first_cluster = pc_finode_cluster(pdrive,pfile->pobj->finode); + + /* Traverse the file one chain at a time. Log start point (in blocks) + and the length in blocks of each segment */ + while (n_clusters_to_seek) + { + /* Get the chain length and the start of the next chain */ + end_of_chain = 0; + n_clusters = FATOP(pdrive)->fatop_get_chain(pdrive, first_cluster, + &next_cluster, n_clusters_to_seek, &end_of_chain); + if (!n_clusters) + { + if (!get_errno()) /* If errno set already use that */ + p_errno = PEINVALIDCLUSTER; + n_segments = -1; + break; + } + + n_segments += 1; + if (n_segments <= infolistsize) + { + plist->block = pc_cl2sector(pdrive,first_cluster); + if (raw) + plist->block += partition_start; + plist->nblocks = n_clusters << pdrive->log2_secpalloc; + plist++; + } + n_clusters_to_seek = (CLUSTERTYPE) (n_clusters_to_seek - n_clusters); + /* Check for corrupted file + If there are more to seek but the cluster pointer + is at the end this means that the file size is + longer than the actual chain length so set errno + to PEINVALIDCLUSTER and force error return */ + if (n_clusters_to_seek && end_of_chain) + { + p_errno = PEINVALIDCLUSTER; + n_segments = -1; + break; + } + else + first_cluster = next_cluster; + } + ret_val = n_segments; + } + release_drive_mount(pdrive->driveno);/* Release lock, unmount if aborted */ +return_error: + if (p_errno) + rtfs_set_errno(p_errno); + return(ret_val); +} + +/**************************************************************************** +Name: + pc_raw_read - Read blocks directly from a disk + +Summary: + + #include + + int pc_raw_read(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io) + +Description: + Attempt to read nblocks blocks starting at blockno. If raw_io is TRUE + then blockno is the offset from the beginning of the disk itself. If + raw_io is FALSE then blockno is the offset from the beginning of the + partition. + This routine may be used in conjunction with pc_get_file_extents() + to find and read blocks without the additional overhead incurred + when calling po_read(). + + The maximum allowable value for nblocks is 128. + + Note: It is possible to read any range of blocks in the disk. + + Returns: + + Returns 0 if the read succeeded or -1 on error. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID - Driveno is incorrect + PEINVALIDPARMS - Invalid or inconsistent arguments + PEIOERRORREAD - The read operation failed + An ERTFS system error +*****************************************************************************/ + +int pc_raw_read(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io) /*__apifn__*/ +{ + CHECK_MEM(int, -1) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_raw_read: clear error status. */ + + /* return -1 if bad arguments */ + if (!nblocks || !buf || (nblocks > 128)) + { + rtfs_set_errno(PEINVALIDPARMS); + return(-1); + } + if (!check_drive_number_mount(driveno)) /* Check set errno */ + return(-1); + /* READ */ + if (!devio_read(driveno, (dword) blockno, buf, (word)nblocks, raw_io)) + { + rtfs_set_errno(PEIOERRORREAD); + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + return(-1); + } + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + return(0); +} + + +/**************************************************************************** +Name: + pc_raw_write - Write blocks directly to a disk + +Summary: + + #include + + int pc_raw_write(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io) + +Description: + Attempt to write nblocks blocks starting at blockno. If raw_io is TRUE + then blockno is the offset from the beginning of the disk itself. If + raw_io is FALSE then blockno is the offset from the beginning of the + partition. + This routine may be used in conjunction with pc_get_file_extents() + to find and read blocks without the additional overhead incurred + when calling po_read(). + + The maximum allowable value for nblocks is 128. + + Note: It is possible to write any range of blocks in the disk. + + + Returns + Returns 0 if the write succeeded or -1 on error. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID - Driveno is incorrect + PEINVALIDPARMS - Invalid or inconsistent arguments + PEIOERRORWRITE - The read operation failed + An ERTFS system error +*****************************************************************************/ + +int pc_raw_write(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io) /*__apifn__*/ +{ + CHECK_MEM(int, -1) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_raw_write: clear error status. */ + + /* return -1 if bad arguments */ + if (!nblocks || !buf || (nblocks > 128)) + { + rtfs_set_errno(PEINVALIDPARMS); + return(-1); + } + /* we will use release_drive_mount() not release_drive_mount_write() + since we did not change file system structures */ + + if (!check_drive_number_mount(driveno)) /* Check set errno */ + return(-1); + if (!devio_write(driveno, (dword) blockno, buf, (word)nblocks, raw_io)) + { + rtfs_set_errno(PEIOERRORWRITE); + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + return(-1); + } + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + return(0); +} + +/*************************************************************************** +Name: + + pc_get_free_list - Get a list free cluster segments on the drive + + +Summary: + + #include + + int pc_get_free_list(byte *drivename, int listsize, FREELISTINFO *plist, long threshhold) + + Where + + drivename is a valid drive specifier for example C:. An empty string + denotes the current workin drive. + + + FREELISTINFO is a structure defined as: + + typedef struct freelistinfo { + CLUSTERTYPE cluster; Cluster where the free region starts + long nclusters; Number of free clusters the free segment + } FREELISTINFO; + + listsize is the number of elements in the storage pointed to + by plist. + + + threshhold is the smallest contiguous free region to report. + This is provided to allow the caller to exclude free chains + that are too snall to be interesting. Setting this to a higher + value also reduces the number of entries in plist that will + be used up. The value of threshold must be at least 1. If it + is one then every free cluster segment is reported. + + Description: + This routines traverses the file allocation table of the drive. + It places in the results structure the starting point and size + of each free segment + + The free list information may then be used by po_extend_file() to + allocate specific clusters for specific files. + If the FAT contains more free extents than will fit in plist as indicated + by listsize then the list is not updated beyond listsize elements + but the count is updated and returned so the list size may be adjusted + and the routine may be called again. + + Returns + Returns the number of free segments or -1 on error. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID - Driveno is incorrect + PEINVALIDPARMS - Invalid or inconsistent arguments + An ERTFS system error +****************************************************************************/ + + +int pc_get_free_list(int driveno, int listsize, FREELISTINFO *plist, long threshhold) /* __apifn__ */ +{ + CLUSTERTYPE clno; + CLUSTERTYPE value; + int index; + long region_size; + DDRIVE *pdr; + int ret_val; + int p_errno; + CHECK_MEM(PCFD, -1) /* Make sure memory is initted */ + + ret_val = 0; + p_errno = 0; + index = 0; + region_size = 0; + + rtfs_set_errno(0); /* pc_get_free_list: clear error status. */ + + if (threshhold < 1) + { + rtfs_set_errno(PEINVALIDPARMS); + return(-1); + } + + /* Get the drive and make sure it is mounted */ + if (!check_drive_number_mount(driveno)) /* Check set errno */ + return(-1); + + pdr = pc_drno2dr(driveno); + for (clno = 2; clno <= pdr->maxfindex; clno++) + { + if (!FATOP(pdr)->fatop_faxx(pdr, clno, &value) ) /* Fat */ + { + /* Fatop will set errno */ + region_size = 0; + ret_val = -1; + break; + } + if (value == 0) + { + /* The cluster is free */ + if (region_size) + { + /* We are inside a free extent so update counter */ + region_size++; + } + else + { + /* Start tracking a new one */ + region_size = 1; + if (index < listsize) + { + plist->cluster = clno; + } + } + } + else + { + /* Cluster is non-zero see if we need to terminate a region */ + if (region_size >= threshhold && index < listsize) + { + plist->nclusters = region_size; + plist++; + } + if (region_size >= threshhold) + index++; + region_size = 0; + } + } + if (!ret_val) + { + /* If we left the for loop while still in a region note it */ + if (region_size >= threshhold) + { + if (index < listsize) + { + plist->nclusters = region_size; + } + index++; + } + ret_val = index; + } + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + if (p_errno) + rtfs_set_errno(p_errno); + return(ret_val); +} diff --git a/build/libraries/fatfs/ARM7/apiregrs.c b/build/libraries/fatfs/ARM7/apiregrs.c new file mode 100644 index 0000000..c58520e --- /dev/null +++ b/build/libraries/fatfs/ARM7/apiregrs.c @@ -0,0 +1,1139 @@ +/***************************************************************************** + apiregrs.c - RTFS Excerciser + + This program performs two functions. It calls virtually all of the API + routines plus it stress tests the system for driver bugs and memory leaks. + It works by repeatedly opening a disk and then entering an inner loop + which creates a directory and then creates N subdirectories below that. + Finally the inner loop creates NUSERFILES files, writes to them, reads from + them, seeks, truncates, closes, renames and deletes them. Along the way + it check set current working directory and get working directory. Finally + the inner loop deletes all of the subdirectories it created and compares + the current disk free space to the free space before it started. These + should be the same. After the inner loop completes the outer loop closes + the drive and then reopens it to continue the test. + + There are a few functions that do not get tested, they are: + pc_gfirst + pc_gnext + pc_gdone + pc_abort + + Not all modes of po_open and po_lseek are tested either. Nor does it + test your port in multitasking mode. You may modify this program and + run it in multiple threads if you wish. + + The following parameters may be changed: + + USEPRINTF - Set this to zero to run completely quietly. If this + is done you should set a break point in regress_error() + to catch errors. + test_drive[] - The drive where the test will occur. + test_dir[] - The directory where the test will occur + INNERLOOP - The Number of times we run the inner loop + OUTERLOOP - The Number of times we run the inner loop + SUBDIRDEPTH - The depth of the tested subdirectories. + NSUBDIRS - The number of subdirectories below test_dir. Must + be less then 26. Each of these directories will + have SUBDIRDEPTH subdirectories below it. + + +To run the program type REGRESS. + +*****************************************************************************/ + +#include + +#define RGE_FLTDRIVE 3 +#define RGE_FREEERROR 4 +#define RGE_LEAKERROR 5 +#define RGE_MKDIR 6 +#define RGE_SCWD 7 +#define RGE_MKDIRERR 9 +#define RGE_PWD 10 +#define RGE_RMDIR 11 +#define RGE_DSKCLOSE 12 +#define RGE_OPEN 13 +#define RGE_SEEK 14 +#define RGE_WRITE 15 +#define RGE_READ 16 +#define RGE_TRUNC 17 +#define RGE_FLUSH 18 +#define RGE_CLOSE 19 +#define RGE_UNLINK 20 +#define RGE_MV 21 +#define RGE_CHSIZE 22 +#define RGE_DELTREE 23 +#define RGE_ERRNO 24 +#define RGE_LONGFILETEST 25 +#define RGE_FILETEST 26 +#define RGE_LARGEFILETEST 27 + + +/* Porting issues */ +#define VERBOSE 1 /* Set to zero for quiet operation */ +byte test_drive[8]; /* The drive where the test will occur */ +#define NLONGS 512 /* Longs to write in file write test */ +#define SUBDIRDEPTH 10 /* Depth of subdirectories */ +#define NSUBDIRS 8 /* <= 26 Number of subdirs at below RTFSTEST */ + + + +dword test_rtfs_buf[NLONGS]; /* Used in the file write test */ + +#if (VFAT) /* Small piece of compile time VFAT vs's NONVFAT code */ +#if (INCLUDE_CS_UNICODE) +/* Make names a little shorter in Unicode, otherwise path's get too long */ +byte _test_dir[] = CS_OP_ASCII("Directory"); /* Test will occur in this Directory */ +byte _test_file_name[] = CS_OP_ASCII("Long File Name"); +byte _test_newfile_name[] =CS_OP_ASCII("New Long File Name"); +byte _test_subdir_name[] = CS_OP_ASCII("Subdir"); +#else +byte _test_dir[] = CS_OP_ASCII("RTFS_Test_Directory"); /* Test will occur in this Directory */ +byte _test_file_name[] = CS_OP_ASCII("Long File Name"); +byte _test_newfile_name[] =CS_OP_ASCII("New Long File Name"); +byte _test_subdir_name[] = CS_OP_ASCII("Subdir"); +#endif +#else +byte _test_dir[] = CS_OP_ASCII("RTFSTEST"); /* Test will occur in this Directory */ +byte _test_file_name[] = CS_OP_ASCII("FILE"); +byte _test_newfile_name[] =CS_OP_ASCII("NEWFILE"); +byte _test_subdir_name[] = CS_OP_ASCII("SUBDIR"); +#endif +/* Above strings are copied into the following buffers in native cha set */ +byte test_dir[40]; +byte test_file_name[40]; +byte test_newfile_name[40]; +byte test_subdir_name[40]; + +#define INNERLOOP 4 /* Number of times we run the tes suite + between open and close. */ +#define OUTERLOOP 20 /* Number of times we open the drive + run the inner loop and close the drive */ + +BOOLEAN do_test(int loop_count, BOOLEAN do_clean); +BOOLEAN do_file_test(int loop_count, BOOLEAN do_clean); +#if (VFAT) +BOOLEAN do_long_file_test(BOOLEAN do_clean); +#endif + +BOOLEAN do_rm(byte *buffer, int level); +void regress_error(int error); +BOOLEAN check_errno(int expected_error); +BOOLEAN do_buffered_file_test(BOOLEAN do_clean); + +/* Copy strings to native character sets */ +void setup_regress_strings() +{ + /* Set up strings in native character set */ + CS_OP_ASCII_TO_CS_STR(test_dir, (byte *) _test_dir); + CS_OP_ASCII_TO_CS_STR(test_file_name, (byte *) _test_file_name); + CS_OP_ASCII_TO_CS_STR(test_newfile_name, (byte *) _test_newfile_name); + CS_OP_ASCII_TO_CS_STR(test_subdir_name, (byte *) _test_subdir_name); +} + + +#define PATHSIZE 256 + +BOOLEAN pc_regression_test(byte *driveid, BOOLEAN do_clean) /* __api__ */ +{ +int inner_loop; +int outer_loop; +long nfree, nfree_too; +dword blocks_total, blocks_free, blocks_free_too; + + rtfs_cs_strcpy(test_drive, driveid); + + setup_regress_strings(); + + for (outer_loop = 0; outer_loop < OUTERLOOP; outer_loop++) + { +#if (VERBOSE) + RTFS_PRINT_LONG_1((dword) outer_loop, PRFLG_NL); +#endif + + if (!pc_set_default_drive(test_drive)) + { regress_error(RGE_FLTDRIVE); return(FALSE);} + + /* If test_dir is not there, deltree will just fail */ + pc_deltree(test_dir); + /* Test buffered IO support. Loop a few times to check for leaks */ + for (inner_loop = 0; inner_loop < 4; inner_loop++) + { + RTFS_PRINT_STRING_1(USTRING_RTFSDEM_16, PRFLG_NL); /* "Performing buffered file io test" */ + if (!do_buffered_file_test(do_clean)) + return(FALSE); + } + for (inner_loop = 0; inner_loop < INNERLOOP; inner_loop++) + { +#if USEPRINTF +#if (!VERBOSE) + RTFS_PRINT_STRING_1(USTRING_RTFSDEM_01,PRFLG_NL); /* "+" */ +#endif +#endif + /* Check freespace */ + nfree = pc_free(test_drive, &blocks_total, &blocks_free); + if (!nfree) + { + regress_error(RGE_FREEERROR); + return(FALSE); + } + if (!do_test(inner_loop, do_clean)) /* Call the main test routine */ + return(FALSE); + /* Check freespace again. They should match */ + + if (!do_clean) /* If not cleaning up don't recheck freespace */ + return(TRUE);/* And return */ + nfree_too = pc_free(test_drive, &blocks_total, &blocks_free_too); + if (!nfree_too) + { + regress_error(RGE_FREEERROR); + return(FALSE); + } + if (blocks_free_too != blocks_free) + { + regress_error(RGE_LEAKERROR); + return(FALSE); + } + } + } + return(TRUE); +} + +/* + Make the test directory + loop + Step into the test directory + Make another subdiretory + loop + Make N deep subdirectories + Change into each + compare what we know is the directory with + what pc_pwd returns. + End loop + In the lowest level directory + loop + create a file + open it with multiple file descriptors + loop + write to it on multiple FDs + loop + seek and read on multiple FDs. Testing values + flush + truncate + close + end loop + loop + rename + delete + end loop + now delete all of the subdirectories + +*/ +void build_subdir_name(byte *p, int index) +{ +byte c; +/* Create SubDirA, or SubDirB.. SubDir('A'+index) */ + rtfs_cs_strcpy(p,test_subdir_name); + p = CS_OP_GOTO_EOS(p); + /* Put A, or B, C, D.. at the end of the string */ + c = CS_OP_ASCII('A'); + c = (byte) (c + index); + CS_OP_ASSIGN_ASCII(p,c); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); +} + +BOOLEAN do_test(int loop_count, BOOLEAN do_clean) +{ + int i; + int j; + byte *buffer; + byte *buffer3; + byte *buffer4; + byte *home; + byte *p; + BLKBUFF *scratch_buffer; + BLKBUFF *scratch_buffer3; + BLKBUFF *scratch_buffer4; + BLKBUFF *scratch_home; + + scratch_buffer = pc_scratch_blk(); + scratch_buffer3 = pc_scratch_blk(); + scratch_buffer4 = pc_scratch_blk(); + scratch_home = pc_scratch_blk(); + + if (!(scratch_buffer && scratch_buffer3 && scratch_buffer4 && scratch_home)) + { + if (scratch_buffer) + pc_free_scratch_blk(scratch_buffer); + if (scratch_buffer3) + pc_free_scratch_blk(scratch_buffer3); + if (scratch_buffer4) + pc_free_scratch_blk(scratch_buffer4); + if (scratch_home) + pc_free_scratch_blk(scratch_home); + return(FALSE); + } + buffer = (byte *)scratch_buffer->data; + buffer3 = (byte *)scratch_buffer3->data; + buffer4 = (byte *)scratch_buffer4->data; + home = (byte *)scratch_home->data; + +#if (VERBOSE) + RTFS_PRINT_STRING_2(USTRING_RTFSDEM_08, test_dir,PRFLG_NL); /* "Creating Subdirectory:" */ +#endif + /* Delete the test dir if it exists */ + if (pc_isdir(test_dir)) + { + if (!pc_deltree(test_dir)) + { regress_error(RGE_DELTREE); goto return_false;} + } + else + { if (!check_errno(PENOENT)) goto return_false;} + /* Create the test dir if it exists */ + if (!pc_mkdir(test_dir)) + { regress_error(RGE_MKDIR); goto return_false;} + if (!check_errno(0)) goto return_false; + if (!pc_set_cwd(test_dir)) + {regress_error(RGE_SCWD); goto return_false;} + if (!check_errno(0)) goto return_false; + + /* Make the top level subdirs and subdirs down to layer SUBDIRDEPTH */ + /* Make D:\RTFS_Test_Directory string in native char set */ +/* rtfs_cs_strcpy(home, test_drive); */ + p = home; +/* p = CS_OP_GOTO_EOS(p); */ + CS_OP_ASSIGN_ASCII(p, '\\'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + rtfs_cs_strcat(home, test_dir); + + for (i = 0; i < NSUBDIRS; i++) + { + if (!pc_set_cwd(home)) + { regress_error(RGE_SCWD); goto return_false;} + if (!check_errno(0)) goto return_false; + /* Make the top level subdirs */ + build_subdir_name(buffer, i); +#if (VERBOSE) + RTFS_PRINT_STRING_2(USTRING_RTFSDEM_09, buffer,PRFLG_NL); /* "Creating Subdirectory " */ +#endif + + if (!pc_mkdir(buffer)) + { regress_error(RGE_MKDIR); goto return_false;} + if (!check_errno(0)) goto return_false; + for (j = 0; j < SUBDIRDEPTH; j++) + { + if (!pc_set_cwd(home)) + { regress_error(RGE_SCWD); goto return_false;} + if (!check_errno(0)) goto return_false; + + /* Now make SubdirA\\Subdir in native char set */ + p = buffer; + p = CS_OP_GOTO_EOS(p); + CS_OP_ASSIGN_ASCII(p,'\\'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + rtfs_cs_strcat(buffer,test_subdir_name); +#if (VERBOSE) + RTFS_PRINT_STRING_2(USTRING_RTFSDEM_10, buffer,PRFLG_NL); /* "Creating Subdirectory " */ +#endif + + /* Save D:\RTFS_Test_Directory\SubdirA\Subdir in native char set */ + /* For later comparison with get_pwd results */ + rtfs_cs_strcpy(buffer4, home); + p = buffer4; + p = CS_OP_GOTO_EOS(p); + CS_OP_ASSIGN_ASCII(p,'\\'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + rtfs_cs_strcat(buffer4, buffer); + /* Create SubdirA\\Subdir under the test directory */ + if (!pc_mkdir(buffer)) + { regress_error(RGE_MKDIR); goto return_false;} + if (!check_errno(0)) goto return_false; + /* Create a dir. We know this will fail. Force error recovery */ + if (pc_mkdir(buffer)) + { regress_error(RGE_MKDIRERR); goto return_false;} + if (!check_errno(PEEXIST)) goto return_false; + /* Go into the new directory */ + if (!pc_set_cwd(buffer)) + { regress_error(RGE_SCWD); goto return_false;} + if (!check_errno(0)) goto return_false; + /* Get the dir string */ + /* Should be:D:\RTFS_Test_Directory\SubdirA\\Subdir */ + if (!pc_pwd(test_drive, buffer3)) + { regress_error(RGE_PWD); goto return_false;} + if (!check_errno(0)) goto return_false; + /* Compare with saved vesrion */ + if (rtfs_cs_strcmp(buffer4, buffer3) != 0) + { regress_error(RGE_PWD); goto return_false;} + } + } + +#if (VFAT) + /* Do the long file test */ + RTFS_PRINT_STRING_1(USTRING_RTFSDEM_15, PRFLG_NL); /* "Performing long file name test" */ + if (!do_long_file_test(do_clean)) + { + regress_error(RGE_LONGFILETEST); + goto return_false; + } +#endif + /* Do the file test */ + if (!do_file_test(loop_count, do_clean)) + { + regress_error(RGE_FILETEST); + goto return_false; + } + /* DELETE the subdirs */ + if (!pc_set_cwd(home)) + { regress_error(RGE_SCWD); goto return_false;} + if (!check_errno(0)) goto return_false; + if (do_clean && (loop_count & 1)) + { + /* Manually remove subdirs on odd loops */ + for (i = 0; i < NSUBDIRS; i++) + { + /* Delete sub directories SubdirectoryA\SUBDIR\SUBDIR ... */ + for (j = SUBDIRDEPTH; j > 0; j--) + { + build_subdir_name(buffer, i); + if (!do_rm(buffer, j)) + goto return_false; + } + /* Delete sub directories SUB_? */ + build_subdir_name(buffer, i); + if (!pc_rmdir(buffer)) + { regress_error(RGE_RMDIR); goto return_false;} + if (!check_errno(0)) goto return_false; + } + } + /* Delete the test dir */ + /* Now make .. in native char set */ + p = buffer; + CS_OP_ASSIGN_ASCII(p,'.'); + CS_OP_INC_PTR(p); + CS_OP_ASSIGN_ASCII(p,'.'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + if (!pc_set_cwd(buffer)) + { regress_error(RGE_SCWD); goto return_false;} + if (!check_errno(0)) goto return_false; + if (do_clean && (loop_count & 1)) + { + if (!pc_rmdir(test_dir)) + { regress_error(RGE_RMDIR); goto return_false;} + if (!check_errno(0)) goto return_false; + } + else if (do_clean) + { + if (!pc_deltree(test_dir)) + { regress_error(RGE_DELTREE); goto return_false;} + if (!check_errno(0)) goto return_false; + } + pc_free_scratch_blk(scratch_buffer); + pc_free_scratch_blk(scratch_buffer3); + pc_free_scratch_blk(scratch_buffer4); + pc_free_scratch_blk(scratch_home); + return(TRUE); +return_false: + pc_free_scratch_blk(scratch_buffer); + pc_free_scratch_blk(scratch_buffer3); + pc_free_scratch_blk(scratch_buffer4); + pc_free_scratch_blk(scratch_home); + return(FALSE); +} + +/* Delete a subdir at level */ +BOOLEAN do_rm(byte *buffer, int level) /*__fn__*/ +{ +int i; +byte *p; + for (i = 0; i < level; i++) + { + p = buffer; + p = CS_OP_GOTO_EOS(p); + CS_OP_ASSIGN_ASCII(p,'\\'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + rtfs_cs_strcat(buffer,test_subdir_name); + } +#if (VERBOSE) + RTFS_PRINT_STRING_2(USTRING_RTFSDEM_11, buffer,PRFLG_NL); /* "Removing Directory " */ +#endif + if (!pc_rmdir(buffer)) + { regress_error(RGE_RMDIR); return(FALSE);} + if (!check_errno(0)) return(FALSE); + return(TRUE); +} + +#if (VFAT) +byte *plong_name; +void create_long_name(int len, byte c); +int write_long_name(int len, byte c); +int reopen_long_name(int len, byte c); +int remove_long_name(int len, byte c); + +BOOLEAN do_long_file_test(BOOLEAN do_clean) +{ + /* Use this buffer since under unicode we need more than 512 bytes to test */ + plong_name = (byte *) &test_rtfs_buf[0]; + if (write_long_name(32, (byte) 'X')) goto big_error; + if (write_long_name(64, (byte) 'X')) goto big_error; + if (write_long_name(63, (byte) 'X')) goto big_error; + if (write_long_name(127,(byte) 'X')) goto big_error; + if (write_long_name(34, (byte) 'X')) goto big_error; + if (write_long_name(66, (byte) 'X')) goto big_error; + if (write_long_name(68, (byte) 'X')) goto big_error; + if (write_long_name(124,(byte) 'X')) goto big_error; + if (write_long_name(221,(byte) 'X')) goto big_error; + if (write_long_name(255,(byte) 'X')) goto big_error; + /* This one should fail */ + if (!write_long_name(256,(byte) 'X')) goto big_error; + if (write_long_name(66, (byte) 'Y')) goto big_error; + if (write_long_name(68, (byte) 'Y')) goto big_error; + if (write_long_name(124,(byte) 'Y')) goto big_error; + if (write_long_name(221,(byte) 'Y')) goto big_error; + if (write_long_name(255,(byte) 'Y')) goto big_error; + if (write_long_name(32, (byte) 'Y')) goto big_error; + if (write_long_name(64, (byte) 'Y')) goto big_error; + if (write_long_name(63, (byte) 'Y')) goto big_error; + if (write_long_name(127,(byte) 'Y')) goto big_error; + if (write_long_name(34, (byte) 'Y')) goto big_error; + if (reopen_long_name(32, (byte) 'X')) goto big_error; + if (reopen_long_name(64, (byte) 'X')) goto big_error; + if (reopen_long_name(63, (byte) 'X')) goto big_error; + if (reopen_long_name(127,(byte) 'X')) goto big_error; + if (reopen_long_name(34, (byte) 'X')) goto big_error; + if (reopen_long_name(66, (byte) 'X')) goto big_error; + if (reopen_long_name(68, (byte) 'X')) goto big_error; + if (reopen_long_name(124,(byte) 'X')) goto big_error; + if (reopen_long_name(221,(byte) 'X')) goto big_error; + if (reopen_long_name(255,(byte) 'X')) goto big_error; + if (reopen_long_name(66, (byte) 'Y')) goto big_error; + if (reopen_long_name(68, (byte) 'Y')) goto big_error; + if (reopen_long_name(124,(byte) 'Y')) goto big_error; + if (reopen_long_name(221,(byte) 'Y')) goto big_error; + if (reopen_long_name(255,(byte) 'Y')) goto big_error; + if (reopen_long_name(32, (byte) 'Y')) goto big_error; + if (reopen_long_name(64, (byte) 'Y')) goto big_error; + if (reopen_long_name(63, (byte) 'Y')) goto big_error; + if (reopen_long_name(127,(byte) 'Y')) goto big_error; + if (reopen_long_name(34, (byte) 'Y')) goto big_error; +/* Return here if you want to see the results */ +/* pc_free_scratch_blk(scratch); */ +/* return(1); */ + if (do_clean) + { + if (remove_long_name(32, (byte) 'X')) goto big_error; + if (remove_long_name(64, (byte) 'X')) goto big_error; + if (remove_long_name(63, (byte) 'X')) goto big_error; + if (remove_long_name(127,(byte) 'X')) goto big_error; + if (remove_long_name(34, (byte) 'X')) goto big_error; + if (remove_long_name(66, (byte) 'X')) goto big_error; + if (remove_long_name(68, (byte) 'X')) goto big_error; + if (remove_long_name(124,(byte) 'X')) goto big_error; + if (remove_long_name(221,(byte) 'X')) goto big_error; + if (remove_long_name(255,(byte) 'X')) goto big_error; + if (remove_long_name(66, (byte) 'Y')) goto big_error; + if (remove_long_name(68, (byte) 'Y')) goto big_error; + if (remove_long_name(124,(byte) 'Y')) goto big_error; + if (remove_long_name(221,(byte) 'Y')) goto big_error; + if (remove_long_name(255,(byte) 'Y')) goto big_error; + if (remove_long_name(32, (byte) 'Y')) goto big_error; + if (remove_long_name(64, (byte) 'Y')) goto big_error; + if (remove_long_name(63, (byte) 'Y')) goto big_error; + if (remove_long_name(127,(byte) 'Y')) goto big_error; + if (remove_long_name(34, (byte) 'Y')) goto big_error; + } + return(TRUE); +big_error: + return(FALSE); +} + +void create_long_name(int len, byte c) +{ + int i; + byte *p; + + p = plong_name; + for (i = 0; i < len; i++) + { + CS_OP_ASSIGN_ASCII(p,c); + CS_OP_INC_PTR(p); + }; + CS_OP_TERM_STRING(p); +} + +int write_long_name(int len, byte c) +{ + int fd; + create_long_name(len, c); + + if ((fd = po_open(plong_name,(word)(PO_BINARY|PO_WRONLY|PO_CREAT|PO_TRUNC),(word)(PS_IWRITE | PS_IREAD))) >= 0) + { + po_close(fd); + return(0); + } + else + { + return(-1); + } + +} +int reopen_long_name(int len, byte c) +{ + int fd; + create_long_name(len, c); + if ((fd = po_open(plong_name,(word)(PO_BINARY|PO_WRONLY),(word)(PS_IWRITE | PS_IREAD))) >= 0) + { + po_close(fd); + return(0); + } + else + { + return(-1); + } +} +int remove_long_name(int len, byte c) +{ + create_long_name(len, c); + if (!pc_unlink(plong_name)) + { + return(-1); + } + else + return(0); +} + + +#endif + + +BOOLEAN do_buffered_file_test(BOOLEAN do_clean) +{ +PCFD fd1, fd2; +word i,j, *wbuff; + wbuff = (word *) &test_rtfs_buf[0]; + + /* Write 1024 bytes, close to flush, re-open and read */ + fd1 = po_open(test_file_name, PO_RDWR|PO_CREAT|PO_TRUNC|PO_BUFFERED, PS_IWRITE|PS_IREAD); + if (fd1 < 0) + { regress_error(RGE_OPEN); return(FALSE);} + for (i = 0; i < 512; i++) + if (po_write(fd1, (byte *)&i, 2) != 2) + { regress_error(RGE_WRITE); return(FALSE);} + po_close(fd1); + fd1 = po_open(test_file_name, PO_RDWR|PO_BUFFERED, 0); + if (fd1 < 0) + { regress_error(RGE_OPEN); return(FALSE);} + for (i = 0; i < 512; i++) + if ( (po_read(fd1, (byte *)&j, 2) != 2) || j != i) + { po_close(fd1); regress_error(RGE_READ); return(FALSE);} + po_close(fd1); + /* Read two buffered viewports of the same file. + Note: Buffer thrashing occurs here */ + fd1 = po_open(test_file_name, PO_RDWR|PO_BUFFERED, PS_IWRITE|PS_IREAD); + if (fd1 < 0) + { regress_error(RGE_OPEN); return(FALSE);} + fd2 = po_open(test_file_name, PO_RDWR|PO_BUFFERED, PS_IWRITE|PS_IREAD); + if (fd2 < 0) + { regress_error(RGE_OPEN); po_close(fd1); return(FALSE);} + if (po_lseek(fd2, 512, PSEEK_SET) != 512) + { regress_error(RGE_SEEK); po_close(fd1); po_close(fd2); return(FALSE);} + for (i = 0; i < 256; i++) + { + if ( (po_read(fd1, (byte *)&j, 2) != 2) || j != i) + { regress_error(RGE_READ); po_close(fd1); po_close(fd2);return(FALSE);} + if ( (po_read(fd2, (byte *)&j, 2) != 2) || j != i+256) + { regress_error(RGE_READ); po_close(fd1); po_close(fd2);return(FALSE);} + } + /* Write buffered, read unbuffered */ + if ( (po_lseek(fd1, 0, PSEEK_SET) != 0) || (po_lseek(fd2, 0, PSEEK_SET) != 0)) + { regress_error(RGE_SEEK); po_close(fd1); po_close(fd2);return(FALSE);} + for (j = 99, i = 0; i < 512; i++) + if (po_write(fd1, (byte *)&j, 2) != 2) + { regress_error(RGE_WRITE); po_close(fd1); po_close(fd2);return(FALSE);} + if (po_read(fd2, (byte *)wbuff, 1024) != 1024) + { regress_error(RGE_READ); po_close(fd1); po_close(fd2);return(FALSE);} + for (j = 99, i = 0; i < 512; i++) + if (wbuff[i] != j) + { regress_error(RGE_READ); po_close(fd1); po_close(fd2);return(FALSE);} + /* Seek test */ + if ( (po_lseek(fd1, 520, PSEEK_SET) != 520) ) + { regress_error(RGE_SEEK); po_close(fd1); po_close(fd2);return(FALSE);} + j = 520; po_write(fd1, (byte *)&j, 2); + if ( (po_lseek(fd1, 20, PSEEK_SET) != 20) ) + { regress_error(RGE_SEEK); po_close(fd1); po_close(fd2);return(FALSE);} + j = 20; po_write(fd1, (byte *)&j, 2); + if ( (po_lseek(fd1, 520, PSEEK_SET) != 520) ) + { regress_error(RGE_SEEK); po_close(fd1); po_close(fd2);return(FALSE);} + j = 0; po_read(fd1, (byte *)&j, 2); + if (j != 520) + { regress_error(RGE_READ); po_close(fd1); po_close(fd2);return(FALSE);} + if ( (po_lseek(fd1, 20, PSEEK_SET) != 20) ) + { regress_error(RGE_SEEK); po_close(fd1); po_close(fd2);return(FALSE);} + j = 0; po_read(fd1,(byte *) &j, 2); + if (j != 20) + { regress_error(RGE_READ); po_close(fd1); po_close(fd2);return(FALSE);} + + /* Write buffered and write unbuffered */ + if ( (po_lseek(fd1, 0, PSEEK_SET) != 0) || (po_lseek(fd2, 0, PSEEK_SET) != 0)) + { regress_error(RGE_SEEK); po_close(fd1); po_close(fd2);return(FALSE);} + for (i = 0; i < 512; i++) + wbuff[i] = (word) (512-i); + for (i = 0; i < 512; i++) + if (po_write(fd1, (byte *)&i, 2) != 2) + { regress_error(RGE_WRITE); po_close(fd1); po_close(fd2);return(FALSE);} + if (po_write(fd2, (byte *)wbuff, 1024) != 1024) + { regress_error(RGE_WRITE); po_close(fd1); po_close(fd2);return(FALSE);} + po_close(fd1); + if ( po_lseek(fd2, 0, PSEEK_SET) != 0 ) + { regress_error(RGE_SEEK); po_close(fd2);return(FALSE);} + for (i = 0; i < 512; i++) + if ( (po_read(fd2, (byte *)&j, 2) != 2) || j != 512-i) + { regress_error(RGE_READ); po_close(fd2); return(FALSE);} + po_close(fd2); + if (do_clean) + pc_unlink(test_file_name); + return(TRUE); +} + + +#define FOUR_GIG_BLOCKS 0x80000 +#define ONE_MEG (dword) 0x100000 +#define ONE_MEG_SHY (0x100000-512) +#define FILL_TEST_HIT_WRAP 0 + +BOOLEAN do_large_file_test(void) +{ +int residual,fd; +dword i, max_filesize_megs,ltemp,ltemp2,target_value,/*nfree,*/ blocks_total,blocks_free; +ERTFS_STAT stat_buff; + + max_filesize_megs = RTFS_MAX_FILE_SIZE/ONE_MEG; + ltemp = max_filesize_megs * ONE_MEG; + ltemp2 = (RTFS_MAX_FILE_SIZE-ltemp); + residual = (int)ltemp2; + /* Won't work on a 16 bit machine */ + if ((dword) residual != ltemp2) + return(FALSE); + + if (residual == 0) + { + max_filesize_megs -= 1; + residual = ONE_MEG; + } + + /*nfree =*/ pc_free(test_drive, &blocks_total, &blocks_free); + if (blocks_free < 0x80000) /* Not enough space to do long test */ + return(TRUE); +#if (VERBOSE) + RTFS_PRINT_STRING_1(USTRING_RTFSDEM_17,PRFLG_NL); /* "Performing Large 4GIG File io test" */ +#endif + /* Test read, write, ulseek, chsize, extend file */ + fd = po_open(test_file_name, PO_RDWR|PO_CREAT|PO_EXCL, PS_IWRITE|PS_IREAD); + if (fd < 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (!check_errno(0)) return(FALSE); + test_rtfs_buf[0] = 0; + /* Write the file - 1 meg at a time */ + for (i = 0 ; i < max_filesize_megs; i++) + { + if (po_write(fd, (byte *) test_rtfs_buf, 512) != 512) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_write(fd, (byte *) 0, ONE_MEG_SHY) != ONE_MEG_SHY) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + test_rtfs_buf[0] += ONE_MEG; + } + /* The last meg will be truncated */ +#if(FILL_TEST_HIT_WRAP) +#if (RTFS_TRUNCATE_WRITE_TO_MAX) + if (po_write(fd, (byte *) 0, residual+1) != residual) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_write(fd, (byte *) 0, 1) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} +#else + if (po_write(fd, (byte *) 0, residual+1) != -1) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_write(fd, (byte *) 0, residual) != residual) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} +#endif +#else + /* The last meg will be truncated */ + if (po_write(fd, (byte *) test_rtfs_buf, 512) != 512) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} +#if (RTFS_TRUNCATE_WRITE_TO_MAX) + if (po_write(fd, (byte *) 0, ONE_MEG) != (residual-512)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} +#else + if (po_write(fd, (byte *) 0, ONE_MEG) != -1) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_write(fd, (byte *) 0, (residual-512)) != (residual-512)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} +#endif +#endif + /* Read it back - 1 gig at a time */ + if (!po_ulseek(fd, 0, <emp, PSEEK_SET)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + /* Read the file - 1 gig at a time */ + target_value = 0; + for (i = 0 ; i < max_filesize_megs; i++) + { + if ( (po_read(fd, (byte *) test_rtfs_buf, 512) != 512) || + (test_rtfs_buf[0] != target_value) ) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + target_value += ONE_MEG; + if (po_read(fd, (byte *) 0, ONE_MEG_SHY) != ONE_MEG_SHY) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + } + /* The last meg will be truncated */ +#if (FILL_TEST_HIT_WRAP) + if (po_read(fd, (byte *) 0, ONE_MEG) != residual) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_read(fd, (byte *) 0, ONE_MEG) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} +#else + if ( (po_read(fd, (byte *) test_rtfs_buf, 512) != 512) || + (test_rtfs_buf[0] != target_value)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_read(fd, (byte *) 0, ONE_MEG) != residual-512) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} +#endif + +#if (FILL_TEST_HIT_WRAP) + po_close(fd); + goto unlink_it; +#endif + + /* Test po_ulseek */ + /* Test seek end */ + if (!po_ulseek(fd, 0, <emp, PSEEK_END) || ltemp != RTFS_MAX_FILE_SIZE) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + /* Test seek curr neg */ + target_value = (dword) (max_filesize_megs * ONE_MEG); + if (!po_ulseek(fd, residual, <emp, PSEEK_CUR_NEG) || ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (!po_ulseek(fd, 0, <emp, PSEEK_CUR_NEG) || + ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if ( (po_read(fd, (byte *) test_rtfs_buf, 512) != 512) || + (test_rtfs_buf[0] != target_value)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (!po_ulseek(fd, 512, <emp, PSEEK_CUR_NEG) || + ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + target_value -= (10 * ONE_MEG); + if (!po_ulseek(fd, 10 * ONE_MEG, <emp, PSEEK_CUR_NEG) || ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if ( (po_read(fd, (byte *) test_rtfs_buf, 512) != 512) || + (test_rtfs_buf[0] != target_value)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (!po_ulseek(fd, 512, <emp, PSEEK_CUR_NEG) || + ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + /* Test seek curr positive */ + target_value += (4 * ONE_MEG); + if (!po_ulseek(fd, 4 * ONE_MEG, <emp, PSEEK_CUR) || ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if ( (po_read(fd, (byte *) test_rtfs_buf, 512) != 512) || + (test_rtfs_buf[0] != target_value)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (!po_ulseek(fd, 512, <emp, PSEEK_CUR_NEG) || + ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (!po_ulseek(fd, 0, <emp, PSEEK_CUR) || + ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + + /* Test seek set */ + target_value = (4 * ONE_MEG); + if (!po_ulseek(fd, target_value, <emp, PSEEK_SET) || ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if ( (po_read(fd, (byte *) test_rtfs_buf, 512) != 512) || + (test_rtfs_buf[0] != target_value)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + target_value = ((max_filesize_megs - 1000) * ONE_MEG); + if (!po_ulseek(fd, target_value, <emp, PSEEK_SET) || ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if ( (po_read(fd, (byte *) test_rtfs_buf, 512) != 512) || + (test_rtfs_buf[0] != target_value)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + /* Test fstat set */ + if (pc_fstat(fd, &stat_buff) != 0 || stat_buff.st_size != RTFS_MAX_FILE_SIZE) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_close(fd) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + /* Test stat set */ + if (pc_stat(test_file_name, &stat_buff) != 0 || stat_buff.st_size != RTFS_MAX_FILE_SIZE) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + /* Test reopen */ + fd = po_open(test_file_name, PO_RDWR, PS_IWRITE|PS_IREAD); + if (fd < 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (!po_ulseek(fd, 0, <emp, PSEEK_END) || ltemp != RTFS_MAX_FILE_SIZE) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + target_value = ((max_filesize_megs - 1000) * ONE_MEG); + if (!po_ulseek(fd, target_value, <emp, PSEEK_SET) || ltemp != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if ( (po_read(fd, (byte *) test_rtfs_buf, 512) != 512) || + (test_rtfs_buf[0] != target_value)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + /* Test fstat */ + if (pc_fstat(fd, &stat_buff) != 0 || stat_buff.st_size != RTFS_MAX_FILE_SIZE) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + + /* Test chsize (truncate) to zero */ + target_value = 0; + if (po_chsize(fd, target_value) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_fstat(fd, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_close(fd) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_stat(test_file_name, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + + /* Test chsize (expand) to RTFS_MAX_FILE_SIZE */ + fd = po_open(test_file_name, PO_RDWR, PS_IWRITE|PS_IREAD); + if (fd < 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + target_value = RTFS_MAX_FILE_SIZE; + if (po_chsize(fd, target_value) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_fstat(fd, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_close(fd) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_stat(test_file_name, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + + + /* Test chsize (expand) to RTFS_MAX_FILE_SIZE */ + fd = po_open(test_file_name, PO_RDWR, PS_IWRITE|PS_IREAD); + if (fd < 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + target_value = RTFS_MAX_FILE_SIZE; + if (po_chsize(fd, target_value) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_fstat(fd, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_close(fd) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_stat(test_file_name, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + + /* Test chsize (truncate) to 4000 meg */ + fd = po_open(test_file_name, PO_RDWR, PS_IWRITE|PS_IREAD); + if (fd < 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + target_value = ((max_filesize_megs - 1000) * ONE_MEG); + if (po_chsize(fd, target_value) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_fstat(fd, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_close(fd) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_stat(test_file_name, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + + /* Test chsize (truncate) to 1000 meg */ + fd = po_open(test_file_name, PO_RDWR, PS_IWRITE|PS_IREAD); + if (fd < 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + target_value = (1000 * ONE_MEG); + if (po_chsize(fd, target_value) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_fstat(fd, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_close(fd) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_stat(test_file_name, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + + /* Test chsize (expand) to ffffffff0 */ + fd = po_open(test_file_name, PO_RDWR, PS_IWRITE|PS_IREAD); + if (fd < 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + target_value = RTFS_MAX_FILE_SIZE-16; + if (po_chsize(fd, target_value) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_fstat(fd, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (po_close(fd) != 0) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + if (pc_stat(test_file_name, &stat_buff) != 0 || stat_buff.st_size != target_value) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + +#if (FILL_TEST_HIT_WRAP) +unlink_it: +#endif + if (!pc_unlink(test_file_name)) + { regress_error(RGE_LARGEFILETEST); return(FALSE);} + + return(TRUE); +} + + +PCFD fdarray[250]; +/* Test file manipulation routines */ +BOOLEAN do_file_test(int loop_count, BOOLEAN do_clean) +{ +int i; +int j; +dword index; +dword di; +int ntestfiles = prtfs_cfg->cfg_NUSERFILES; + + + if (ntestfiles >= 250) + ntestfiles = 250; + +#if USEPRINTF +#if (!VERBOSE) + RTFS_PRINT_STRING_1(USTRING_RTFSDEM_12,PRFLG_NL); /* "-" */ +#endif +#endif + if (!loop_count) + if (!do_large_file_test()) + return(FALSE); +#if (VERBOSE) + RTFS_PRINT_STRING_1(USTRING_RTFSDEM_13,PRFLG_NL); /* "Performing File io test" */ +#endif + fdarray[0] = po_open(test_file_name, PO_RDWR|PO_CREAT|PO_EXCL, PS_IWRITE|PS_IREAD); + if (fdarray[0] < 0) + { regress_error(RGE_OPEN); return(FALSE);} + if (!check_errno(0)) return(FALSE); + for (i = 1; i < ntestfiles;i++) + { + /* This should fail */ + fdarray[i] = po_open(test_file_name, PO_RDWR|PO_CREAT|PO_EXCL, PS_IWRITE|PS_IREAD); + if (fdarray[i] >= 0) + { regress_error(RGE_OPEN); return(FALSE);} + if (!check_errno(PEEXIST)) return(FALSE); + + /* This should work */ + fdarray[i] = po_open(test_file_name, PO_RDWR, PS_IWRITE|PS_IREAD); + if (fdarray[i] < 0) + { regress_error(RGE_OPEN); return(FALSE);} + if (!check_errno(0)) return(FALSE); + } + /* Write into the file using all file descriptors */ + index = 0; + for (i = 0; i < ntestfiles;i++) + { + if (po_lseek(fdarray[i], 0L, PSEEK_END) == -1L) + { regress_error(RGE_SEEK); return(FALSE);} + if (!check_errno(0)) return(FALSE); + for (j = 0; j < NLONGS; j++) + test_rtfs_buf[j] = index++; + if (po_write(fdarray[i], (byte *) test_rtfs_buf, (NLONGS*4)) != (NLONGS*4)) + { regress_error(RGE_WRITE); return(FALSE);} + if (!check_errno(0)) return(FALSE); + } + + /* Read file using all fds */ + index = 0; + for (i = 0; i < ntestfiles;i++) + { + if (po_lseek(fdarray[i], (dword) (index*4), PSEEK_SET) != (long) (index*4)) + { regress_error(RGE_SEEK); return(FALSE);} + if (!check_errno(0)) return(FALSE); + if (po_read(fdarray[i], (byte *) test_rtfs_buf, (NLONGS*4)) != (NLONGS*4)) + { regress_error(RGE_READ); return(FALSE);} + if (!check_errno(0)) return(FALSE); + for (j = 0; j < NLONGS; j++) + { + if (test_rtfs_buf[j] != index++) + { regress_error(RGE_READ); return(FALSE);} + } + } + + /* This should fail if more than one file is open */ + if (ntestfiles > 1) + { + if (po_truncate(fdarray[0], 256)) + { regress_error(RGE_TRUNC); return(FALSE);} + + if (!check_errno(PEACCES)) return(FALSE); + } + if (!po_flush(fdarray[0])) + { regress_error(RGE_FLUSH); return(FALSE);} + if (!check_errno(0)) return(FALSE); + + /* Close all secondary files */ + for (i = 1; i < ntestfiles;i++) + { + if (po_close(fdarray[i]) != 0) + { regress_error(RGE_CLOSE); return(FALSE);} + if (!check_errno(0)) return(FALSE); + } + /* This should work */ + if (!po_truncate(fdarray[0], 256)) + { regress_error(RGE_TRUNC); return(FALSE);} + if (!check_errno(0)) return(FALSE); + + /* This should work */ + for (di = 0; di < 64 * 1024; di += 1024) + { + if (po_chsize(fdarray[0], di) != 0) + { regress_error(RGE_CHSIZE); return(FALSE);} + if (!check_errno(0)) return(FALSE); + } + + /* This should work */ + for (di = 63 * 1024; di; di -= 1024) + { + if (po_chsize(fdarray[0], di) != 0) + { regress_error(RGE_CHSIZE); return(FALSE);} + if (!check_errno(0)) return(FALSE); + } + + /* This should fail */ + if (pc_unlink(test_file_name)) + { regress_error(RGE_UNLINK); return(FALSE);} + if (!check_errno(PEACCES)) return(FALSE); + + if (po_close(fdarray[0]) != 0) + { regress_error(RGE_CLOSE); return(FALSE);} + if (!check_errno(0)) return(FALSE); + + if (!pc_mv(test_file_name, test_newfile_name)) + { regress_error(RGE_MV); return(FALSE);} + if (!check_errno(0)) return(FALSE); + + /* This should work */ + /* Manually delete on odd loops, even loops use deltree */ + if (do_clean && (loop_count & 1)) + { + if (!pc_unlink(test_newfile_name)) + { regress_error(RGE_UNLINK); return(FALSE);} + if (!check_errno(0)) return(FALSE); + } + + return(TRUE); +} + +BOOLEAN check_errno(int expected_error) +{ + if (get_errno() != expected_error) + { + regress_error(RGE_ERRNO); + RTFS_PRINT_LONG_1((dword) get_errno(), PRFLG_NL); + return(FALSE); + } + else + { + rtfs_set_errno(PEILLEGALERRNO); /* Set it to illegal, it should be cleared */ + return(TRUE); + } +} + +void regress_error(int error) /*__fn__*/ +{ + RTFS_PRINT_STRING_1(USTRING_RTFSDEM_14, 0); /* "regress_error was called with error" */ + RTFS_PRINT_LONG_1((dword) error, PRFLG_NL); + return; +} diff --git a/build/libraries/fatfs/ARM7/apisetwd.c b/build/libraries/fatfs/ARM7/apisetwd.c new file mode 100644 index 0000000..9b2b3cd --- /dev/null +++ b/build/libraries/fatfs/ARM7/apisetwd.c @@ -0,0 +1,146 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APISETCWD.C - Contains user api level source code. + + The following routines are included: + + pc_set_cwd - Set the current working directory. + +*/ + +#include + +/*************************************************************************** + PC_SET_CWD - Set the current working directory for a drive. + + Description + Find path. If it is a subdirectory make it the current working + directory for the drive. + + Returns + Returns TRUE if the current working directory was changed. + + errno is set to one of the following + 0 - No error + PEINVALIDPATH - Path specified badly formed. + PENOENT - Path not found + PEINVALIDDIR - Not a directory + An ERTFS system error +****************************************************************************/ + +BOOLEAN pc_set_cwd(byte *name) /* __apifn__ */ +{ + DROBJ *pobj; + int driveno; + DDRIVE *pdrive; + DROBJ *parent_obj; + byte fileext[4]; + byte *path, *pfilename,*pfilename_plus_1, *pfileext; + BOOLEAN ret_val; + int p_errno; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_set_cwd: clear error status */ + + + ret_val = FALSE; + p_errno = 0; + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(name); + if (driveno < 0) + { /* errno was set by check_drive */ + return(FALSE); + } + + pdrive = pc_drno2dr(driveno); + + /* Use the buffers in the DRIVE structure. Access is locked via semaphore */ + pfilename_plus_1 = pfilename = (byte *)&(pdrive->filename_buffer[0]); + pfileext = &fileext[0]; + CS_OP_INC_PTR(pfilename_plus_1); + path = (byte *)&(pdrive->pathname_buffer[0]); + + /* Get out the filename and d:parent */ + if (!pc_parsepath(path, pfilename,pfileext,name)) + { + p_errno = PEINVALIDPATH; + goto errex; + } + /* Find the parent and make sure it is a directory */ + parent_obj = pc_fndnode(path); + if (!parent_obj) + goto errex; /* pc_fndnode set errno */ + + if (!pc_isadir(parent_obj)) + { + p_errno = PEACCES; /* Path is not a directory */ + goto errex; + } + /* Get the directory */ + if (CS_OP_CMP_ASCII(pfilename,'\0') || CS_OP_CMP_ASCII(pfilename,' ')) + { + pobj = parent_obj; + } + else if (CS_OP_CMP_ASCII(pfilename,'.') && CS_OP_CMP_ASCII(pfilename_plus_1,'.')) + { + if (pc_isroot(parent_obj)) + pobj = parent_obj; + else + { + pobj = pc_get_inode(0, parent_obj, 0, 0, GET_INODE_DOTDOT); + /* If the request is cd .. then we just found the .. directory entry + we have to call get_mom to access the parent. */ + pc_freeobj(parent_obj); + if (!pobj) /* pc_get_inode() has set errno to PENOENT or to an internal or IO error status */ + goto errex; + parent_obj = pobj; + /* Find parent_objs parent. By looking back from .. */ + pobj = pc_get_mom(parent_obj); + pc_freeobj(parent_obj); + if (!pobj) + { /* if pc_get_mom() set errno, use it otherwise set PENOENT */ + if (!get_errno()) + p_errno = PENOENT; /* Not found */ + goto errex; + } + } + } + else if (CS_OP_CMP_ASCII(pfilename,'.')) + { + pobj = parent_obj; + } + else + { + pobj = pc_get_inode(0, parent_obj, pfilename, pfileext, GET_INODE_MATCH); + pc_freeobj(parent_obj); + } + if (!pobj) + { + /* pc_get_inode set errno */ + goto errex; + } + else if (!pc_isadir(pobj)) + { + pc_freeobj(pobj); + p_errno = PEINVALIDDIR; /* Path is not a directory */ + goto errex; + } + + driveno = pobj->pdrive->driveno; + if (rtfs_get_system_user()->lcwd[driveno] != 0) + pc_freeobj((DROBJ *)(rtfs_get_system_user()->lcwd[driveno])); + rtfs_get_system_user()->lcwd[driveno] = (void *) pobj; + ret_val = TRUE; +errex: + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + if (p_errno) + rtfs_set_errno(p_errno); + return(ret_val); +} + diff --git a/build/libraries/fatfs/ARM7/apistat.c b/build/libraries/fatfs/ARM7/apistat.c new file mode 100644 index 0000000..59d0c0d --- /dev/null +++ b/build/libraries/fatfs/ARM7/apistat.c @@ -0,0 +1,194 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APISTAT.C - Contains user api level source code. + + The following routines are included: + + pc_fstat - Obtain statistics on an open file + pc_stat - Obtain statistics on a path. + +*/ + +#include + +void pc_finode_stat(FINODE *pi, ERTFS_STAT *pstat); /*__fn__*/ + +/**************************************************************************** + PS_FSTAT - Obtain statistics on an open file + + Description + Fills in the stat buffer for information about an open file. + + See pc_stat for a description of the stat buffer. + + + Returns + Returns 0 if all went well otherwise it returns -1 + + errno is set to one of the following + 0 - No error + PEBADF - Invalid file descriptor +****************************************************************************/ + +int pc_fstat(PCFD fd, ERTFS_STAT *pstat) /*__apifn__*/ +{ +PC_FILE *pfile; +CHECK_MEM(int, -1) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_fstat: clear error status */ + + /* Get the file structure and semaphore lock the drive */ + pfile = pc_fd2file(fd, 0); + if (!pfile) + { /* fd2file set errno */ + return(-1); + } + else + { + /* cal pc_finode_stat() to update the stat structure */ + pc_finode_stat(pfile->pobj->finode, pstat); + release_drive_mount(pfile->pobj->pdrive->driveno);/* Release lock, unmount if aborted */ + return(0); + } +} + +/**************************************************************************** + PC_STAT - Obtain statistics on a path. + + Description + This routine searches for the file or directory provided in the first + argument. If found it fills in the stat structure as described here. + + st_dev - The entry s drive number + st_mode; + S_IFMT type of file mask + S_IFCHR character special (unused) + S_IFDIR directory + S_IFBLK block special (unused) + S_IFREG regular (a file) + S_IWRITE Write permitted + S_IREAD Read permitted. + st_rdev - The entry s drive number + st_size - file size + st_atime - creation date in DATESTR format + st_mtime - creation date in DATESTR format + st_ctime - creation date in DATESTR format + t_blksize - optimal blocksize for I/O (cluster size) + t_blocks - blocks allocated for file + fattributes - The DOS attributes. This is non-standard but supplied + if you want to look at them + + + Returns + Returns 0 if all went well otherwise it returns -1. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PENOENT - File or directory not found + An ERTFS system error +****************************************************************************/ + +int pc_stat(byte *name, ERTFS_STAT *pstat) /*__apifn__*/ +{ + DROBJ *pobj; + int driveno; + int ret_val; + CHECK_MEM(int, -1) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_stat: clear error status */ + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(name); + if (driveno < 0) + { /* errno was set by check_drive */ + return(-1); + } + + /* pc_fndnode will set errno */ + pobj = pc_fndnode(name); + if (pobj) + { + if (pobj->isroot) + { + pstat->st_rdev = + pstat->st_dev = pobj->finode->my_drive->driveno; + pstat->st_ino = 0; + pstat->fattribute = ADIRENT; + pstat->st_mode = S_IFDIR; + pstat->st_nlink = 1; /* (always 1) */ + pstat->st_size = 0; /* file size, in bytes */ + pstat->st_atime.date = pstat->st_atime.time = 0; + pstat->st_mtime = pstat->st_atime; /* last modification */ + pstat->st_ctime = pstat->st_atime; /* last status change */ + pstat->st_blksize = (dword) pobj->finode->my_drive->bytespcluster; + pstat->st_blocks = (dword) ((pobj->finode->fsize + 511)>>9); + } + else + { + /* cal pc_finode_stat() to update the stat structure */ + pc_finode_stat(pobj->finode, pstat); + } + ret_val = 0; + } + else + ret_val = -1; + if (pobj) + pc_freeobj(pobj); + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + + return(ret_val); +} + +/**************************************************************************** + PC_FINODE_STAT - Convert finode information to stat info for stat and fstat + + Description + Given a pointer to a FINODE and a ERTFS_STAT structure + load ERTFS_STAT with filesize, date of modification et al. Interpret + the fattributes field of the finode to fill in the st_mode field of the + the stat structure. + + Returns + Nothing + + +****************************************************************************/ + +void pc_finode_stat(FINODE *pi, ERTFS_STAT *pstat) /*__fn__*/ +{ + pstat->st_dev = pi->my_drive->driveno; /* (drive number, rtfs) */ + pstat->st_ino = 0; /* inode number (0) */ + pstat->st_mode = 0; /* (see S_xxxx below) */ + + /* Store away the DOS file attributes in case someone needs them */ + pstat->fattribute = pi->fattribute; + pstat->st_mode |= S_IREAD; + if(!(pstat->fattribute & ARDONLY)) + pstat->st_mode |= S_IWRITE; + if (pstat->fattribute & ADIRENT) + pstat->st_mode |= S_IFDIR; + if (!(pstat->fattribute & (AVOLUME|ADIRENT))) + pstat->st_mode |= S_IFREG; + + pstat->st_nlink = 1; /* (always 1) */ + pstat->st_rdev = pstat->st_dev; /* (drive number, rtfs) */ + pstat->st_size = pi->fsize; /* file size, in bytes */ + + pstat->st_atime.date = pi->fdate; /* last access */ + pstat->st_atime.time = pi->ftime; + pstat->st_mtime = pstat->st_atime; /* last modification */ + pstat->st_ctime = pstat->st_atime; /* last status change */ + /* optimal buffering size. is a cluster */ + pstat->st_blksize = (dword) pi->my_drive->bytespcluster; + /* blocks is file size / 512. with round up */ + pstat->st_blocks = (dword) ((pi->fsize + 511)>>9); +} + + + diff --git a/build/libraries/fatfs/ARM7/apiwrite.c b/build/libraries/fatfs/ARM7/apiwrite.c new file mode 100644 index 0000000..66d0d2a --- /dev/null +++ b/build/libraries/fatfs/ARM7/apiwrite.c @@ -0,0 +1,828 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIWRITE.C - Contains user api level file IO source code. + + The following routines are included: + po_write - Write Bytes to a file. + po_flush - Flush an open file + po_trunc - Truncate an open file + pc_set_attributes - Set File Attributes + pc_diskflush - Flush the FAT and all files on a disk +*/ + +#include + +/*************************************************************************** + PO_WRITE - Write to a file. + + Description + Attempt to write count bytes from buf to the current file pointer of file + at fd. The file pointer is updated. + + Returns + Returns the number of bytes written or -1 on error. + + errno is set to one of the following + 0 - No error + PEBADF - Invalid file descriptor + PEACCES - File is read only + PEIOERRORWRITE - Error performing write + PEIOERRORREAD - Error reading block for merge and write + PENOSPC - Disk full + An ERTFS system error +****************************************************************************/ + +int po_write(PCFD fd, byte *in_buff, int count) /*__apifn__*/ +{ + PC_FILE *pfile; + DDRIVE *pdrive; + dword block_in_cluster; + dword byte_offset_in_block; + dword n_blocks_left; + dword n_to_write, n_w_to_write; + CLUSTERTYPE next_cluster; + CLUSTERTYPE n_clusters; + dword ltemp; + int n_bytes; + int n_written; + int n_left; + dword alloced_size; + dword block_to_write; + int end_of_chain; + BOOLEAN extending_file; + int ret_val; + int p_errno; + + CHECK_MEM(int, -1) /* Make sure memory is initted */ + + /* Return 0 (none written) on bad args */ + if (!count) + return(0); + + p_errno = 0; + rtfs_set_errno(0); /* po_write: clear error status */ + /* Get the FILE. must be open for write */ + /* Get the file structure and semaphore lock the drive */ + pfile = pc_fd2file(fd, PO_WRONLY|PO_RDWR); + if (!pfile) + { /* fd2file set errno */ + ret_val = -1; + goto return_unlocked; + } + pdrive = pfile->pobj->pdrive; + /* Only one process may write at a time */ + /* if the file is zero sized make sure the current cluster pointer + is invalid */ + if (!pfile->pobj->finode->fsize) + pfile->fptr_cluster = 0; + + /* Round the file size up to its cluster size by adding in clustersize-1 + and masking off the low bits */ + alloced_size = (pfile->pobj->finode->fsize + pdrive->byte_into_cl_mask) & + ~(pdrive->byte_into_cl_mask); + if (alloced_size < pfile->pobj->finode->fsize) + alloced_size = 0xffffffff; + + /* Set the cluster and block file pointers if not already set */ + if (!_synch_file_ptrs(pfile)) + { /* _synch_file_ptrs set errno */ + ret_val = -1; + goto return_locked; + } + /* Seek to the end if append mode */ + if (pfile->flag & PO_APPEND) + { + if (pfile->pobj->finode->fsize) + { + if (!_po_ulseek(pfile, 0L, <emp, PSEEK_END)) + { /* po_ulseek will set error status */ + ret_val = -1; + goto return_locked; + } + if (!_synch_file_ptrs(pfile)) + { /* _synch_file_ptrs set errno */ + ret_val = -1; + goto return_locked; + } + } + } + /* Check if this write will wrap past 4 Gigabytes + if so truncate the count to 4 Gig minus 1*/ + ltemp = pfile->fptr + count; + if (ltemp < pfile->fptr) + { +#if (RTFS_TRUNCATE_WRITE_TO_MAX) + ltemp = 0xffffffff; + count = ltemp - pfile->fptr; +#else + p_errno = PETOOLARGE; + ret_val = -1; + goto return_locked; +#endif + } + if (ltemp > RTFS_MAX_FILE_SIZE) + { +#if (RTFS_TRUNCATE_WRITE_TO_MAX) + ltemp = RTFS_MAX_FILE_SIZE; + count = ltemp - pfile->fptr; +#else + p_errno = PETOOLARGE; + ret_val = -1; + goto return_locked; +#endif + } + + /* calculate initial values */ + n_left = count; + n_written = 0; + + while (n_left) + { + end_of_chain = 0; + block_in_cluster = (dword) (pfile->fptr & pdrive->byte_into_cl_mask); + block_in_cluster >>= 9; + + if (pfile->fptr >= alloced_size) + { + /* Extending the file */ + extending_file = TRUE; + ltemp = (dword) n_left; + n_blocks_left = (dword) ((ltemp + 511) >> 9); + /* how many clusters are left- + * assume 1 for the current cluster. + * subtract out the blocks in the current + * round up by adding secpalloc-1 and then + * divide by sectors per cluster + + | n_clusters = 1 + + | (n_blocks_left- + | (pdrive->secpalloc-block_in_cluster) + | + pdrive->secpalloc-1) >> pdrive->log2_secpalloc; + ==> + */ + n_clusters = ( CLUSTERTYPE ) (1 + + ((n_blocks_left + block_in_cluster -1) >> pdrive->log2_secpalloc)); + + /* Call pc_alloc_chain to build a chain up to n_cluster clusters + long. Return the first cluster in pfile->fptr_cluster and + return the # of clusters in the chain. If pfile->fptr_cluster + is non zero link the current cluster to the new one */ + n_clusters = FATOP(pdrive)->fatop_alloc_chain(pdrive, &(pfile->fptr_cluster), n_clusters, TRUE); + if (!n_clusters) + { /* Allocchain will set errno to PENOSPC or an IO or internal error */ + break; + } + + /* Calculate the last cluster in this chain. */ + next_cluster = (CLUSTERTYPE) (pfile->fptr_cluster + n_clusters -1); + /* link the chain to the directory object if just starting */ + if (!pc_finode_cluster(pfile->pobj->pdrive,pfile->pobj->finode)) + pc_pfinode_cluster(pfile->pobj->pdrive,pfile->pobj->finode,pfile->fptr_cluster); + + /* calculate the new block pointer */ + pfile->fptr_block = pc_cl2sector(pdrive, pfile->fptr_cluster); + + /* calculate amount of space used by the file */ + ltemp = n_clusters << pdrive->log2_secpalloc; ltemp <<= 9; + alloced_size += ltemp; + if (alloced_size < ltemp) + alloced_size = 0xffffffff; + + } + else /* Not extending the file. (writing inside the file) */ + { + extending_file = FALSE; + ltemp = (dword) n_left; + n_blocks_left = (dword) ((ltemp + 511) >> 9); + /* how many clusters are left- + * assume 1 for the current cluster. + * subtract out the blocks in the current + * round up by adding secpalloc-1 and then + * divide by sectors per cluster + + | n_clusters = 1 + + | (n_blocks_left- + | (pdrive->secpalloc-block_in_cluster) + | + pdrive->secpalloc-1) >> pdrive->log2_secpalloc; + ==> + */ + n_clusters = (CLUSTERTYPE) (1 + + ((n_blocks_left + block_in_cluster -1) >> pdrive->log2_secpalloc)); + + /* how many contiguous clusters can we get ? <= n_clusters */ + end_of_chain = 0; + n_clusters = FATOP(pdrive)->fatop_get_chain(pdrive, pfile->fptr_cluster, + &next_cluster, n_clusters, &end_of_chain); + if (!n_clusters) + { + /* get_chain already set errno */ + ret_val = (int) -1; + goto return_locked; + } + } + + /* Are we inside a block */ + if ( (pfile->fptr & 0x1ffL) || (n_left < 512) ) + { + block_in_cluster = (dword) (pfile->fptr & pdrive->byte_into_cl_mask); + block_in_cluster >>= 9; + block_to_write = pfile->fptr_block + block_in_cluster; + + byte_offset_in_block = (dword) (pfile->fptr & 0x1ffL); + + /* Copy source data to the local buffer */ + n_bytes = (int) (512 - byte_offset_in_block); + if (n_bytes > n_left) + n_bytes = n_left; + /* READ - Use the block buffer pool to read for us */ + if (!pc_load_file_buffer(pfile, block_to_write)) + break; /* load_file_block set errno */ + /* Merge the data and mark it dirty */ + if (in_buff) + copybuff(&(pfile->pobj->finode->pfile_buffer->data[byte_offset_in_block]), in_buff, n_bytes); + pfile->pobj->finode->file_buffer_dirty = 1; + if (!(pfile->pobj->finode->openflags & OF_BUFFERED)) + { + /* Write the buffer. and discard it */ + if (!pc_load_file_buffer(pfile, 0)) + break; /* load_file_block set errno */ + } + if (in_buff) + in_buff += n_bytes; + + n_left = (int) (n_left - n_bytes); + pfile->fptr += n_bytes; + n_written += n_bytes; + + /* Are we on a cluster boundary ? */ + if (!(pfile->fptr & pdrive->byte_into_cl_mask)) + { + if (--n_clusters) /* If contiguous */ + { + pfile->fptr_block += pdrive->secpalloc; + pfile->fptr_cluster += (CLUSTERTYPE)1; + } + else + { + /* Check for corrupted file + We are about to advance fptr_cluster by + making next_cluster the current cluster. + If the file pointer is less than the current file + size, but we are at the end of chain we know + that there is no next_cluster and the chain is + corrupted. It shorter than the file size indicates. + Reset the byte pointer to match the current + block and cluster pointers, set errno to + PEINVALIDCLUSTER, return -1 + We check against alloced_size here because next + time through the write routine will check and if + fptr >= alloced_size then it will link a new cluster + to the file. + */ + if (!extending_file && + pfile->fptr < alloced_size && + end_of_chain) + { + pfile->fptr -= n_bytes; + p_errno = PEINVALIDCLUSTER; + ret_val = -1; + goto return_locked; + } + else + { + /* NOTE: Put the next cluster into the pointer. If we had + alloced a chain this value is the last cluster in + the chain and does not concur with the byte file pointer. + This is not a problem since the cluster pointer is known + to be off at this point any (fptr>=alloced_size) */ + pfile->fptr_cluster = next_cluster; + pfile->fptr_block = pc_cl2sector(pdrive, next_cluster); + } + } /* if (--nclusters) {} else {}; */ + } /* if (!(pfile->fptr & byte_into_cl_mask)) */ + } /* if ( (pfile->fptr & 0x1ff) || (n_left < 512) ) */ + + if (n_clusters && (n_left>511)) + { + /* If we get here we need to write contiguous blocks */ + block_in_cluster = (dword) (pfile->fptr & pdrive->byte_into_cl_mask); + block_in_cluster >>= 9; + block_to_write = pfile->fptr_block + block_in_cluster; + /* how many do we write ? */ + ltemp = (dword) n_left; + n_blocks_left = (dword) (ltemp >> 9); + n_to_write = (dword) ((n_clusters << pdrive->log2_secpalloc) - block_in_cluster); + + if (n_to_write > n_blocks_left) + { + n_to_write = n_blocks_left; + + /* If we are not writing to the end of the chain we may not + advance the cluster pointer to the beginning of the next + chain. We add in block_in_cluster so we account for the + partial cluster we have already seen */ + next_cluster = (CLUSTERTYPE) (pfile->fptr_cluster + + ((n_to_write+block_in_cluster) >> pdrive->log2_secpalloc)); + } + /* Devio takes words for blocks so split it up */ + ltemp = n_to_write; + while (ltemp) + { + if (ltemp > 0xffff) + n_w_to_write = 0xffff; + else + n_w_to_write = ltemp & 0xffff; + /* Set pdrive->drive_flags to tell the device driver we are perfroming + file data transfer */ + pdrive->drive_flags |= DRIVE_FLAGS_FILEIO; + if (in_buff && !devio_write(pdrive->driveno, block_to_write, in_buff, (word) n_w_to_write, FALSE)) + { + pdrive->drive_flags &= ~DRIVE_FLAGS_FILEIO; + /* set errno to IO error unless devio set PEDEVICE */ + if (!get_errno()) + p_errno = PEIOERRORWRITE; + ret_val = n_written; + goto return_locked; + } + pdrive->drive_flags &= ~DRIVE_FLAGS_FILEIO; + /* Purge the file block buffer if it was in our range, since we will overwrite */ + pc_sync_file_buffer(pfile, block_to_write, n_w_to_write, FALSE); + n_bytes = (int) n_w_to_write * 512; + if (in_buff) + in_buff = in_buff + n_bytes; + block_to_write += n_w_to_write; + ltemp = ltemp - n_w_to_write; + } +/* See note above */ + n_bytes = (int) n_to_write * 512; + n_left = n_left - n_bytes; + pfile->fptr += n_bytes; + n_written += n_bytes; + pfile->fptr_cluster = next_cluster; + pfile->fptr_block = pc_cl2sector(pdrive, next_cluster); + } + } /* while n_left */ + ret_val = n_written; + /* If we wrote anything change the last modified time and date */ + if (pfile->pobj && ret_val > 0) + { + DATESTR crdate; + pc_getsysdate(&crdate); + pfile->pobj->finode->fattribute |= ARCHIVE; + pfile->pobj->finode->ftime = crdate.time; + pfile->pobj->finode->fdate = crdate.date; + pfile->needs_flush = TRUE; + } + +return_locked: + if (pfile->pobj && (pfile->fptr > pfile->pobj->finode->fsize)) + { + pfile->needs_flush = TRUE; + pfile->pobj->finode->fsize = pfile->fptr; + pfile->pobj->finode->fattribute |= ARCHIVE; + } + /* If the file pointer is beyond the space allocated to the file note it. + since we may need to adjust this files cluster and block pointers + later if someone else extends the file behind our back */ + if (pfile->fptr >= alloced_size) + pfile->at_eof = TRUE; + else + pfile->at_eof = FALSE; + /* If the file is open in auto flush mode flush directory entry changes to disk */ + if (pfile->flag & PO_AFLUSH) + if (!_po_flush(pfile)) + ret_val = -1; + if (!release_drive_mount_write(pdrive->driveno))/* Release lock, unmount if aborted */ + return(-1); +return_unlocked: + if (p_errno) + rtfs_set_errno(p_errno); + return(ret_val); +} + +/************************************************************************** + PO_TRUNCATE - Truncate an open file. + + Description + Move the file pointer offset bytes from the beginning of the file + and truncate the file beyond that point by adjusting the file size + and freeing the cluster chain past the file pointer. + + Returns + Returns TRUE if successful otherwise FALSE + + errno is set to one of the following + 0 - No error + PEBADF - Invalid file descriptor + PEACCES - File is read only or opened more than once + PEINVALIDPARMS - Invalid or inconsistent arguments + An ERTFS system error +*****************************************************************************/ + + +BOOLEAN po_truncate(PCFD fd, dword offset) /*__apifn__*/ +{ + PC_FILE *pfile; + DDRIVE *pdrive; + BOOLEAN ret_val; + CLUSTERTYPE first_cluster_to_release; + CLUSTERTYPE last_cluster_in_chain; + CLUSTERTYPE clno; + int p_errno; + dword clusters_to_release; + dword old_chain_len; + dword new_chain_len; + dword range_check; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + p_errno = 0; + rtfs_set_errno(0); /* po_truncate: clear error status */ + + /* Assume failure */ + ret_val = FALSE; + + /* Get the FILE. must be open for write */ + /* Get the file structure and semaphore lock the drive */ + pfile = pc_fd2file(fd, PO_WRONLY|PO_RDWR); + /* Make sure we have write privilages */ + if (!pfile) + goto return_error; + + pdrive = pfile->pobj->pdrive; + + /* Can only truncate a file that you hold exclusively */ + if (pfile->pobj->finode->opencount > 1) + { + p_errno = PEACCES; + goto errex; + } + + /* Set the cluster and block file pointers if not already set */ + if (!_synch_file_ptrs(pfile)) + { /* _synch_file_ptrs set errno */ + goto errex; + } + + /* Make sure the file buffer is clear */ + pc_load_file_buffer(pfile, 0); + + if (offset > pfile->pobj->finode->fsize) + { + p_errno = PEINVALIDPARMS; + goto errex; + } + + /* Call the internal seek routine that we share with po_lseek. Seek to + offset from the origin of zero. */ + if (!_po_ulseek(pfile, offset, &offset, PSEEK_SET)) + goto errex; /* _po_lseek() set errno */ + else if (offset == pfile->pobj->finode->fsize) + { + ret_val = TRUE; + } + else + { + pfile->needs_flush = TRUE; + + /* calculate maximum number of clusters we will need to release */ + /* Round the file size up to its cluster size by adding in clustersize-1 + and masking off the low bits */ + old_chain_len = pc_chain_length(pdrive, pfile->pobj->finode->fsize); + new_chain_len = pc_chain_length(pdrive, offset); + + clusters_to_release = old_chain_len - new_chain_len; + + /* Are we on a cluster boundary ? */ + if (!(offset & pdrive->byte_into_cl_mask)) + { + + /* Free the current cluster and beyond since we are on a cluster boundary. */ + first_cluster_to_release = pfile->fptr_cluster; + /* Find the previous cluster so we can terminate the chain */ + clno = pc_finode_cluster(pdrive,pfile->pobj->finode); + last_cluster_in_chain = clno; + range_check = 0; + while (clno != first_cluster_to_release) + { + if ((clno < 2) || (clno > pdrive->maxfindex) || (++range_check > old_chain_len) ) + { + rtfs_set_errno(PEINVALIDCLUSTER); + goto errex; + } + last_cluster_in_chain = clno; + clno = FATOP(pdrive)->fatop_clnext(pdrive , clno); /* File */ + if (!clno) + { /* FATOP will set errno */ + goto errex; + } + } + /* Set ptr_cluster to last in chain so read&write will work right */ + pfile->fptr_cluster = last_cluster_in_chain; + if (last_cluster_in_chain) + { + if ((clno < 2) || (clno > pdrive->maxfindex)) + { + rtfs_set_errno(PEINVALIDCLUSTER); + goto errex; + } + + pfile->fptr_block = pc_cl2sector(pdrive, last_cluster_in_chain); + } + else + pfile->fptr_block = 0; + pfile->at_eof = TRUE; + } + else /* Simple case. we are not on a cluster boundary. Just free*/ + { /* The chain beyond us and terminate the list */ + last_cluster_in_chain = pfile->fptr_cluster; + first_cluster_to_release = FATOP(pdrive)->fatop_clnext(pdrive, pfile->fptr_cluster); /* File */ + if (!first_cluster_to_release) /* if 0 and not end of chain it's an error */ + { /* clnext set errno */ + goto errex; + } + /* Clear cluster number if clnext returned eof */ + if (first_cluster_to_release == 0xffffffff) + first_cluster_to_release = 0; + pfile->at_eof = TRUE; + } + + /* Now update the directory entry. */ + pfile->pobj->finode->fsize = offset; + if (!offset) /* If the file goes to zero size unlink the chain */ + { + pc_pfinode_cluster(pdrive,pfile->pobj->finode,0); + pfile->fptr_cluster = 0; + pfile->fptr_block = 0; + pfile->fptr = 0; + pfile->at_eof = FALSE; + /* We are freeing the whole chain so we do not mark last_cluster in chain */ + last_cluster_in_chain = 0; + } + ret_val = TRUE; /* If it doesn't get changed to false, it worked */ + /* Convert to native. Overwrite the existing inode.Set archive/date */ + if (!pc_update_inode(pfile->pobj, TRUE, TRUE)) + ret_val = FALSE; + /* Terminate the chain and free the lost chain part */ + /* Free the rest of the chain */ + if (ret_val && clusters_to_release && first_cluster_to_release) + { + /* Release the chain FATOP will set erno if needed - set min and max + the same so it must delete exactly this many clusters */ + if (!FATOP(pdrive)->fatop_freechain(pdrive, first_cluster_to_release, clusters_to_release, clusters_to_release)) + ret_val = FALSE; + } + /* Null terminate the chain */ + if (ret_val && last_cluster_in_chain) + { + if (!FATOP(pdrive)->fatop_pfaxxterm(pdrive, last_cluster_in_chain)) /* File */ + ret_val = FALSE; + } + if (ret_val && !FATOP(pdrive)->fatop_flushfat(pdrive->driveno)) + ret_val = FALSE; + } + +errex: + if (!release_drive_mount_write(pdrive->driveno))/* Release lock, unmount if aborted */ + return(FALSE); +return_error: /* No only errors return through here. Everything does. */ + if (p_errno) + rtfs_set_errno(p_errno); + return(ret_val); +} + +/* +* Note: when this routine is caled the files finode is LOCKED so the code +* need not be reentrant relative to the finode. +*/ +/* Internal version of po_flush() called by po_flush and po_close */ +BOOLEAN _po_flush(PC_FILE *pfile) /*__fn__*/ +{ + /*---------- ctr modified ----------*/ + int driveno; + DDRIVE *pdr; + /*----------------------------------*/ + BOOLEAN ret_val; + + /* Flush the file buffer if it is in use */ + if (!pc_flush_file_buffer(pfile)) + return(FALSE); + ret_val = TRUE; + /* Convert to native. Overwrite the existing inode.Set archive + set date. */ + if (pfile->needs_flush) + { + if (pc_update_inode(pfile->pobj, TRUE, TRUE)) + { /* pc_update_inode and FATOP both set errno */ + pfile->needs_flush = FALSE; + /* Flush the file allocation table */ + if (!FATOP(pfile->pobj->pdrive)->fatop_flushfat(pfile->pobj->pdrive->driveno)) + ret_val = FALSE; + } + else + ret_val = FALSE; + } + + /*---------- ctr modified ----------*/ + driveno = pfile->pobj->pdrive->driveno; + pdr = pc_drno_to_drive_struct( driveno); + if( pdr) { + if( pdr->dev_table_perform_device_ioctl) { + if( pdr->dev_table_perform_device_ioctl( driveno, + DEVCTL_FLUSH, + NULL) != 0) { + ret_val = FALSE; + } + } + } + /*----------------------------------*/ + return(ret_val); +} + +/**************************************************************************** + PO_FLUSH - Flush a file. + + Description + Flush the file updating the disk. + + Returns + Returns TRUE if all went well otherwise it returns FALSE. + + errno is set to one of the following + 0 - No error + PEBADF - Invalid file descriptor + PEACCES - File is read only + An ERTFS system error +****************************************************************************/ + +BOOLEAN po_flush(PCFD fd) /*__apifn__*/ +{ + PC_FILE *pfile; + BOOLEAN ret_val; + int driveno; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* po_flush: clear error status */ + + /* Get the FILE. must be open for write */ + /* Get the file structure and semaphore lock the drive */ + pfile = pc_fd2file(fd, PO_WRONLY|PO_RDWR); + if (pfile) + { + driveno = pfile->pobj->pdrive->driveno; + ret_val = _po_flush(pfile); /* _po_flush() will set errno */ + if (!release_drive_mount_write(driveno))/* Release lock, unmount if aborted */ + ret_val = -1; + return(ret_val); + } + else + { /* fd2file set errno */ + return(FALSE); + } +} +/***************************************************************************** + pc_set_attributes - Set File Attributes + + Description + Given a file or directory name return set directory entry attributes + associated with the entry. + + The following values may be set: + + BIT Nemonic + 0 ARDONLY + 1 AHIDDEN + 2 ASYSTEM + 5 ARCHIVE + + Note: bits 3 & 4 (AVOLUME,ADIRENT) may not be changed. + + + Returns + Returns TRUE if successful otherwise it returns FALSE. + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + PEINVALIDPATH - Path specified badly formed. + PENOENT - Path not found + PEACCESS - Object is read only + PEINVALIDPARMS - attribute argument is invalid + An ERTFS system error +****************************************************************************/ + +BOOLEAN pc_set_attributes(byte *path, byte attributes) /*__apifn__*/ +{ + DROBJ *pobj; + BOOLEAN ret_val; + int driveno; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_set_attributes: clear error status */ + if ((attributes&(0x40|0x80)) !=0 ) /* Illegal */ + { + rtfs_set_errno(PEINVALIDPARMS); + return(FALSE); + } + driveno = check_drive_name_mount(path); + if (driveno < 0) + { /* errno was set by check_drive */ + return (FALSE); + } + ret_val = FALSE; + /* pc_fndnode will set errno */ + pobj = pc_fndnode(path); + if (pobj) + { + /* Change the attributes if legal */ + if ( + (attributes & (AVOLUME|ADIRENT)) == /* Still same type */ + (pobj->finode->fattribute & (AVOLUME|ADIRENT))) + { + pobj->finode->fattribute = attributes; + /* Overwrite the existing inode. Do not set archive/date */ + /* pc_update_inode() will set errno */ + ret_val = pc_update_inode(pobj, FALSE, FALSE); + } + else + rtfs_set_errno(PEACCES); + + pc_freeobj(pobj); + } + if (!release_drive_mount_write(driveno))/* Release lock, unmount if aborted */ + ret_val = FALSE; + return (ret_val); +} + +/**************************************************************************** + PC_DISKFLUSH - Flush the FAT and all files on a disk + + Description + + If an application may call this functions to force all files + to be flushed and the fat to be flushed. After this call returns + the disk image is synchronized with RTFSs internal view of the + voulme. + +Returns + TRUE if the disk flushed else no + + errno is set to one of the following + 0 - No error + PEINVALIDDRIVEID- Drive component is invalid + An ERTFS system error +****************************************************************************/ + +BOOLEAN pc_diskflush(byte *path) /*__apifn__*/ +{ + int driveno; + DDRIVE *pdrive; + BOOLEAN ret_val; + CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */ + + rtfs_set_errno(0); /* pc_diskflush: clear error status */ + ret_val = FALSE; + driveno = check_drive_name_mount(path); + /* if error, errno was set by check_drive */ + if (driveno >= 0) + { + /* Find the drive */ + pdrive = pc_drno2dr(driveno); + if (pdrive) + { + /*---------- ctr modified ----------*/ + if (pc_flush_all_fil(pdrive)) { + if (FATOP(pdrive)->fatop_flushfat(driveno)) { + ret_val = TRUE; + } + } + if( pdrive->dev_table_perform_device_ioctl) { + if( pdrive->dev_table_perform_device_ioctl( driveno, + DEVCTL_FLUSH, + NULL) == 0) { + ret_val = TRUE; + } + }/*----------------------------------*/ + } + + if (!release_drive_mount_write(driveno))/* Release lock, unmount if aborted */ + ret_val = FALSE; + } + return(ret_val); +} + +/*ctr modified + 本来、pc_diskflushはRTFSのバッファを吐き出す処理を行うが、 + nandはドライバ側でもバッファを持っているためデバイスに反映されない。 + ドライバ側のバッファも同期してデバイスに吐き出すようにするための変更。 + ファイルであれば_po_flushにDEVCTL_FLUSHを引っ掛けていることにより対策。 + mkdir, rmdirなどは、ファイルでないため_po_flushを素通りしてしまうので、 + pc_diskflushにもDEVCTL_FLUSHを引っ掛けることにより対策。 + これだと上位層でpc_diskflushを呼ばないといけないのでmkdir,rmdirなどに + も引っ掛けている。(pc_diskflushでDEVCTL_FLUSHは削除してよいかも) + */ diff --git a/build/libraries/fatfs/ARM7/appcmdsh.c b/build/libraries/fatfs/ARM7/appcmdsh.c new file mode 100644 index 0000000..28487bb --- /dev/null +++ b/build/libraries/fatfs/ARM7/appcmdsh.c @@ -0,0 +1,2473 @@ +/************************************************************************** + APPTSTSH.C - Command driven test shell for embedded file manager. + +Summary + TSTSH + + Description + Interactive shell program designed to allow testing of the file manager. + Commands are: + + BRA V1 < = ! < > > V2 LABEL + CAT PATH + CD PATH or CD to display PWD + CHSIZE FILENAME newsize + CLOSE FDNO + COPY PATH PATH + DELETE PATH + DELTREE PATH + DIFF PATH PATH + DIR PATH + DSKOPEN: D: + DSKSEL D: + ECHO: [args] + FILLFILE PATH PATTERN NTIMES + FORMAT (routine will prompt for arguments) + HELP: + LOAD [FILENAME] + LSTOPEN (lists all open file decscriptors) + MKDIR PATH + MKFS E: + OP V1 <+,*,-,/,%> V2 DEST + QUIET ON,OFF + QUIT + READ FDNO + RENAME PATH NEWNAME + STAT PATH + FSTAT PATH + RMDIR PATH + RNDOP PATH RECLEN + RUN + SEEK FDNO RECORD + SET VALUE + SETATTR D:PATH RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL + WRITE FDNO QUOTED DATA + +Returns + +Example: +***************************************************************************** +*/ + +#include +#include /* For included devices */ + +#define INCLUDE_STRESS_TESTS 0 /* Echo command fires off stress test */ + +do_display_errno(void) +{ +#if (INCLUDE_CS_UNICODE) + RTFS_PRINT_STRING_2(USTRING_SYS_NULL,(byte *)L"Errno == ",0); +#else + RTFS_PRINT_STRING_2(USTRING_SYS_NULL,(byte *)"Errno == ",0); +#endif + RTFS_PRINT_LONG_1((dword)get_errno(), PRFLG_NL); +} +#define DISPLAY_ERRNO(ROUTINE) do_display_errno(); + + +void rtfs_print_format_dir(byte *display_buffer, DSTAT *statobj); +void rtfs_print_format_stat(byte *display_buffer, ERTFS_STAT *st); + +#if (INCLUDE_HOSTDISK) +int domkhostdisk(int agc, byte **agv); +int domkrom(int agc, byte **agv); +#endif + +/* 10-24-2000 added LBA formatting. Submit to main tree */ + +/* Set to one to compile in check disk. */ +#define INCLUDE_CHKDSK 1 +#if (INCLUDE_PCMCIA) +void KS_FAR mgmt_isr(void); +#endif + +byte shell_buf[1024]; +byte working_buf[100]; /* used by lex: must be global */ + + +typedef struct prg_line +{ + struct prg_line *pnext_line; + int label; + byte command[1]; +} PRGLINE; + +typedef struct dispatcher { + int cmd; + int (*proc)( int argc, byte **argv); + int helpstr; +} DISPATCHER; + +typedef struct rndfile { + PCFD fd; + int reclen; + byte name[90]; + byte buff[512]; +} RNDFILE; + +#define MAXRND 10 + +RNDFILE rnds[MAXRND]; + +DISPATCHER *lex(int *agc, byte **agv); +byte *gnext(byte *p); +int echo( int agc, byte **agv); +int doquit(int agc, byte **agv); +int dopcmcia(int agc, byte **agv); +RNDFILE *fndrnd( PCFD fd); +int doeject(int agc, byte **agv); +int dohelp(int agc, byte **agv); +int dodskop(int agc, byte **agv); +int dodskcl(int agc, byte **agv); +int doselect(int agc, byte **agv); +int rndop(int agc, byte **agv); +int doclose(int agc, byte **agv); +int doseek(int agc, byte **agv); +int doread(int agc, byte **agv); +int dowrite(int agc, byte **agv); +int dolstop(int agc, byte **agv); +int domkdir(int agc, byte **agv); +int dormdir(int agc, byte **agv); +int dorm(int agc, byte **agv); +int dodeltree(int agc, byte **agv); +int domv(int agc, byte **agv); +int docwd(int agc, byte **agv); +int dols(int agc, byte **agv); +int pc_seedir(byte *path); +int docat(int agc, byte **agv); +int dochsize(int agc, byte **agv); +#if (INCLUDE_CHKDSK) +int dochkdsk(int agc, byte **agv); +#endif + +int docopy(int agc, byte **agv); +int dodevinfo(int agc, byte **agv); +int dodiff(int agc, byte **agv); +int domkfs(int agc, byte **agv); +int dofillfile(int agc, byte **agv); +int dostat(int agc, byte **agv); +int dogetattr(int agc, byte **agv); +int dosetattr(int agc, byte **agv); +int doformat(int agc, byte **agv); +int doregress(int agc, byte **agv); + +#if (INCLUDE_CDROM) +#include +int cddodskop(int agc, byte **agv); +int cddodskcl(int agc, byte **agv); +int cddoselect(int agc, byte **agv); +int cddocwd(int agc, byte **agv); +int cddols(int agc, byte **agv); +int cddocat(int agc, byte **agv); +/*int cddocopy(int agc, byte **agv); */ +/*int cddoread(int agc, byte **agv); */ +#endif +#if (INCLUDE_FAILSAFE_CODE) +int dofsinit(int agc, byte **agv); +int dofscommit(int agc, byte **agv); +int dofsrestore(int agc, byte **agv); +int dofsstatus(int agc, byte **agv); +int dofstest(int agc, byte **agv); +#endif +/* From rtfsinit.c */ +void print_device_names(void); + + +DISPATCHER cmds[] = + { + { USTRING_TSTSHCMD_26, dohelp, USTRING_TSTSHHELP_26 }, /* "HELP","HELP:" */ + { USTRING_TSTSHCMD_01, docat, USTRING_TSTSHHELP_01 }, /* "CAT","CAT PATH" */ + { USTRING_TSTSHCMD_02, dochsize, USTRING_TSTSHHELP_02}, /* "CHSIZE","CHSIZE FILENAME NEWSIZE" */ + { USTRING_TSTSHCMD_03, docwd, USTRING_TSTSHHELP_03 }, /* "CD","CD PATH or CD to display PWD " */ +#if (INCLUDE_CDROM) + { USTRING_TSTSHCMD_04, cddocat, USTRING_TSTSHHELP_04 }, /* "CDCAT","CDCAT PATH" */ +/* { USTRING_TSTSHCMD_05, cddocopy, USTRING_TSTSHHELP_05 }, "CDCOPY","CDCOPY PATH PATH" */ +/* { USTRING_TSTSHCMD_06, cddoread, USTRING_TSTSHHELP_06 }, "CDREAD","CDREAD PATH" */ + { USTRING_TSTSHCMD_07, cddocwd, USTRING_TSTSHHELP_07 }, /* "CDCD","CDCD PATH " */ + { USTRING_TSTSHCMD_08, cddols, USTRING_TSTSHHELP_08 }, /* "CDDIR","CDDIR PATH" */ + { USTRING_TSTSHCMD_09, cddodskcl, USTRING_TSTSHHELP_09 }, /* "CDDSKCLOSE","CDDSKCLOSE: D:" */ + { USTRING_TSTSHCMD_10, cddodskop, USTRING_TSTSHHELP_10 }, /* "CDDSKOPEN","CDDSKOPEN: D:" */ + { USTRING_TSTSHCMD_11, cddoselect, USTRING_TSTSHHELP_11 }, /* "CDDSKSEL","CDDSKSEL D:" */ +#endif +#if (INCLUDE_CHKDSK) + { USTRING_TSTSHCMD_12, dochkdsk, USTRING_TSTSHHELP_12}, /* "CHKDSK","CHKDSK D: <0,1> 1 is write lost chains" */ +#endif + { USTRING_TSTSHCMD_13, doclose, USTRING_TSTSHHELP_13 }, /* "CLOSE","CLOSE FDNO" */ + { USTRING_TSTSHCMD_14, docopy, USTRING_TSTSHHELP_14 }, /* "COPY","COPY PATH PATH" */ + { USTRING_TSTSHCMD_15,dorm, USTRING_TSTSHHELP_15 }, /* "DELETE","DELETE PATH" */ + { USTRING_TSTSHCMD_16, dodeltree, USTRING_TSTSHHELP_16}, /* "DELTREE","DELTREE PATH" */ + { USTRING_TSTSHCMD_17, dodevinfo, USTRING_TSTSHHELP_17}, /* "DEVINFO","DEVINFO (Display Device Information)" */ + { USTRING_TSTSHCMD_18, dodiff, USTRING_TSTSHHELP_18 }, /* "DIFF","DIFF PATH PATH" */ + { USTRING_TSTSHCMD_19, dols, USTRING_TSTSHHELP_19 }, /* "DIR","DIR PATH" */ + { USTRING_TSTSHCMD_20, doselect, USTRING_TSTSHHELP_20 }, /* "DSKSEL","DSKSEL D:" */ + { USTRING_TSTSHCMD_21, echo, USTRING_TSTSHHELP_21 }, /* "ECHO","ECHO: [args]" */ + { USTRING_TSTSHCMD_22, doeject, USTRING_TSTSHHELP_22 }, /* "EJECT","EJECT (ejects LS-120)" */ + { USTRING_TSTSHCMD_23, dofillfile, USTRING_TSTSHHELP_23}, /* "FILLFILE","FILLFILE PATH PATTERN NTIMES" */ + { USTRING_TSTSHCMD_24, doformat, USTRING_TSTSHHELP_24}, /* "FORMAT","FORMAT (routine will prompt for arguments)" */ + { USTRING_TSTSHCMD_25, dogetattr, USTRING_TSTSHHELP_25}, /* "GETATTR","GETATTR FILE" */ + { USTRING_TSTSHCMD_27, dolstop, USTRING_TSTSHHELP_27 }, /* "LSTOPEN","LSTOPEN (lists all open file decscriptors) " */ + { USTRING_TSTSHCMD_28, domkdir, USTRING_TSTSHHELP_28 }, /* "MKDIR","MKDIR PATH" */ + { USTRING_TSTSHCMD_29, dopcmcia, USTRING_TSTSHHELP_29 }, /* "PCMCIAINT","PCMCIAINT (Force a PCMCIA mgmt Interrupt)" */ + { USTRING_TSTSHCMD_30, 0, USTRING_TSTSHHELP_30 }, /* The Zero value will force a return */ /* "QUIT","QUIT" */ + { USTRING_TSTSHCMD_31, doread, USTRING_TSTSHHELP_31 }, /* "READ","READ FDNO" */ + { USTRING_TSTSHCMD_32,domv, USTRING_TSTSHHELP_32 }, /* "RENAME","RENAME PATH NEWNAME" */ + { USTRING_TSTSHCMD_33, dormdir, USTRING_TSTSHHELP_33 }, /* "RMDIR","RMDIR PATH" */ + { USTRING_TSTSHCMD_34, rndop, USTRING_TSTSHHELP_34 }, /* "RNDOP","RNDOP PATH RECLEN" */ + { USTRING_TSTSHCMD_35, doseek, USTRING_TSTSHHELP_35 }, /* "SEEK","SEEK FDNO RECORD" */ + { USTRING_TSTSHCMD_36, dosetattr, USTRING_TSTSHHELP_36}, /* "SETATTR","SETATTR D:PATH RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL" */ + { USTRING_TSTSHCMD_37, dostat, USTRING_TSTSHHELP_37 }, /* "STAT","STAT PATH" */ + { USTRING_TSTSHCMD_38, dowrite, USTRING_TSTSHHELP_38 }, /* "WRITE","WRITE FDNO QUOTED DATA" */ + { USTRING_TSTSHCMD_39, doregress, USTRING_TSTSHHELP_39 }, /* "REGRESSTEST","REGRESSTEST D:" */ +#if (INCLUDE_HOSTDISK) + { USTRING_TSTSHCMD_40, domkhostdisk, USTRING_TSTSHHELP_40 }, /* "MKHOSTDISK","MKHOSTDISK win9xpath" */ + { USTRING_TSTSHCMD_41, domkrom, USTRING_TSTSHHELP_41 }, /* "MKROM","MKROM" */ +#endif +#if (INCLUDE_FAILSAFE_CODE) + { USTRING_TSTSHCMD_42, dofsinit, USTRING_TSTSHHELP_42 }, /* "FAILSAFEINIT","FAILSAFEINIT" */ + { USTRING_TSTSHCMD_43, dofscommit, USTRING_TSTSHHELP_43 }, /* "FAILSAFECOMMIT","FAILSAFECOMMIT" */ + { USTRING_TSTSHCMD_44, dofsrestore, USTRING_TSTSHHELP_44 }, /* "FAILSAFERESTORE","FAILSAFERESTORE" */ + { USTRING_TSTSHCMD_45, dofsstatus, USTRING_TSTSHHELP_45 }, /* "BUFFERSTAT","BUFFERSTAT D:" */ + { USTRING_TSTSHCMD_46, dofstest, USTRING_TSTSHHELP_46 }, /* "FAILSAFETEST","FAILSAFETEST D: (must be host disk)" */ +#endif + { 0 } + }; + + +byte *gnext(byte *p); +void exit(int); + + +BOOLEAN tstsh_is_(byte *p, byte c) +{ +int index; + index = CS_OP_ASCII_INDEX(p, 'A'); + if (index == (int) (c - CS_OP_ASCII('A'))) + return(TRUE); + else + return(FALSE); +} +BOOLEAN tstsh_is_yes(byte *p) +{ + return(tstsh_is_(p, CS_OP_ASCII('Y'))); +} +BOOLEAN tstsh_is_no(byte *p) +{ + return(tstsh_is_(p, CS_OP_ASCII('N'))); +} + +long rtfs_atol(byte * s); + +int rtfs_atoi(byte * s) +{ + return((int)rtfs_atol(s)); +} + +long rtfs_atol(byte * s) +{ +long n; +BOOLEAN neg; +int index; + /* skip over tabs and spaces */ + while ( CS_OP_CMP_ASCII(s,' ') || CS_OP_CMP_ASCII(s,'\t') ) + CS_OP_INC_PTR(s); + n = 0; + neg = FALSE; + if (CS_OP_CMP_ASCII(s,'-')) + { + neg = TRUE; + CS_OP_INC_PTR(s); + } + while (CS_OP_IS_NOT_EOS(s)) + { + index = CS_OP_ASCII_INDEX(s, '0'); + + if (index >= 0 && index <= 9) + { + n = n * 10; + n += (long) index; + CS_OP_INC_PTR(s); + } + else + break; + } + + if (neg) + n = 0 - n; + + return(n); +} + + + +/* ******************************************************************** */ +/* THIS IS THE MAIN PROGRAM FOR THE TEST SHELL */ +/* ******************************************************************** */ +void tst_shell(void) /* __fn__ */ +{ + DISPATCHER *pcmd; + int agc = 0; + byte *agv[20]; + int i; + + rtfs_memset((byte *)rnds, 0, sizeof(RNDFILE)*MAXRND); + + for (i = 0 ; i < MAXRND; i++) + rnds[i].fd = -1; + rtfs_print_prompt_user(UPROMPT_TSTSH2, working_buf); + dohelp(agc, agv); + + pcmd = lex( &agc, &agv[0]); + while (pcmd) + { + if (!pcmd->proc) + return; + i = pcmd->proc(agc, &agv[0]); + pcmd = lex( &agc, &agv[0]); + } +} + +/* ******************************************************************** */ +/* get next command; process history log */ +DISPATCHER *lex(int *agc, byte **agv) /*__fn__*/ +{ + byte *cmd,*p,*p2; + DISPATCHER *pcmds = &cmds[0]; + + *agc = 0; + /* "CMD>" */ + rtfs_print_prompt_user(UPROMPT_TSTSH1, working_buf); + + p = cmd = &working_buf[0]; + + p = gnext(p); + + while (p) + { + if (CS_OP_CMP_ASCII(p,'"')) + { + p2 = p; + CS_OP_TERM_STRING(p); + CS_OP_INC_PTR(p2); + *agv++ = p2; + } + else + *agv++ = p; + *agc += 1; + p = gnext(p); + } + + while (pcmds->cmd) + { + if (rtfs_cs_strcmp(cmd,rtfs_strtab_user_string(pcmds->cmd)) == 0) + return (pcmds); + pcmds++; + } + + /* No match return ??? */ + + return (&cmds[0]); + } + +/* Null term the current token and return a pointer to the next */ +byte *gnext(byte *p) /*__fn__*/ +{ + byte termat; + /* Look for space or the end of a quoted string */ + if (CS_OP_CMP_ASCII(p,'"')) + termat = CS_OP_ASCII('"'); + else + termat = CS_OP_ASCII(' '); + + CS_OP_INC_PTR(p); + while (CS_OP_IS_NOT_EOS(p)) + { + if (CS_OP_CMP_ASCII(p,termat)) + { + CS_OP_TERM_STRING(p); /* Null it and look at next */ + CS_OP_INC_PTR(p); + break; + } + CS_OP_INC_PTR(p); + } + + if (CS_OP_IS_EOS(p)) /* All done */ + p = 0; + + return (p); +} + + +int echo( int agc, byte **agv) /*__fn__*/ +{ + int i; + for (i = 0; i < agc; i++) + RTFS_PRINT_STRING_2(USTRING_SYS_NULL, *agv++, PRFLG_NL); + return (1); +} + + +int dopcmcia( int agc, byte **agv) /*__fn__*/ +{ + RTFS_ARGSUSED_INT(agc); + RTFS_ARGSUSED_PVOID((void *)agv); +#if (INCLUDE_PCMCIA) + mgmt_isr(); +#endif + /* Release all resources used by the disk the disk */ + return (1); +} + + +/* Given a fd. return a RNDFILE record with matching fd */ +RNDFILE *fndrnd( PCFD fd) /*__fn__*/ +{ +int i; + + for (i = 0 ; i < MAXRND; i++) + { + if (fd == rnds[i].fd) + return (&rnds[i]); + } + return (0); +} + +int dohelp(int agc, byte **agv) /*__fn__*/ +{ +DISPATCHER *pcmds = &cmds[0]; +byte buf[10]; +int nprinted = 0; + + RTFS_ARGSUSED_INT(agc); + RTFS_ARGSUSED_PVOID((void *)agv); + + + while (pcmds->cmd) + { + RTFS_PRINT_STRING_1(pcmds->helpstr, PRFLG_NL); + if (nprinted++ > 20) + { + /* "Press return" */ + rtfs_print_prompt_user(UPROMPT_TSTSH2, buf); + nprinted = 0; + } + pcmds++; + } + return (1); +} + +/* EJECT D: */ +BOOLEAN ide_eject_media(int driveno); + +int doeject(int agc, byte **agv) /*__fn__*/ +{ + RTFS_ARGSUSED_INT(agc); + RTFS_ARGSUSED_PVOID((void *)agv); + RTFS_PRINT_STRING_1(USTRING_TSTSH_02,PRFLG_NL); /* "Not implemented. See ide ioctl... " */ + return(0); +} + + + +/* DSKSEL PATH */ +int doselect(int agc, byte **agv) /*__fn__*/ +{ + if (agc == 1) + { + if (!pc_set_default_drive(*agv)) + { + DISPLAY_ERRNO("pc_set_default_drive") + RTFS_PRINT_STRING_1(USTRING_TSTSH_03,PRFLG_NL); /* "Set Default Drive Failed" */ + return(0); + } + return(1); + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_04, PRFLG_NL); /* "Usage: DSKSELECT D: " */ + return(0); + } +} + + +/* RNDOP PATH RECLEN */ +int rndop(int agc, byte **agv) /*__fn__*/ +{ + RNDFILE *rndf; + + if (agc == 2) + { + rndf = fndrnd( -1); + if (!rndf) + {RTFS_PRINT_STRING_1(USTRING_TSTSH_05, PRFLG_NL);} /* "No more random file slots " */ + else + { + rtfs_cs_strcpy(rndf->name, *agv); + agv++; + rndf->reclen = (int)rtfs_atoi(*agv); + if ((rndf->fd = po_open(rndf->name, (PO_BINARY|PO_RDWR|PO_CREAT), + (PS_IWRITE | PS_IREAD) ) ) < 0) + { + DISPLAY_ERRNO("pc_open(PO_BINARY|PO_RDWR|PO_CREAT)") + RTFS_PRINT_STRING_2(USTRING_TSTSH_06,rndf->name,PRFLG_NL); /* "Cant open :" */ + /* Note: rndf->fd is still -1 on error */ + } + else + return (1); + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_07,PRFLG_NL); /* "Usage: RNDOP D:PATH RECLEN" */ + return (0); +} + + +/* CLOSE fd */ +int doclose(int agc, byte **agv) /*__fn__*/ +{ + PCFD fd; + RNDFILE *rndf; + + if (agc == 1) + { + fd = rtfs_atoi(*agv); + rndf = fndrnd( fd ); + if (!rndf) + {RTFS_PRINT_STRING_1(USTRING_TSTSH_08,PRFLG_NL);} /* "Cant find file" */ + else + { + if (po_close(fd) < 0) + { + DISPLAY_ERRNO("po_close") + RTFS_PRINT_STRING_1(USTRING_TSTSH_09,PRFLG_NL); /* "Close failed" */ + } + else + { + rndf->fd = -1; + return (1); + } + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_10,PRFLG_NL); /* "Usage: CLOSE fd" */ + return (0); +} + +/* SEEK fd recordno */ +int doseek(int agc, byte **agv) /*__fn__*/ +{ + PCFD fd; + RNDFILE *rndf; + int recno; + long foff; + long seekret; + + if (agc == 2) + { + fd = rtfs_atoi(*agv); + rndf = fndrnd(fd); + if (!rndf) + {RTFS_PRINT_STRING_1(USTRING_TSTSH_11,PRFLG_NL);} /* "Cant find file" */ + else + { + agv++; + recno = rtfs_atoi(*agv); + foff = (long) recno * rndf->reclen; + + if (foff !=(seekret = po_lseek(fd, foff, PSEEK_SET ) )) + { + DISPLAY_ERRNO("po_lseek") + RTFS_PRINT_STRING_1(USTRING_TSTSH_12, PRFLG_NL); /* "Seek operation failed " */ + } + else + return (1); + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_13,PRFLG_NL); /* "Usage: SEEK fd recordno" */ + return (0); +} + +/* READ fd */ +int doread(int agc, byte **agv) /*__fn__*/ +{ + PCFD fd; + RNDFILE *rndf; + int res; + + if (agc == 1) + { + fd = (PCFD) rtfs_atoi(*agv); + rndf = fndrnd( fd ); + if (!rndf) + {RTFS_PRINT_STRING_1(USTRING_TSTSH_14,PRFLG_NL);} /* "Cant find file" */ + else + { + if ( (res = po_read(fd,(byte*)rndf->buff,(word)rndf->reclen)) != rndf->reclen) + { + DISPLAY_ERRNO("po_read") + RTFS_PRINT_STRING_1(USTRING_TSTSH_15, PRFLG_NL); /* "Read operation failed " */ + } + else + { + RTFS_PRINT_STRING_2(USTRING_SYS_NULL, rndf->buff,PRFLG_NL); + return (1); + } + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_16,PRFLG_NL); /* "Usage: READ fd" */ + return (0); +} + +/* WRITE fd "data" */ +int dowrite(int agc, byte **agv) /*__fn__*/ +{ + PCFD fd; + RNDFILE *rndf; + int res; + + if (agc == 2) + { + fd = rtfs_atoi(*agv++); + rndf = fndrnd( fd ); + if (!rndf) + {RTFS_PRINT_STRING_1(USTRING_TSTSH_17,PRFLG_NL);} /* "Cant find file" */ + else + { + pc_cppad((byte*)rndf->buff,(byte*) *agv,(int) rndf->reclen); + rndf->buff[rndf->reclen] = CS_OP_ASCII('\0'); + if ( (res = po_write(fd,(byte*)rndf->buff,(word)rndf->reclen)) != rndf->reclen) + { + DISPLAY_ERRNO("po_write") + RTFS_PRINT_STRING_1(USTRING_TSTSH_18, PRFLG_NL); /* "Write operation failed " */ + } + else + { + return (1); + } + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_19,PRFLG_NL); /* "Usage: WRITE fd data " */ + return (0); +} + +/* LSTOPEN */ +int dolstop(int agc, byte **agv) /*__fn__*/ +{ + int i; + RTFS_ARGSUSED_INT(agc); + RTFS_ARGSUSED_PVOID((void *)agv); + + for (i = 0 ; i < MAXRND; i++) + { + if (rnds[i].fd != -1) + { + RTFS_PRINT_LONG_1((dword)rnds[i].fd, 0); + RTFS_PRINT_STRING_2(USTRING_TSTSH_30, rnds[i].name, PRFLG_NL); + } + } + return (1); +} + +/* MKDIR PATH */ +int domkdir(int agc, byte **agv) /*__fn__*/ +{ + if (agc == 1) + { + if (pc_mkdir(*agv)) + return (1); + else + { + DISPLAY_ERRNO("pc_mkdir") + return(0); + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_20,PRFLG_NL); /* "Usage: MKDIR D:PATH" */ + return (0); +} + +/* RMDIR PATH */ +int dormdir(int agc, byte **agv) /*__fn__*/ +{ + + if (agc == 1) + { + if (pc_rmdir(*agv)) + return (1); + else + { + DISPLAY_ERRNO("pc_rmdir") + return(0); + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_21,PRFLG_NL); /* "Usage: RMDIR D:PATH\n" */ + return (0); +} + +/* DELTREE PATH */ +int dodeltree(int agc, byte **agv) /*__fn__*/ +{ + + if (agc == 1) + { + if (pc_deltree(*agv)) + return (1); + else + { + DISPLAY_ERRNO("pc_deltree") + return(0); + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_22,PRFLG_NL); /* "Usage: DELTREE D:PATH" */ + return (0); +} + +/* DELETE PATH */ +byte rmfil[EMAXPATH_BYTES]; +byte rmpath[EMAXPATH_BYTES]; +byte tfile[EMAXPATH_BYTES]; +byte _rmpath[EMAXPATH_BYTES]; +int dorm(int agc, byte **agv) /*__fn__*/ +{ + DSTAT statobj; + + if (agc == 1) + { + /* Get the path */ + rtfs_cs_strcpy(&rmfil[0],*agv); + + if (pc_gfirst(&statobj, &rmfil[0])) + { + do + { + /* Construct the path to the current entry ASCII 8.3 */ + pc_ascii_mfile((byte*) &tfile[0],(byte*) &statobj.fname[0], (byte*) &statobj.fext[0]); + pc_mpath((byte*)&_rmpath[0],(byte*) &statobj.path[0],(byte*)&tfile[0] ); + /* Convert to native character set if needed */ + CS_OP_ASCII_TO_CS_STR(&rmpath[0], &_rmpath[0]); + if (!pc_isdir(rmpath) && !pc_isvol(rmpath)) + { + RTFS_PRINT_STRING_2(USTRING_TSTSH_23,rmpath,PRFLG_NL); /* "deleting --> " */ + if (!pc_unlink( rmpath ) ) + { + DISPLAY_ERRNO("pc_unlink") + RTFS_PRINT_STRING_2(USTRING_TSTSH_24,rmpath,PRFLG_NL); /* "Can not delete: " */ + } + } + } while(pc_gnext(&statobj)); + DISPLAY_ERRNO("pc_gnext: PENOENT IS OKAY") + pc_gdone(&statobj); + } + else + { + DISPLAY_ERRNO("pc_gfirst") + } + return(1); + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_25,PRFLG_NL); /* "Usage: DELETE D:PATH" */ + return(0); +} + +/* RENAME PATH NEWNAME */ +int domv(int agc, byte **agv) /*__fn__*/ +{ + if (agc == 2) + { + if (pc_mv(*agv , *(agv+1))) + return (1); + else + { + DISPLAY_ERRNO("pc_mv") + return(0); + } + } + + RTFS_PRINT_STRING_1(USTRING_TSTSH_26,PRFLG_NL); /* "Usage: RENAME PATH NEWNAME" */ + return (0); +} + + +/* CD PATH */ +int docwd(int agc, byte **agv) /*__fn__*/ +{ + byte lbuff[EMAXPATH_BYTES]; + + if (agc == 1) + { + if (pc_set_cwd(*agv)) + return(1); + else + { + DISPLAY_ERRNO("pc_set_cwd") + RTFS_PRINT_STRING_1(USTRING_TSTSH_27,PRFLG_NL); /* "Set cwd failed" */ + return(0); + } + } + else + { + if (pc_pwd(rtfs_strtab_user_string(USTRING_SYS_NULL),lbuff)) + { + RTFS_PRINT_STRING_2(USTRING_SYS_NULL,lbuff,PRFLG_NL); + return(1); + } + else + { + DISPLAY_ERRNO("pc_pwd") + RTFS_PRINT_STRING_1(USTRING_TSTSH_28,PRFLG_NL); /* "Get cwd failed" */ + return(0); + } + } +} + +/* DIR PATH */ +int dols(int agc, byte **agv) /*__fn__*/ +{ + int fcount; + int doit, addwild; + byte *ppath,*p; + dword blocks_total, blocks_free; + dword nfree; + byte null_str[2]; + ppath = 0; + + addwild = 0; + if (agc == 1) + { + ppath = *agv; + p = ppath; + /* If the second char is ':' and the third is '\0' then we have + D: and we will convert to D:*.* */ + CS_OP_INC_PTR(p); + if (CS_OP_CMP_ASCII(p,':')) + { + CS_OP_INC_PTR(p); + if (CS_OP_IS_EOS(p)) + addwild = 1; + } + } + else + { + null_str[0] = null_str[1] = 0; /* Works for all char sets */ + /* get the working dir of the default dir */ + if (!pc_pwd(null_str,(byte *)shell_buf)) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_29,PRFLG_NL); /* "PWD Failed " */ + return(1); + } + ppath = (byte *)shell_buf; + p = ppath; + + /* If not the root add a \\ */ + doit = 1; /* Add \\ if true */ + if (CS_OP_CMP_ASCII(p,'\\')) + { + CS_OP_INC_PTR(p); + if (CS_OP_IS_EOS(p)) + doit = 0; + } + if (doit) + { + p = CS_OP_GOTO_EOS(p); + CS_OP_ASSIGN_ASCII(p,'\\'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + } + addwild = 1; + } + if (addwild) + { + p = ppath; + p = CS_OP_GOTO_EOS(p); + /* Now tack on *.* */ + CS_OP_ASSIGN_ASCII(p,'*'); + CS_OP_INC_PTR(p); + /* Add .* for non vfat */ + CS_OP_ASSIGN_ASCII(p,'.'); + CS_OP_INC_PTR(p); + CS_OP_ASSIGN_ASCII(p,'*'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + } + + + /* Now do the dir */ + fcount = pc_seedir(ppath); + + /* And print the bytes remaining */ + nfree = pc_free(ppath, &blocks_total, &blocks_free); +/* printf( " %-6d File(s) %-8ld KBytes free\n", fcount, blocks_free/2 ); */ + + RTFS_PRINT_STRING_1(USTRING_TSTSH_30, 0); /* " " */ + RTFS_PRINT_LONG_1((dword) fcount, 0); + RTFS_PRINT_STRING_1(USTRING_TSTSH_31, 0); /* " File(s) " */ + RTFS_PRINT_LONG_1((dword) blocks_free, 0); + RTFS_PRINT_STRING_1(USTRING_TSTSH_32, PRFLG_NL); /* " Blocks Free " */ + return(1); +} + +/* List directory, and return number of matching file */ +int pc_seedir(byte *path) /*__fn__*/ +{ + int fcount = 0; + DSTAT statobj; + byte display_buffer[100]; + /* Get the first match */ + if (pc_gfirst(&statobj, path)) + { + for (;;) + { + fcount++; + + rtfs_print_format_dir(display_buffer, &statobj); + + /* Get the next */ + if (!pc_gnext(&statobj)) + break; + + } + DISPLAY_ERRNO("pc_next") + /* Call gdone to free up internal resources used by statobj */ + pc_gdone(&statobj); + } + else + { + DISPLAY_ERRNO("pc_gfirst") + } + return(fcount); +} + + +int doregress(int agc, byte **agv) +{ + if (agc == 1) + { + pc_regression_test(*agv, TRUE); + return(1); + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_110,PRFLG_NL); /* "Usage: REGRESSTEST D:" */ + return(0); + } +} + + +/* CAT PATH */ +int docat(int agc, byte **agv) /*__fn__*/ +{ + PCFD fd; + int nread; + + + if (agc == 1) + { + if ((fd = po_open(*agv, (word)(PO_BINARY|PO_RDONLY|PO_BUFFERED),(word) (PS_IWRITE | PS_IREAD) ) ) < 0) + { + DISPLAY_ERRNO("po_open") + RTFS_PRINT_STRING_2(USTRING_TSTSH_33, *agv,PRFLG_NL); /* "Cant open " */ + return(0); + } + else + { + do + { + + nread = po_read(fd,(byte*)shell_buf,1); + if (nread > 0) + { + shell_buf[1] = '\0'; + RTFS_PRINT_STRING_2(USTRING_SYS_NULL,shell_buf,0); + } + if (nread < 0) + { + DISPLAY_ERRNO("po_read") + } + }while(nread > 0); + if (po_close(fd) != 0) + { + DISPLAY_ERRNO("po_read") + } + return(1); + } + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_34,PRFLG_NL); /* "Usage: CAT PATH" */ + return(0); + } +} + + +#if (INCLUDE_CHKDSK) + +/* */ +int dochkdsk(int agc, byte **agv) /*__fn__*/ +{ +int write_chains; +CHKDISK_STATS chkstat; + if (agc != 2) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_35,PRFLG_NL); /* "Usage: CHKDSK DRIVE: WRITECHAINS" */ + RTFS_PRINT_STRING_1(USTRING_TSTSH_36,PRFLG_NL); /* "Example:CHKDSK A: 1" */ + RTFS_PRINT_STRING_1(USTRING_TSTSH_37,PRFLG_NL); /* "Runs chkdsk on A: writes lost chains" */ + RTFS_PRINT_STRING_1(USTRING_TSTSH_38,PRFLG_NL); /* "Example:CHKDSK A: 0" */ + RTFS_PRINT_STRING_1(USTRING_TSTSH_39,PRFLG_NL); /* "Runs chkdsk on A: does not write lost chains" */ + return(0); + } + write_chains = (int) rtfs_atoi( *(agv+1) ); + pc_check_disk(*agv, &chkstat, 1, write_chains, write_chains); + return(0); +} +#endif + +int dochsize(int agc, byte **agv) /*__fn__*/ +{ + PCFD fd; + long newsize; + + if (agc == 2) + { + if ((fd = po_open(*agv, (word)(PO_BINARY|PO_WRONLY),(word) (PS_IWRITE | PS_IREAD) ) ) < 0) + { + DISPLAY_ERRNO("po_open") + RTFS_PRINT_STRING_2(USTRING_TSTSH_40, *agv ,PRFLG_NL); /* "Cant open file: " */ + return(0); + } + newsize = rtfs_atol( *(agv+1) ); + if (po_chsize(fd, newsize) != 0) + { + DISPLAY_ERRNO("po_chsize") + RTFS_PRINT_STRING_1(USTRING_TSTSH_41, PRFLG_NL); /* "Change size function failed" */ + } + if (po_close(fd) != 0) + { + DISPLAY_ERRNO("po_close") + } + return(1); + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_42,PRFLG_NL); /* "Usage: CHSIZE PATH NEWSIZE" */ + return(0); + } +} + +/* COPY PATH PATH */ +int docopy(int agc, byte **agv) /*__fn__*/ +{ + PCFD in_fd; + PCFD out_fd; + int nread; + BOOLEAN forever = TRUE; /* use this for while(forever) to quiet anal compilers */ + + if (agc == 2) + { + if ((in_fd = po_open(*agv,(word) (PO_BINARY|PO_RDONLY),(word) (PS_IWRITE | PS_IREAD) ) ) < 0) + { + DISPLAY_ERRNO("po_open") + RTFS_PRINT_STRING_2(USTRING_TSTSH_43, *agv,PRFLG_NL); /* "Cant open file: " */ + return(0); + } + if ((out_fd = po_open(*(agv+1),(word) (PO_BINARY|PO_WRONLY|PO_CREAT),(word) (PS_IWRITE | PS_IREAD) ) ) < 0) + { + DISPLAY_ERRNO("po_open") + RTFS_PRINT_STRING_2(USTRING_TSTSH_44, *(agv+1),PRFLG_NL); /* "Cant open file" */ + return(0); + } + while (forever) + { + nread = (word)po_read(in_fd,(byte*)shell_buf, 1024); + if (nread < 0) + { + DISPLAY_ERRNO("po_read") + break; + } + else if (nread > 0) + { + if (po_write(out_fd,(byte*)shell_buf,(word)nread) != (int)nread) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_45,PRFLG_NL); /* "Write failure " */ + break; + } + } + else + break; + } + po_close(in_fd); + po_close(out_fd); + return(1); + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_46,PRFLG_NL); /* "Usage: COPY FROMPATH TOPATH\n" */ + return(0); + } +} + +/* DIFF PATH PATH */ +int dodiff(int agc, byte **agv) /*__fn__*/ +{ + PCFD in_fd; + PCFD in_fd1; + int nread; + int nread1; + int i; + byte buff[256]; + byte buff1[256]; + + + if (agc == 2) + { + if ((in_fd = po_open(*agv, (PO_BINARY|PO_RDONLY), (PS_IWRITE | PS_IREAD) ) ) < 0) + { + DISPLAY_ERRNO("po_open") + RTFS_PRINT_STRING_2(USTRING_TSTSH_47, *agv,PRFLG_NL); /* "Cant open file: " */ + return(0); + } + if ((in_fd1 = po_open(*(agv+1), (PO_BINARY|PO_RDONLY), (PS_IWRITE | PS_IREAD) ) ) < 0) + { + DISPLAY_ERRNO("po_open") + RTFS_PRINT_STRING_2(USTRING_TSTSH_48, *(agv+1),PRFLG_NL); /* "Cant open file" */ + return(0); + } + for (;;) + { + nread = po_read(in_fd,(byte*)buff,(word)255); + if (nread > 0) + { + nread1 = po_read(in_fd1,(byte*)buff1,(word)nread); + if (nread1 != nread) + { + if (nread1 < 0) + { + DISPLAY_ERRNO("po_read") + } +difffail: + RTFS_PRINT_STRING_1(USTRING_TSTSH_49,PRFLG_NL); /* "Files are different" */ + po_close (in_fd); + po_close (in_fd); + return(0); + } + for(i = 0; i < nread; i++) + { + if (buff[i] != buff1[i]) + goto difffail; + } + } + else + { + if (nread < 0) + { + DISPLAY_ERRNO("po_read") + } + break; + } + } + nread1 = po_read(in_fd1,(byte*)buff1,(word)nread); + if (nread1 <= 0) + { + if (nread1 < 0) + { + DISPLAY_ERRNO("po_read") + } + RTFS_PRINT_STRING_2(USTRING_TSTSH_50,*agv,0); /* "File: " */ + RTFS_PRINT_STRING_2(USTRING_TSTSH_51,*(agv+1),0); /* " And File: " */ + RTFS_PRINT_STRING_1(USTRING_TSTSH_52,PRFLG_NL); /* " are the same" */ + } + else + { + { + RTFS_PRINT_STRING_2(USTRING_TSTSH_53, *(agv+1), 0); /* "File: " */ + RTFS_PRINT_STRING_2(USTRING_TSTSH_54, *agv, PRFLG_NL); /* " is larger than File: " */ + } + goto difffail; + } + po_close(in_fd); + po_close(in_fd1); + return(1); + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_55, PRFLG_NL); /* "Usage: DIFF PATH PATH " */ + } + return (0); +} + + +/* FILLFILE PATH PATTERN NTIMES */ +int dofillfile(int agc, byte **agv) /*__fn__*/ +{ + PCFD out_fd; + byte workbuf[255]; + word bufflen; + int ncopies; + + if (agc == 3) + { + ncopies = (int) rtfs_atoi( *(agv+2) ); + if (!ncopies) + ncopies = 1; + + if ((out_fd = po_open(*agv,(word) (PO_BINARY|PO_WRONLY|PO_CREAT),(word) (PS_IWRITE | PS_IREAD) ) ) < 0) + { + DISPLAY_ERRNO("po_open") + RTFS_PRINT_STRING_2(USTRING_TSTSH_56, *agv, PRFLG_NL); /* "Cant open file" */ + return(0); + } + rtfs_cs_strcpy(workbuf, *(agv+1)); + rtfs_strcat(workbuf, (byte *) "\r\n"); + bufflen = (word) rtfs_strlen(workbuf); + + while (ncopies--) + { + if (po_write(out_fd,(byte*)workbuf,(word)bufflen) != (int)bufflen) + { + DISPLAY_ERRNO("po_write") + RTFS_PRINT_STRING_1(USTRING_TSTSH_57, PRFLG_NL); /* "Write failure" */ + po_close(out_fd); + return(0); + } + } + po_close(out_fd); + return(1); + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_58, PRFLG_NL); /* "Usage: FILLFILE PATH PATTERN NTIMES" */ + return(0); + } +} + +/* STAT PATH */ +int dostat(int agc, byte **agv) /*__fn__*/ +{ +ERTFS_STAT st; +byte display_buffer[256]; + if (agc == 1) + { + if (pc_stat(*agv, &st)==0) + { + rtfs_print_format_stat(display_buffer, &st); + RTFS_PRINT_STRING_1(USTRING_TSTSH_59, 0); /* "MODE BITS :" */ + if (st.st_mode&S_IFDIR) + RTFS_PRINT_STRING_1(USTRING_TSTSH_60, 0); /* "S_IFDIR|" */ + if (st.st_mode&S_IFREG) + RTFS_PRINT_STRING_1(USTRING_TSTSH_61, 0); /* "S_IFREG|" */ + if (st.st_mode&S_IWRITE) + RTFS_PRINT_STRING_1(USTRING_TSTSH_62, 0); /* "S_IWRITE|" */ + if (st.st_mode&S_IREAD) + RTFS_PRINT_STRING_1(USTRING_TSTSH_63, 0); /* "S_IREAD\n" */ + RTFS_PRINT_STRING_1(USTRING_SYS_NULL, PRFLG_NL); /* "" */ + + return (1); + } + else + { + DISPLAY_ERRNO("pc_stat") + RTFS_PRINT_STRING_1(USTRING_TSTSH_65, PRFLG_NL); /* "FSTAT failed" */ + return(0); + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_66, PRFLG_NL); /* "Usage: FSTAT D:PATH" */ + return (0); +} +#if (INCLUDE_FAILSAFE_CODE) +int dofsstatus(int agc, byte **agv) +{ +struct pro_buffer_stats s; + if (agc == 1) + { + if (!pro_buffer_status(*agv, &s)) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_136, PRFLG_NL); /* "pro_buffer_status failed" */ + return -1; + } + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_137, PRFLG_NL); /* "USAGE: BUFFERSTAT D:" */ + return -1; + } + if (s.failsafe_mode == 0) + RTFS_PRINT_STRING_1(USTRING_TSTSH_118, PRFLG_NL); /* "Failsafe disabled" */ + else if (s.failsafe_mode == 1) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_119, PRFLG_NL); /* "Failsafe enabled with journaling " */ + + RTFS_PRINT_STRING_1(USTRING_TSTSH_147, 0); /* "Failsafe file blocks consumed" */ + RTFS_PRINT_LONG_1(s.failsafe_blocks_used, PRFLG_NL); + + RTFS_PRINT_STRING_1(USTRING_TSTSH_148, 0); /* "Failsafe file blocks still available" */ + RTFS_PRINT_LONG_1(s.failsafe_blocks_free, PRFLG_NL); + } + else if (s.failsafe_mode == 2) + RTFS_PRINT_STRING_1(USTRING_TSTSH_120, PRFLG_NL); /* "Failsafe enabled with no journaling" */ + + + + RTFS_PRINT_STRING_1(USTRING_TSTSH_121, 0); /* " Total Number of Block Buffers" */ + RTFS_PRINT_LONG_1(s.total_block_buffers, PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_122, 0); /* " Pending writes on Block Buffers" */ + RTFS_PRINT_LONG_1(s.block_buffers_pending,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_123, 0); /* " Block Buffers available" */ + RTFS_PRINT_LONG_1(s.block_buffers_available,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_124, 0); /* " Block Buffer Cache Hits" */ + RTFS_PRINT_LONG_1(s.block_buffers_cache_hits,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_125, 0); /* " Block Buffer Cache Misses" */ + RTFS_PRINT_LONG_1(s.block_buffers_cache_misses,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_126, 0); /* " Block Buffer Low Water" */ + RTFS_PRINT_LONG_1(s.block_buffers_low,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_127, 0); /* " Block Allocation failures" */ + RTFS_PRINT_LONG_1(s.block_buffers_fail,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_128, 0); /* " Total Number of FAT Buffers" */ + RTFS_PRINT_LONG_1(s.total_fat_buffers,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_129, 0); /* " FAT Buffers never used" */ + RTFS_PRINT_LONG_1(s.fat_buffers_free,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_130, 0); /* " FAT Buffer Primary Cache Hits" */ + RTFS_PRINT_LONG_1(s.fat_buffer_primary_cache_hits,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_131, 0); /* " FAT Buffer Secondary Cache Hits" */ + RTFS_PRINT_LONG_1(s.fat_buffer_secondary_cache_hits,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_132, 0); /* " FAT Buffer Secondary Cache Loads" */ + RTFS_PRINT_LONG_1(s.fat_buffer_cache_loads,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_133, 0); /* " FAT Buffer Secondary Cache Swaps" */ + RTFS_PRINT_LONG_1(s.fat_buffer_cache_swaps,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_134, 0); /* " Pending writes on FAT Buffers" */ + RTFS_PRINT_LONG_1(s.fat_buffers_pending,PRFLG_NL); + RTFS_PRINT_STRING_1(USTRING_TSTSH_135, 0); /* " FAT Buffers available" */ + RTFS_PRINT_LONG_1(s.fat_buffers_available,PRFLG_NL); + return (0); +} + + + +int fs_test(byte *drivename); + +int dofstest(int agc, byte **agv) +{ + if (agc != 1) + return (-1); + else + return (fs_test(*agv)); +} + +/* Reserve some storage for block mapping structure. See the + description of fscontext.blockmap_size below for a discussion + on block maps */ +FAILSAFECONTEXT fscontext; +#define BLOCKMAPSIZE 128 +FSBLOCKMAP failsafe_blockmap_array[BLOCKMAPSIZE]; + +void app_failsafe_init(byte *drive_name) +{ + rtfs_memset((void *) &fscontext, 0, sizeof(fscontext)); + /* Initial setting. + autorestore, autocommit and autorecover options all disabled */ + fscontext.configuration_flags = 0; + /* Select AUTORESTORE. - The volume is autorestored if needed at mount time + . If restore is needed but the failsafe file is damaged the mount fails + unless FS_MODE_AUTORECOVER is also enabled */ + fscontext.configuration_flags |= FS_MODE_AUTORESTORE; + /* Select AUTORECOVER. - If autorestore failed because the failsafe file + is damaged skip the restore state and proceed with the mount */ + fscontext.configuration_flags |= FS_MODE_AUTORECOVER; + /* Enable AUTOCOMMIT mode. Force ERTFS to perform the FailSafe commit + internally before the return from API calls that may have changed + volume */ + fscontext.configuration_flags |= FS_MODE_AUTOCOMMIT; + /* Optional setting - If the user_journal_size is set prior to the + call to pro_failsafe_init() the FailSafe file space is limitted to + this size. Otherwise the Failsafe file size is set to the size of + the FAT plus CFG_NUM_JOURNAL_BLOCKS (prfailsf.h) blocks. + The default setting are purposely conservative. One block must be + available for each FAT block and directory block that will be + changed between calls to pro_failsafe_commit(). + The following line, if enabled, will fix the failsafe journal size + to 64K. This would be appropriate for example if the journal file + was being held in 64K of nvram. */ + /* fscontext.user_journal_size = 128; */ /* In blocks */ + /* Provide buffer space for failsafe to cache indexing information + pertaining to journaling activities. Without adequate caching + failsafe will still perform as specified but with lowered + performance. Each blockmap element required approximately + 32 bytes, for optimal performance one element should be + available for each FAT block and directory block that will be + changed between calls to pro_failsafe_commit(). In this example + we choose 128 entries. This is very a conservative setting that will + allow failsafe to use caching in a session with up to 128 + FAT and directory blocks changed. + Note that performance will degrade drastically if the number of + blocks modified exceeds the size of the block map. + If you have a need to minimize resources you may reduce blockmap_size + and user_journal_size. To help determine appropriate values you may + call pro_buffer_status(), the failsafe_blocks_used field in the status + structure will contain the number of these resources used. + In order to gauge the worst case usage, pro_buffer_status() should be + called immediately prior to calling pro_failsafe_commit() + */ + fscontext.blockmap_size = BLOCKMAPSIZE; + fscontext.blockmap_freelist = &failsafe_blockmap_array[0]; + + if (!pro_failsafe_init(drive_name, &fscontext)) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_114, PRFLG_NL); /* "Failsafe init failed\n" */ + if (get_errno() == PEFSREINIT) + { + /* "Failsafe already running on a drive\n" */ + RTFS_PRINT_STRING_1(USTRING_TSTSH_115, PRFLG_NL); + /* "Command shell only supports one failsafe session\n" */ + RTFS_PRINT_STRING_1(USTRING_TSTSH_116, PRFLG_NL); + } + } + +} + +int dofsinit(int agc, byte **agv) +{ + if (agc == 1) + app_failsafe_init(*agv); + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_111, PRFLG_NL); /* "Usage: FAILSAFEINIT D:" */ + } + return(0); +} +int dofscommit(int agc, byte **agv) +{ + if (agc == 1) + { + if (!pro_failsafe_commit(*agv)) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_113, PRFLG_NL); /* "Failsafe commit failed\n" */ + } + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_112, PRFLG_NL); /* "Usage: FAILCOMMIT D:" */ + } + return(0); +} +int dofsrestore(int agc, byte **agv) +{ +int rval; + + if (agc == 1) + { + rval = pro_failsafe_restore(*agv,0,FALSE,FALSE); + switch (rval) + { + case FS_STATUS_OK: + RTFS_PRINT_STRING_1(USTRING_TSTSH_138, PRFLG_NL); /* "FAILSAFE: Volume up to date, no restore required" */ + break; + case FS_STATUS_NO_JOURNAL: + RTFS_PRINT_STRING_1(USTRING_TSTSH_139, PRFLG_NL); /* "FAILSAFE: no journal present" */ + break; + case FS_STATUS_BAD_JOURNAL: + RTFS_PRINT_STRING_1(USTRING_TSTSH_140, PRFLG_NL); /* "FAILSAFE: bad journal file present" */ + break; + case FS_STATUS_BAD_CHECKSUM: + RTFS_PRINT_STRING_1(USTRING_TSTSH_141, PRFLG_NL); /* "FAILSAFE: journal file has checksum error" */ + break; + case FS_STATUS_IO_ERROR: + RTFS_PRINT_STRING_1(USTRING_TSTSH_142, PRFLG_NL); /* "FAILSAFE: IO error during restore" */ + break; + case FS_STATUS_RESTORED: + RTFS_PRINT_STRING_1(USTRING_TSTSH_144, PRFLG_NL); /* "FAILSAFE: Volume was restored sucessfully" */ + break; + case FS_STATUS_MUST_RESTORE: + RTFS_PRINT_STRING_1(USTRING_TSTSH_145, PRFLG_NL); /* "FAILSAFE: Restore required" */ + break; + default: + RTFS_PRINT_STRING_1(USTRING_TSTSH_146, 0); /* "FAILSAFE: Restore unknown return code" */ + RTFS_PRINT_LONG_1((dword)rval, PRFLG_NL); + break; + } + if (rval == FS_STATUS_MUST_RESTORE) + { + rval = pro_failsafe_restore(*agv,0,TRUE,FALSE); + switch (rval) + { + case FS_STATUS_BAD_JOURNAL: + RTFS_PRINT_STRING_1(USTRING_TSTSH_140, PRFLG_NL); /* "FAILSAFE: bad journal file present" */ + break; + case FS_STATUS_BAD_CHECKSUM: + RTFS_PRINT_STRING_1(USTRING_TSTSH_141, PRFLG_NL); /* "FAILSAFE: journal file has checksum error" */ + break; + case FS_STATUS_IO_ERROR: + RTFS_PRINT_STRING_1(USTRING_TSTSH_142, PRFLG_NL); /* "FAILSAFE: IO error during restore" */ + break; + case FS_STATUS_RESTORED: + RTFS_PRINT_STRING_1(USTRING_TSTSH_144, PRFLG_NL); /* "FAILSAFE: Volume was restored sucessfully" */ + break; + case FS_STATUS_MUST_RESTORE: + RTFS_PRINT_STRING_1(USTRING_TSTSH_145, PRFLG_NL); /* "FAILSAFE: Restore required" */ + break; + default: + RTFS_PRINT_STRING_1(USTRING_TSTSH_146, 0); /* "FAILSAFE: Restore unknown return code" */ + RTFS_PRINT_LONG_1((dword)rval, PRFLG_NL); + break; + } + } + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_111, PRFLG_NL); /* "Usage: FAILSAFERESTORE D:" */ + } + return(0); +} +#endif + +/* FORMAT D:*/ +int doformat(int agc, byte **agv) /*__fn__*/ +{ +byte buf[10]; +byte working_buffer[100]; +DDRIVE *pdr; +int driveno; +dword partition_list[3]; +byte path[10]; +DEV_GEOMETRY geometry; + + RTFS_ARGSUSED_INT(agc); + RTFS_ARGSUSED_PVOID((void *)agv); + + + /* "Enter the drive to format as A:, B: etc " */ + rtfs_print_prompt_user(UPROMPT_TSTSH3, path); + pdr = 0; + driveno = pc_parse_raw_drive(path); + if (driveno != -1) + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr) + { +inval: + /* "Invalid drive selection to format, press return" */ + rtfs_print_prompt_user(UPROMPT_TSTSH4, working_buffer); + return(-1); + } + + if ( !(pdr->drive_flags&DRIVE_FLAGS_VALID) ) + { + goto inval; + } + + OS_CLAIM_LOGDRIVE(driveno) + /* check media and clear change conditions */ + if (!check_drive_number_present(driveno)) + { + /* "Format - check media failed. Press return" */ + rtfs_print_prompt_user(UPROMPT_TSTSH5, working_buffer); + goto return_error; + } + + /* This must be called before calling the later routines */ + if (!pc_get_media_parms(path, &geometry)) + { + /* "Format: get media geometry failed. Press return" */ + rtfs_print_prompt_user(UPROMPT_TSTSH6, working_buffer); + goto return_error; + } + + /* Call the low level media format. Do not do this if formatting a + volume that is the second partition on the drive */ + /* "Format: Press Y to format media " */ + rtfs_print_prompt_user(UPROMPT_TSTSH7, buf); + if (tstsh_is_yes(buf)) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_67, PRFLG_NL); /* "Calling media format" */ + if (!pc_format_media(path, &geometry)) + { + DISPLAY_ERRNO("pc_format_media") + /* "Format: Media format failed. Press return" */ + rtfs_print_prompt_user(UPROMPT_TSTSH8, working_buffer); + goto return_error; + } + } + + /* Partition the drive if it needs it */ + if ( pdr->drive_flags&DRIVE_FLAGS_PARTITIONED ) + { + /* "Format: Press Y to partition media " */ + rtfs_print_prompt_user(UPROMPT_TSTSH9, buf); + if (tstsh_is_yes(buf)) + { + if (geometry.dev_geometry_lbas) + { + do + { + /* "Format: Press Y to USE LBA formatting, N to use CHS " */ + rtfs_print_prompt_user(UPROMPT_TSTSH10, buf); + } + while (!tstsh_is_yes(buf) && !tstsh_is_no(buf)); + + if (tstsh_is_no(buf)) + { + geometry.dev_geometry_lbas = 0; + } + } + + if (geometry.dev_geometry_lbas) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_68, 0); /* "The drive is contains this many logical blocks " */ + RTFS_PRINT_LONG_1((dword) geometry.dev_geometry_lbas, PRFLG_NL); + /* "Format: Select the number of lbas for the first partition :" */ + rtfs_print_prompt_user(UPROMPT_TSTSH11, working_buffer); + partition_list[0] = (dword)rtfs_atol(working_buffer); + partition_list[1] = 0; + + if (partition_list[0] != geometry.dev_geometry_lbas) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_69, 0); /* "This many logical blocks remain" */ + RTFS_PRINT_LONG_1((dword) (geometry.dev_geometry_lbas - partition_list[0]), PRFLG_NL); + /* "Format: Select the number of lbas for the second partition :" */ + rtfs_print_prompt_user(UPROMPT_TSTSH12, working_buffer); + partition_list[1] = (dword)rtfs_atol(working_buffer); + partition_list[2] = 0; + } + if ((partition_list[0] == 0) || ((dword)(partition_list[0]+partition_list[1]) > geometry.dev_geometry_lbas)) + { + /* "Format: Bad input for partition values. Press return" */ + rtfs_print_prompt_user(UPROMPT_TSTSH13, working_buffer); + goto return_error; + } + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_70, 0); /* "The drive contains this many cylinders" */ + RTFS_PRINT_LONG_1((dword) geometry.dev_geometry_cylinders, PRFLG_NL); + /* "Format: Select the number of cyls for the first partition :" */ + rtfs_print_prompt_user(UPROMPT_TSTSH14, working_buffer); + partition_list[0] = (word)rtfs_atoi(working_buffer); + partition_list[1] = 0; + + if (partition_list[0] != geometry.dev_geometry_cylinders) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_71, 0); /* "There are this many cylinders remainng" */ + RTFS_PRINT_LONG_1((dword) geometry.dev_geometry_cylinders - partition_list[0], PRFLG_NL); + /* "Format: Select the number of cyls for the second partition :" */ + rtfs_print_prompt_user(UPROMPT_TSTSH15, working_buffer); + partition_list[1] = (dword)rtfs_atol(working_buffer); + partition_list[2] = 0; + } + if ((partition_list[0] == 0) || ((dword)(partition_list[0]+partition_list[1]) > geometry.dev_geometry_cylinders)) + { + /* "Format: Bad input for partition values. Press return" */ + rtfs_print_prompt_user(UPROMPT_TSTSH13, working_buffer); + goto return_error; + } + } + if (!pc_partition_media(path, &geometry, &partition_list[0])) + { + /* "Format: Media partition failed. Press return" */ + rtfs_print_prompt_user(UPROMPT_TSTSH16, working_buffer); + goto return_error; + } + } + } + + /* Put the DOS format */ + /* "Format: Press Y to format the volume " */ + rtfs_print_prompt_user(UPROMPT_TSTSH17, buf); + if (tstsh_is_yes(buf)) + { + if (!pc_format_volume(path, &geometry)) + { + /* "Format: Format volume failed. Press return" */ + rtfs_print_prompt_user(UPROMPT_TSTSH18, working_buffer); + goto return_error; + } + } + OS_RELEASE_LOGDRIVE(driveno) + return (0); +return_error: + OS_RELEASE_LOGDRIVE(driveno) + return(-1); +} + + +/* GETATTR PATH*/ +int dogetattr(int agc, byte **agv) /*__fn__*/ +{ +byte attr; + + if (agc == 1) + { + if (pc_get_attributes(*agv, &attr)) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_72,0); /* "Attributes: " */ + if (attr & ARDONLY) + RTFS_PRINT_STRING_1(USTRING_TSTSH_73,0); /* "ARDONLY|" */ + if (attr & AHIDDEN) + RTFS_PRINT_STRING_1(USTRING_TSTSH_74,0); /* "AHIDDEN|" */ + if (attr & ASYSTEM) + RTFS_PRINT_STRING_1(USTRING_TSTSH_75,0); /* "ASYSTEM|" */ + if (attr & AVOLUME) + RTFS_PRINT_STRING_1(USTRING_TSTSH_76,0); /* "AVOLUME|" */ + if (attr & ADIRENT) + RTFS_PRINT_STRING_1(USTRING_TSTSH_77,0); /* "ADIRENT|" */ + if (attr & ARCHIVE) + RTFS_PRINT_STRING_1(USTRING_TSTSH_78,0); /* "ARCHIVE|" */ + if (attr == ANORMAL) + RTFS_PRINT_STRING_1(USTRING_TSTSH_79,0); /* "NORMAL FILE (No bits set)" */ + RTFS_PRINT_STRING_1(USTRING_SYS_NULL, PRFLG_NL); /* "" */ + return(0); + } + else + { + DISPLAY_ERRNO("pc_get_attributes") + RTFS_PRINT_STRING_1(USTRING_TSTSH_81, PRFLG_NL); /* "get attributes failed" */ + return(0); + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_82, PRFLG_NL); /* "Usage: GETATTR D:PATH" */ + return (0); +} + +/* GETATTR PATH*/ +int dosetattr(int agc, byte **agv) /*__fn__*/ +{ +byte attr; + + attr = 0; + if (agc == 2) + { + if (!pc_get_attributes(*agv, &attr)) + { + DISPLAY_ERRNO("pc_get_attributes") + RTFS_PRINT_STRING_1(USTRING_TSTSH_83, PRFLG_NL); /* "Can not get attributes" */ + return(0); + } + attr &= ~(ARDONLY|AHIDDEN|ASYSTEM|ARCHIVE); + if (rtfs_cs_strcmp(*(agv+1),rtfs_strtab_user_string(USTRING_TSTSH_84))==0) /* "RDONLY" */ + attr |= ARDONLY; + else if (rtfs_cs_strcmp(*(agv+1),rtfs_strtab_user_string(USTRING_TSTSH_85))==0) /* "HIDDEN" */ + attr |= AHIDDEN; + else if (rtfs_cs_strcmp(*(agv+1),rtfs_strtab_user_string(USTRING_TSTSH_86))==0) /* "SYSTEM" */ + attr |= ASYSTEM; + else if (rtfs_cs_strcmp(*(agv+1),rtfs_strtab_user_string(USTRING_TSTSH_87))==0) /* "ARCHIVE" */ + attr |= ARCHIVE; + else if (rtfs_cs_strcmp(*(agv+1),rtfs_strtab_user_string(USTRING_TSTSH_88))==0) /* "NORMAL" */ + attr = ANORMAL; + else + goto usage; + + if (pc_set_attributes(*agv, attr)) + return(0); + else + { + DISPLAY_ERRNO("pc_set_attributes") + RTFS_PRINT_STRING_1(USTRING_TSTSH_89, PRFLG_NL); /* "Set attributes failed" */ + return(0); + } + } +usage: + RTFS_PRINT_STRING_1(USTRING_TSTSH_90, PRFLG_NL); /* "Usage: SETATTR D:PATH RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL" */ + + return (0); +} + +/* DEVINFO */ +int dodevinfo(int agc, byte **agv) /*__fn__*/ +{ + RTFS_ARGSUSED_INT(agc); + RTFS_ARGSUSED_PVOID((void *)agv); + + print_device_names(); + + return (1); +} + + + +#if (INCLUDE_CDROM) + +/* DSKOPEN PATH */ +int cddodskop(int agc, byte **agv) /*__fn__*/ +{ + if (agc == 1) + { + if (cd_dskopen(*agv)) + { + cddoselect(agc, agv); + return(1); + } + else + { + RTFS_PRINT_STRING_2(USTRING_TSTSH_91,*agv, PRFLG_NL); /* "Can not open disk %s\n" */ + return(0); + } + } + RTFS_PRINT_STRING_1(USTRING_TSTSH_92, PRFLG_NL); /* "Usage: DSKOPEN D:" */ + return(0); +} + + +/* DSKCLOSE PATH */ +int cddodskcl(int agc, byte **agv) /*__fn__*/ +{ + if (agc == 1) + { + cd_dskclose(*agv); + } + else + RTFS_PRINT_STRING_1(USTRING_TSTSH_93, PRFLG_NL); /* "Usage: DSKCLOSE D: " */ + return(1); + } + +/* DSKSEL PATH */ +int cddoselect(int agc, byte **agv) /*__fn__*/ +{ + + if (agc == 1) + { + if (cd_set_default_drive(*agv)) + return(1); + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_94, PRFLG_NL); /* "cd_set_default failed" */ + return(0); + } + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_95, PRFLG_NL); /* "Usage: DSKSELECT D: " */ + return(0); + } +} + +/* CD PATH */ +int cddocwd(int agc, byte **agv) /*__fn__*/ +{ +byte lbuf[255]; + if (agc == 1) + { + if (cd_scwd(*agv)) + return(1); + else + return(0); + } + else + { + lbuf[0] = '\0'; /* CDFS */ + if (cd_gcwd(lbuf)) + { + RTFS_PRINT_STRING_2(USTRING_SYS_NULL, lbuf, PRFLG_NL); + return (1); + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_96, PRFLG_NL); /* "path error" */ + return(0); + } + } +} + + +/* DIR PATH */ +int cddols(int agc, byte **agv) /*__fn__*/ +{ + byte *dirstr; + CD_DSTAT dirent; + byte lbuf[255]; + + if (agc==1) + rtfs_cs_strcpy(lbuf, *agv); + else + rtfs_cs_strcpy(lbuf, (byte*)"*.*"); /* CDFS */ + + if (!cd_gfirst(&dirent,lbuf)) + return(0); + do + { + if (dirent.dir.file_flags & FF_DIRECTORY) + dirstr = (byte *)""; + else + dirstr = (byte *)" "; + + if ((dirent.dir.len_fi == 1) && (dirent.dir.file_id[0] == 0)) + { + dirent.dir.file_id[0] = '.'; /* CDFS */ + dirent.dir.file_id[1] = 0; + } + if ((dirent.dir.len_fi == 1) && (dirent.dir.file_id[0] == 1)) + { + dirent.dir.file_id[0] = '.';/* CDFS */ + dirent.dir.file_id[1] = '.';/* CDFS */ + dirent.dir.file_id[2] = 0; + } + + printf( "%-15.15s ",dirent.dir.file_id); /* CDFS */ + printf( "%-5.5s ", dirstr); + printf( "%7ld ", dirent.dir.data_len); + printf( "%02d-", dirent.dir.record_time.month); + printf( "%02d-", dirent.dir.record_time.day); + printf( "%02d ", dirent.dir.record_time.year); + printf( "%02d:", dirent.dir.record_time.hour); + printf( "%02d:", dirent.dir.record_time.min); + printf( "%02d\n", dirent.dir.record_time.sec); /* CDFS */ + + } while (cd_gnext(&dirent,lbuf)); + + cd_gdone(&dirent); + return(1); +} + +/* CAT PATH */ +int cddocat(int agc, byte **agv) /*__fn__*/ +{ + CDFD fd; + int n_read; + byte buff[42]; + BOOLEAN forever = TRUE; /* use this for while(forever) to quiet anal compilers */ + + + if (agc == 1) + { + fd=cd_open(*agv); + + if (fd < 0) + { + RTFS_PRINT_STRING_2(USTRING_TSTSH_97, *agv, PRFLG_NL); /* "Cant open File" */ + return(0); + } + else + { + while (forever) + { + n_read = cd_read(fd, (byte *)buff, 40); + if (n_read > 0) + { + buff[n_read] = '\0'; /* CDFS */ + RTFS_PRINT_STRING_2(USTRING_SYS_NULL, buff, PRFLG_NL); + } + else + break; + } + cd_close(fd); + return(1); + } + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_98, PRFLG_NL); /* "Usage: CAT PATH" */ + return(0); + } +} + +#define ZERO 0 +#if (ZERO) +/* READ PATH */ +int cddoread(int agc, byte **agv) /*__fn__*/ +{ + CDFD fd; + word n_read; + BOOLEAN forever = TRUE; /* use this for while(forever) to quiet anal compilers */ + + + if (agc == 1) + { + fd=cd_open(*agv); + + if (fd < 0) + { + RTFS_PRINT_STRING_2(USTRING_TSTSH_99, *agv, PRFLG_NL); /* "Cant open :" */ + return(0); + } + else + { + while (forever) + { + n_read = cd_read(fd, (byte *)read_buf, (word) 16384); + if ( (n_read != 0) && (n_read <= 16384) ) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_100,0); /* "." */ + } + else + { + RTFS_PRINT_STRING_1(USTRING_SYS_NULL, PRFLG_NL); /* "" */ + break; + } + } + cd_close(fd); + return(1); + } + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_102, PRFLG_NL); /* "Usage: READ PATH" */ + return(0); + } +} + + + +/* COPY CDFILE DOSFILE */ +int cddocopy(int agc, byte **agv) /*__fn__*/ +{ + BOOLEAN is_wild = FALSE; + CD_DSTAT dirent; + byte *p; + byte *p2; + byte *last_s; + byte tofile[255]; + byte from_dir[255]; + byte from_file[255]; + + from_dir[0] = '\0'; /* CDFS */ + + if (agc == 2) + { + + /* See if we have a wild card */ + p = *agv; + while (*p) + {if (*p == '*' || *p == '?'){is_wild = TRUE; break;} p++;}/* CDFS */ + if (is_wild) + { + /* Copy the source path into from dir. Remember the location just + past the last \ character. Later on we cat the file to it to + create the source file name. If there is no '\' this will all + be harmless */ + p = *agv; + p2 = &from_dir[0]; + last_s = &from_dir[0]; + while (*p) + { + *p2++ = *p; + if ((*p == BACKSLASH) || (*p == ':'))/* CDFS */ + last_s = p2; + p++; + } + *last_s = '\0';/* CDFS */ + + if (!cd_gfirst(&dirent,*agv)) + return(FALSE); + do + { + if (!(dirent.dir.file_flags & FF_DIRECTORY)) + { + rtfs_cs_strcpy(tofile, *(agv+1)); + strcat(tofile, "\\"); + strcat(tofile, dirent.dir.file_id); + rtfs_cs_strcpy(from_file, from_dir); + strcat(from_file, dirent.dir.file_id); + if (!copy_one_file(tofile,from_file)) + { + cd_gdone(&dirent); + return(FALSE); + } + } + } while (cd_gnext(&dirent,*agv)); + cd_gdone(&dirent); + } + else + { + if (!copy_one_file(*(agv+1), *agv)) + return(0); + } + } + else + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_103, PRFLG_NL); /* "Usage: COPY CDPATH OSPATH" */ + return(0); + } + return(1); +} + +/* READ PATH */ +BOOLEAN copy_one_file(byte *to, byte *from) /*__fn__*/ +{ + CDFD fd; + int fi; + word n_read; + BOOLEAN forever = TRUE; /* use this for while(forever) to quiet anal compilers */ + int do_dot; + + fd=cd_open(from); + if (fd < 0) + { + RTFS_PRINT_STRING_2(USTRING_TSTSH_104, from, PRFLG_NL); /* "Cant open " */ + return(FALSE); + } + if ( (fi = open(to,O_WRONLY|O_BINARY|O_CREAT|O_TRUNC, S_IREAD | S_IWRITE)) < 0) /* msc open */ + { + RTFS_PRINT_STRING_2(USTRING_TSTSH_105,to, PRFLG_NL); /* "Cant creat " */ + cd_close(fd); + return(FALSE); + } + RTFS_PRINT_STRING_1(from, PRFLG_NL); + do_dot = 2; + while (forever) + { + + cd_read(fd, (byte *)read_buf, (word)32768L); + if((n_read != 0xffff) && (n_read > 0)) + { + if (!--do_dot) + { + RTFS_PRINT_STRING_1(USTRING_TSTSH_106,0); /* "." */ + do_dot = 2; + } + if ( (unsigned)write (fi,read_buf,n_read) != n_read) + { + RTFS_PRINT_STRING_1(USTRING_SYS_NULL, PRFLG_NL); /* "" */ + RTFS_PRINT_STRING_1(USTRING_TSTSH_108, PRFLG_NL); /* "Write error" */ + close(fi); + cd_close(fd); + return (FALSE); + } + } + else + { + close(fi); + cd_close(fd); + RTFS_PRINT_STRING_1(USTRING_SYS_NULL, PRFLG_NL); /* "" */ + break; + } + } + return(TRUE); +} + +#endif /* ZERO */ +#endif /* CDFS */ + + +#if (INCLUDE_STRESS_TESTS) + + +byte filename[1000][30]; +byte PattBuff[4096]; +struct stat st[1000]; +int rand(); + + +void stat_1000( void) +{ + unsigned long i, ret; + + int fd; + + pc_mkdir((byte *)"\\subdir"); + for(i=0;i<1000;i++) + sprintf(filename[i], "\\subdir\\file%04d.txt", i); + for(i=0;i<1000;i++) + { + sprintf(PattBuff, "%04d", i); + printf("Creating %s\n", filename[i]); + if ((fd = po_open(filename[i],(word)(PO_BINARY|PO_WRONLY|PO_CREAT),(word)(PS_IWRITE | PS_IREAD))) >= 0) + { + + ret = po_write(fd,(byte*)PattBuff,1024); + + po_close(fd); + } + else + { + printf("Halt:Open failed %s\n", filename[i]); + for(;;); + } + printf("\n"); + } + + for(i=0;i<1000;i++){ + printf("Statting %s\n", filename[i]); + if (pc_stat(filename[i], &st[i])!=0){ + printf("Halt: Stat failed %s\n", filename[i]); + for(;;); /* Error */ + } + printf("\n"); + } + +} + +void write_big_file() +{ + unsigned long i, j, k, ret; + dword *dw; + int fd; + + sprintf((char *)filename[0], "\\subdir\\bigfile.txt"); + + printf("Creating %s\n", filename[0]); + if ((fd = po_open(filename[0],(word)(PO_BINARY|PO_WRONLY|PO_CREAT|PO_TRUNC),(word)(PS_IWRITE | PS_IREAD))) >= 0) + { + for(i=0;i<2000;i++) + { + if ((i & 64) == 0) + printf("Writing i == %ld\n",i); + dw = (dword *) PattBuff; + for (j = 0; j < 1024; j++) + *dw++ = i*1024 + j; + ret = po_write(fd,(byte*)PattBuff,4096); + } + po_close(fd); + } + printf("reading %s\n", filename[0]); + if ((fd = po_open(filename[0],(word)(PO_BINARY|PO_RDONLY),(word)(PS_IWRITE | PS_IREAD))) >= 0) + { + for(i=0;i<2000;i++) + { + if ((i & 64) == 0) + printf("Reading i == %ld\n",i); + ret = po_read(fd,(byte*)PattBuff,4096); + if (ret != 4096) + printf("Read error at i == %ld\n",i); + else + { + dw = (dword *) PattBuff; + for (j = 0; j < 1024; j++) + if (*dw++ != i*1024 + j) + printf("Mattch error j== i == %ld %ld\n", j, i); + } + } + printf("seek and reading %s\n", filename[i]); + for(k=0;k<2000;k++) + { + i = (((dword) rand()) % 2000); + + if ((k & 8) == 0) + printf("Seeking i == %ld\n",i); + po_lseek(fd, i*4096, PSEEK_SET); + ret = po_read(fd,(byte*)PattBuff,4096); + if (ret != 4096) + printf("Read error at i == %ld\n",i); + else + { + dw = (dword *) PattBuff; + for (j = 0; j < 1024; j++) + if (*dw++ != i*1024 + j) + printf("Mattch error j== i == %ld %ld\n", j, i); + } + } + } + po_close(fd); +} +#define BIGBUFFINTS 10000 /* have tried 8000000 eight million successfully in hostdisk mode. */ +int big_buffer[BIGBUFFINTS]; + +int myrand() +{ +int l; + l = rand(); l*=rand(); + return(l); +} +int test_long_write(int fd) +{ +int i, j, ret, pos, nleft, nleft1, nleft2; + + for (j = 0; j < 10; j++) + { + pos = myrand()%BIGBUFFINTS; + nleft2 = BIGBUFFINTS - pos; + nleft1 = myrand()%nleft2; + printf("Write Testing %d of 10000 POS %d, NLEFT %d NLEFT2 %d\n",j, pos, nleft1, nleft2); + nleft = nleft1; +again: + for(i=0;i= 0) + { + for(i=0;i= 0) + { + po_close(fd1); + for (i = 0; i < 1000; i++) + po_open((byte *)"FILENAME",(word)(PO_BINARY|PO_WRONLY|PO_CREAT|PO_EXCL),(word)(PS_IWRITE | PS_IREAD)); + + } + for (i = 0; i < 1000; i++) + pc_rmdir((byte *)"SUB_DIR"); + for (i = 0; i < 1000; i++) + pc_unlink((byte *)"FILENAME"); + for (i = 0; i < 1000; i++) + pc_mv((byte *)"NOTTHERE", (byte *)"STILLNOT"); +} + + +#endif diff --git a/build/libraries/fatfs/ARM7/appdemo.c b/build/libraries/fatfs/ARM7/appdemo.c new file mode 100644 index 0000000..5e4d9a6 --- /dev/null +++ b/build/libraries/fatfs/ARM7/appdemo.c @@ -0,0 +1,26 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright Peter Van Oudenaren , 1993 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* appdemo.c - Top level demo code. Initializes ertfs and calls the test shell. +*/ +#include + +void tst_shell(void); + +void rtfs_app_entry(void) /* __fn__ */ +{ + + /* Initialize ertfs */ + if (!pc_ertfs_init()) + { + RTFS_PRINT_STRING_1(USTRING_RTFSDEM_01, PRFLG_NL); /* "pc_ertfs_init failed" */ + return; + } + tst_shell(); +} + diff --git a/build/libraries/fatfs/ARM7/attach.c b/build/libraries/fatfs/ARM7/attach.c new file mode 100644 index 0000000..43348fa --- /dev/null +++ b/build/libraries/fatfs/ARM7/attach.c @@ -0,0 +1,274 @@ +/*---------------------------------------------------------------------------* + Project: CTR - for RTFS + File: attach.c + + 2006 Nintendo. + *---------------------------------------------------------------------------*/ + +#include +#include +#include /* For included devices */ + +#include "drdefault.h" /* default driver */ + + +/*---------------------------------------------------------------------------* + global変数 + *---------------------------------------------------------------------------*/ +BOOLEAN rtfs_first_attach = TRUE; //attach APIがまだ未使用ならTRUE + +//attachされてからDEVCTL_CHECKSTATUSが呼ばれてなければ1 +int rtfs_first_stat_flag[26] ={ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + + +/*---------------------------------------------------------------------------* + extern変数 + *---------------------------------------------------------------------------*/ +extern int enabled_drivers; +extern DDRIVE* current_pdr; +extern int auto_format_disk(DDRIVE *pdr, byte *drivename); +extern void drno_to_string(byte *pname, int drno); +extern DDRIVE* pc_drno_to_drive_struct(int driveno); +extern void print_device_names(void); + + +/*---------------------------------------------------------------------------* + extern関数 + *---------------------------------------------------------------------------*/ +/*-------------- ctr modified(new function) -------------*/ +//extern BOOLEAN i_rtfs_begin_attach( void); +extern BOOLEAN i_rtfs_attach( int driveno, DDRIVE* pdr, char* dev_name); +extern BOOLEAN i_rtfs_end_attach( void); +/*-------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------* + static関数 + *---------------------------------------------------------------------------*/ +static void i_rtfsInit( DDRIVE* pdr); +static int i_rtfs_close_disk( int driveno); + + + +/*---------------------------------------------------------------------------* + Name: rtfs_attach + + Description: + + Arguments: driveno : ドライブ番号 + + Returns: + *---------------------------------------------------------------------------*/ +BOOLEAN rtfs_attach( int driveno, DDRIVE* pdr, char* dev_name) +{ + DDRIVE *target_pdr; + +// osTPrintf( "original attach\n"); + + if( rtfs_first_attach) { //最初のattachだった場合 + rtfs_init();//i_rtfs_begin_attach(); + rtfs_first_attach = FALSE; + } + + /*アタッチする構造体を選択*/ + target_pdr = pc_drno_to_drive_struct( driveno); + if( target_pdr == 0) { /*未登録なら新しい構造体へ*/ +// osTPrintf( "target_pdr = current_pdr\n"); + target_pdr = current_pdr; + }else{ +/* if( target_pdr->dev_table_drive_io == pdr->dev_table_drive_io) { + return( TRUE); //自分自身ならスルー + }*/ + if(( target_pdr->dev_table_drive_io != defaultRtfsIo)&& + ( target_pdr->dev_table_drive_io != NULL)){ + return( FALSE); /*default以外のドライバがattach済みならエラー*/ + } + } + + if (++enabled_drivers > prtfs_cfg->cfg_NDRIVES) { +// osTPrintf( "%s : too many drives attached - "); +// osTPrintf( "%d / %d\n", enabled_drivers, prtfs_cfg->cfg_NDRIVES); + return( FALSE); + } + + target_pdr->driveno = driveno; + target_pdr->dev_table_drive_io = pdr->dev_table_drive_io; + target_pdr->dev_table_perform_device_ioctl = pdr->dev_table_perform_device_ioctl; + rtfs_strcpy(target_pdr->device_name, (byte *)dev_name); + target_pdr->register_file_address = pdr->register_file_address; + target_pdr->interrupt_number = pdr->interrupt_number; + target_pdr->drive_flags = pdr->drive_flags | DRIVE_FLAGS_REMOVABLE | DRIVE_FLAGS_INSERTED; + target_pdr->partition_number = pdr->partition_number; + target_pdr->pcmcia_slot_number = pdr->pcmcia_slot_number; + target_pdr->controller_number = pdr->controller_number; + target_pdr->logical_unit_number = pdr->logical_unit_number; + + + rtfs_first_stat_flag[driveno] = 1; /* 初回DEVCTL_CHECKSTATUSフラグON */ + + i_rtfsInit( target_pdr); + + /**/ + if( target_pdr == current_pdr) { + current_pdr++; + } + + return( TRUE); +} + + +/**/ +static void i_rtfsInit( DDRIVE* pdr) +{ + byte drname[8]; /* Temp buffer for displaying drive letters as strings */ + + + /* WARMSTART */ + if( pdr->dev_table_drive_io) { + prtfs_cfg->drno_to_dr_map[pdr->driveno] = pdr; /* MAPS DRIVE structure to DRIVE: */ + if (pdr->dev_table_perform_device_ioctl(pdr->driveno, DEVCTL_WARMSTART, (void *) 0) != 0) + { + prtfs_cfg->drno_to_dr_map[pdr->driveno] = 0; /* It is not there. */ + /* so forget it */ + } + +#if (INCLUDE_FAILSAFE_CODE) + /* Call the the ertfs pro failsafe autoinit function */ + if (pdr->drive_flags&DRIVE_FLAGS_FAILSAFE) + { + if (!pro_failsafe_auto_init(pdr)) //マニュアル参照 + { + prtfs_cfg->drno_to_dr_map[pdr->driveno] = 0; /* Forget it, not there. */ + } + } +#endif + + /* Set the default drive to the lowest assigned drive letter */ + prtfs_cfg->default_drive_id = pdr->driveno; + } + + + /* autoformatting ram devices */ + if ( pdr->drive_flags&DRIVE_FLAGS_VALID && pdr->drive_flags&DRIVE_FLAGS_FORMAT) + { + OS_CLAIM_LOGDRIVE(pdr->driveno) /* Autoformat Register drive in use */ + drno_to_string(drname, pdr->driveno); +// RTFS_PRINT_STRING_2(USTRING_RTFSINIT_07, drname,0); /* "Autoformatting Drive Id - " */ +#if (STORE_DEVICE_NAMES_IN_DRIVE_STRUCT) +// RTFS_PRINT_STRING_2(USTRING_RTFSINIT_08, pdr->device_name,PRFLG_NL); /* " as Device: " */ +#endif + if (auto_format_disk(pdr, drname) != 0) { +// RTFS_PRINT_STRING_2(USTRING_RTFSINIT_09, drname,PRFLG_NL); /* "Autoformatting Drive Id - \n" */ + } + OS_RELEASE_LOGDRIVE(pdr->driveno) /* Autoformat release */ + } +} + + +/*---------------------------------------------------------------------------* + Name: rtfs_detach + + Description: + + Arguments: driveno : ドライブ番号 + + Returns: + *---------------------------------------------------------------------------*/ +BOOLEAN rtfs_detach( int driveno) +{ + DDRIVE *target_pdr; + +// osTPrintf( "original detach\n"); + + target_pdr = pc_drno_to_drive_struct( driveno); + if( target_pdr == 0) { + return( FALSE); //未割り当てなのでdetachの必要なし + }else{ + if( (target_pdr->dev_table_drive_io == defaultRtfsIo) || + (target_pdr->dev_table_drive_io == NULL)) { + return( FALSE); //既にdetach済み + } + } + + /*open中のファイルを全てcloseする*/ + i_rtfs_close_disk( driveno); + + --enabled_drivers; + + target_pdr->driveno = driveno; + target_pdr->dev_table_drive_io = defaultRtfsIo; + target_pdr->dev_table_perform_device_ioctl = defaultRtfsCtrl; + rtfs_strcpy(target_pdr->device_name, (byte *)"DEFAULT\0"); //STORE_DEVICE_NAME(dev_name) + target_pdr->register_file_address = 0; + target_pdr->interrupt_number = 0; + target_pdr->drive_flags = 0; + target_pdr->partition_number = 0; + target_pdr->pcmcia_slot_number = 0; + target_pdr->controller_number = 0; + target_pdr->logical_unit_number = 0; + + /*REMOVE扱い*/ + target_pdr->drive_flags |= DRIVE_FLAGS_REMOVABLE; + target_pdr->drive_flags &= ~DRIVE_FLAGS_INSERTED; + + return( TRUE); +} + + +/*---------------------------------------------------------------------------* + Name: i_rtfs_close_disk + + Description: 指定ドライブでopen中のファイルを全てcloseする + + Arguments: driveno : ドライブ番号 + + Returns: + *---------------------------------------------------------------------------*/ +static int i_rtfs_close_disk( int driveno) +{ + PC_FILE *pfile; + PCFD i; + DROBJ *pobj; + int ret_val = 0; + + rtfs_set_errno(0); /* clear errno */ + + pfile = prtfs_cfg->mem_file_pool; + for (i=0;iis_free) == FALSE) //open中か? + { + if( !pfile->pobj) {//PECLOSEDエラーに相当(pc_fd2file参照) + OS_CLAIM_FSCRITICAL() + pfile->is_free = TRUE; + OS_RELEASE_FSCRITICAL() + }else{ //正常にopen中 + if( (pfile->pobj->pdrive->driveno) == driveno) { +#if (RTFS_WRITE) + if (pfile->flag & ( PO_RDWR | PO_WRONLY ) ) + { + if (!_po_flush(pfile)) + { /* _po_flush has set errno */ + ret_val = -1; + } + } +#endif + /* Release the FD and its core */ + //pc_freefile( pfile); apifilio.c内のstatic関数なので以下に展開 + OS_CLAIM_FSCRITICAL() + pobj = pfile->pobj; + pfile->is_free = TRUE; + OS_RELEASE_FSCRITICAL() + pc_freeobj(pobj); + } + } + } + } + if (!release_drive_mount_write(driveno)) {/* Release lock, unmount if aborted */ + return (-1); + } + return( ret_val); +} diff --git a/build/libraries/fatfs/ARM7/csascii.c b/build/libraries/fatfs/ARM7/csascii.c new file mode 100644 index 0000000..cf3c8fa --- /dev/null +++ b/build/libraries/fatfs/ARM7/csascii.c @@ -0,0 +1,525 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 2002 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* UTILASCI.C - Contains ASCII string manipulation and character +* conversion routines +*/ + +#include + +#if (INCLUDE_CS_ASCII) + +byte *ascii_goto_eos(byte *p) +{ + while (*p) p++; + return(p); +} + +void pc_byte2upper(byte *to, byte *from) +{ +byte c; + c = *from; + if ((c >= 'a') && (c <= 'z')) + c = (byte) ('A' + c - 'a'); + *to = c; +} + +void pc_ascii_strn2upper(byte *to, byte *from, int n) +{ + int i; + for (i = 0; i < n; i++,to++, from++) + pc_byte2upper(to, from); +} + +void pc_ascii_str2upper(byte *to, byte *from) +{ + while(*from) + pc_byte2upper(to++, from++); + *to = '\0'; +} + +void pc_str2upper(byte *to, byte *from) +{ + pc_ascii_str2upper(to, from); +} + +/*************************************************************************** + PC_MFILE - Build a file spec (xxx.yyy) from a file name and extension + + Description + Fill in to with a concatenation of file and ext. File and ext are + not assumed to be null terminated but must be blank filled to [8,3] + chars respectively. 'to' will be a null terminated string file.ext. + + ASCII character function only. Unicode not required + + Returns + A pointer to 'to'. + +****************************************************************************/ +byte *pc_ascii_mfile(byte *to, byte *filename, byte *ext) +{ + byte *p; + int i; + byte *retval = to; + + p = filename; + i = 0; + while(*p) + { + if (*p == ' ') + break; + else + { + *to++ = *p++; + i++; + } + if (i == 8) + break; + } + if (p != filename) + { + p = ext; + if (*p && *p != ' ') + *to++ = '.'; + i = 0; + while(p && *p) + { + if (*p == ' ') + break; + else + { + *to++ = *p++; + i++; + } + if (i == 3) + break; + } + } + *to = '\0'; + return (retval); +} +/* Version of MFILE that converts path from byte orientation to + native char set before returning. pc_mfile and pc_cs_mfile are + the same for ascii */ +byte *pc_cs_mfile(byte *to, byte *filename, byte *ext) +{ + return(pc_ascii_mfile(to, filename, ext)); +} + +byte * rtfs_cs_strcat(byte * targ, byte * src) /*__fn__*/ +{ + return(rtfs_strcat(targ, src)); +} + +int rtfs_cs_strcmp(byte * s1, byte * s2) +{ + return (rtfs_strcmp(s1, s2)); +} + +int rtfs_cs_strcpy(byte * targ, byte * src) +{ + return (rtfs_strcpy(targ, src)); +} + +/* return number of ascii chars in a string */ +int rtfs_cs_strlen(byte * string) /*__fn__*/ +{ + return (rtfs_strlen(string)); +} +/* return number of bytes in an ascii character string */ +int rtfs_cs_strlen_bytes(byte * string) /*__fn__*/ +{ + return (rtfs_strlen(string)); +} + +int ascii_ascii_index(byte *p, byte base) +{ +byte c; + pc_byte2upper(&c, p); + return((int) (c - base)); +} + +int ascii_compare_nc(byte *p1, byte *p2) +{ +byte c,d; + if (*p1 == *p2) + return(1); + else + { + pc_byte2upper(&c, p1); + pc_byte2upper(&d, p2); + return(c == d); + } +} + +/*************************************************************************** +PC_FILEPARSE - Parse a file xxx.yyy into filename/pathname + + Description + Take a file named XXX.YY and return SPACE padded NULL terminated + filename [XXX ] and fileext [YY ] components. If the name or ext are + less than [8,3] characters the name/ext is space filled and null termed. + If the name/ext is greater than [8,3] the name/ext is truncated. '.' + is used to seperate file from ext, the special cases of . and .. are + also handled. + Returns + Returns TRUE + + ****************************************************************************/ + /* UNICODE - Called by pc_malias not by others if vfat - Okay as ascii */ + /* UNICODE - pc_enum usage is probably incorrect */ + /* Take a string xxx[.yy] and put it into filename and fileext */ + /* Note: add a check legal later */ +BOOLEAN pc_ascii_fileparse(byte *filename, byte *fileext, byte *p) /* __fn__*/ +{ + int i; + + /* Defaults */ + rtfs_memset(filename, ' ', 8); + filename[8] = '\0'; + rtfs_memset(fileext, ' ', 3); + fileext[3] = '\0'; + + /* Special cases of . and .. */ + if (*p == '.') + { + *filename = '.'; + if (*(p+1) == '.') + { + *(++filename) = '.'; + return (TRUE); + } + else if (*(p + 1) == '\0') + return (TRUE); + else + return (FALSE); + } + + i = 0; + while (*p) + { + if (*p == '.') + { + p++; + break; + } + else + if (i++ < 8) + *filename++ = *p; + p++; + } + + i = 0; + while (*p) + { + if (i++ < 3) + *fileext++ = *p; + p++; + } + return (TRUE); +} + +/*************************************************************************** + PC_VALID_SFN - See if filename is a valid short file name + +Description + Determines validity of a short file name based on the following criteria: + - the file name must be between 0 and 8 characters + - the file extension must be between 0 and 3 characters + - the file name must not begin with a period + - it must not be a reserved DOS file name + - it must not contain any characters that are illegal within sfn's +Returns + TRUE if filename is a valid sfn, FALSE otherwise +****************************************************************************/ + +BOOLEAN pc_valid_sfn(byte *filename) +{ + int len,period_count,ext_start; + byte name_part[9]; + + BOOLEAN badchar; + if(name_is_reserved(filename)) return(FALSE); + name_part[0] = 0; + for(len=0,badchar=FALSE,period_count=0,ext_start=0; filename[len]!=0; len++) + { + if(_illegal_alias_char(filename[len])) badchar = TRUE; + if(filename[len] == '.') + { + ext_start = len+1; + period_count++; + } + else + { + if (!ext_start && len < 8) + { + name_part[len] = filename[len]; + name_part[len+1] = 0; + } + } + } + + /* check if the file name part contains a reserved name */ + if(name_is_reserved(name_part)) return(FALSE); + + if( (filename[0] == ' ') || /* 1st char is a space */ + (len == 0) || /* no name */ + badchar || /* contains illegal chars */ + (period_count > 1) || /* contains more than one extension */ + ((len-ext_start)>3 && period_count>0) || /* extension is longer than 3 chars */ + (period_count==0 && len > 8) || /* name is longer than 8 chars */ + (ext_start > 9) || /* name is longer than 8 chars */ + (ext_start==1) ) return(FALSE); /* no name; 1st char is a period */ + + return(TRUE); +} + +#if (VFAT) /* Small piece of compile time VFAT vs's NONVFAT code */ + +/*************************************************************************** + PC_VALID_LFN - See if filename is a valid long file name + +Description + Determines validity of a long file name based on the following criteria: + - the file must be between 0 and 256 characters in length + - it must not be a reserved DOS file name + - it must not contain any characters that are illegal within lfn's +Returns + TRUE if filename is a valid lfn, FALSE otherwise +*****************************************************************************/ + +BOOLEAN validate_filename(byte * filename, byte * ext) +{ + int len,n; + byte name_part[9]; + + RTFS_ARGSUSED_PVOID((void *) ext); + + name_part[0] = 0; + for(n=0,len=0; filename[n]!=0; len++,n++) + { + if(_illegal_lfn_char(filename[n])) + return(FALSE); + else + { + if (len < 5) /* handles lpt1, aux, con etc */ + { + if(filename[len] == '.') + { + name_part[len] = 0; + if(name_is_reserved(name_part)) return(FALSE); + } + else + { + name_part[len] = filename[len]; + if (filename[len+1] == 0) + { + name_part[len+1] = 0; + if(name_is_reserved(name_part)) return(FALSE); + } + } + } + } + } + if( (len == 0) || (len > 255) ) return(FALSE); + + return(TRUE); +} + + +BOOLEAN pc_cs_malias(byte *alias, byte *input_file, int try) +{ + int n,i,s; + byte filename[9],fileext[4]; + + /* Check if invalid short file name.Case is ignored. If ignored we fix it later ! */ + if ((try == -1) && !pc_valid_sfn((byte *)input_file)) + return(FALSE); + + /* Process the ASCII alias name */ + while(*input_file=='.' || *input_file==' ') input_file++; + + /* find extension start */ + for(n=0,i=0; input_file[n]!=0; n++) /* i holds the position right */ + { /* after the last period */ + if(input_file[n]=='.') i=n+1; + } + + if(i>0 && input_file[i]!=0){ + /* copy extension to fileext[] */ + for(n=i,s=0; input_file[n]!=0 && s<3; n++) + { + if(input_file[n]!=' ') + { + if(_illegal_alias_char(input_file[n])) + { + fileext[s++] = '_'; + } + else + fileext[s++] = input_file[n]; + } + } + fileext[s]=0;} else { i=512; fileext[0]=0; } /* null terminate */ + + /* copy file name to filename[], filtering out spaces, periods and + replacing characters illegal in alias names with '_' */ + for(n=0,s=0; n127) + { + filename[s++] = '_'; + } + else + filename[s++] = input_file[n]; + } + } + for(;s<8;s++) /* pad with spaces */ + { + filename[s] = ' '; + } + filename[8]=0; /* null terminate filename[] */ + + pc_ascii_str2upper(filename,filename); + pc_ascii_str2upper(fileext,fileext); + + if (try != -1) + { + /* append (TEXT[])i to filename[] */ + for(n=7,s=try; s>0 && n>0; s/=10,n--) + { + filename[n] = (byte)(((byte)s%10)+'0'); + } + if(n==0 && s>0) + return(FALSE); + else + filename[n]='~'; + } + /* copy filename[] to alias[], filtering out spaces */ + for(n=0,s=0; s<8; s++) + { + if(filename[s]!=' ') + alias[n++]=filename[s]; + } + if(fileext[0] != 0) + { + alias[n++]='.'; /* insert separating period */ + /* copy fileext[] to alias[] */ + for(s=0; fileext[s]!=0; s++,n++) + { + alias[n]=fileext[s]; + } + } + alias[n]=0; /* null terminate alias[] */ + + return(TRUE); +} +#endif + +#if (!VFAT) /* Small piece of compile time VFAT vs's NONVFAT code */ + + + + +BOOLEAN validate_8_3_name(byte * name,int len) /*__fn__*/ +{ + int i; + int last; + + last = len-1; + for (i = 0; i < len; i++) + { + /* If we hit a space make sure the rest of the string is spaces */ + if (name[i] == ' ') + { + for (; i < len; i++) + { + if (name[i] != ' ') + return(FALSE); + } + break; + } + } + return(TRUE); +} + +BOOLEAN validate_filename(byte * name, byte * ext) +{ +byte sfn_buffer[14]; /* Only uses 13 but keep declarations even */ + if (!( validate_8_3_name(name,8) && validate_8_3_name(ext,3)) ) + return(FALSE); + pc_ascii_mfile(sfn_buffer, name, ext); + return (pc_valid_sfn(sfn_buffer)); +} + +BOOLEAN pc_patcmp_8(byte *p, byte *pattern, BOOLEAN dowildcard) /* __fn__*/ +{ +int size = 8; + /* Kludge. never match a deleted file */ + if (*p == PCDELETE) + return (FALSE); + else if (*pattern == PCDELETE) /* But E5 in the Pattern matches 0x5 */ + { + if (*p == 0x5) + { + size -= 1; + p++; + pattern++; + } + else + return (FALSE); + } + + while (size--) + { + if(dowildcard) + { + if (*pattern == '*') /* '*' matches the rest of the name */ + return (TRUE); + if (*pattern != '?' && !ascii_compare_nc(pattern,p)) + return (FALSE); + } + else + { + if (!ascii_compare_nc(pattern,p)) + return (FALSE); + } + p++; + pattern++; + } + return (TRUE); +} + +BOOLEAN pc_patcmp_3(byte *p, byte *pattern, BOOLEAN dowildcard) /* __fn__*/ +{ +int size = 3; + + while (size--) + { + if(dowildcard) + { + if (*pattern == '*') /* '*' matches the rest of the name */ + return (TRUE); + if (*pattern != '?' && !ascii_compare_nc(pattern,p)) + return (FALSE); + } + else + { + if (!ascii_compare_nc(pattern,p)) + return (FALSE); + } + p++; + pattern++; + } + return (TRUE); +} +#endif /* VFAT */ + +#endif + diff --git a/build/libraries/fatfs/ARM7/csjis.c b/build/libraries/fatfs/ARM7/csjis.c new file mode 100644 index 0000000..35a7073 --- /dev/null +++ b/build/libraries/fatfs/ARM7/csjis.c @@ -0,0 +1,910 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 2002 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* JIS.C - Contains Japanese string manipulation and character conversion routines */ +/* See also jistab.c */ + + +#include + +#if (INCLUDE_CS_JIS) + +byte *jis_goto_eos(byte *p) +{ + while (*p) + p = jis_increment(p); + return(p); +} + +/* Return the length of a JIS character, 1 or 2 */ +int jis_char_length(byte *p) +{ + if ((*p >= 0x81 && *p <= 0x9f) || (*p >= 0xe0 && *p <= 0xfc)) + return(2); + else + return(1); +} + +/* Copy JIS character, 1 or 2 bytes */ +int jis_char_copy(byte *to, byte *from) +{ +int len; + len = jis_char_length(from); + *to++ = *from++; + if (len == 2) + *to++ = *from++; + return(len); +} + +/* Advance a pointer to the next JIS character in a string */ +byte *jis_increment(byte *p) +{ + return(p + jis_char_length(p)); +} + +/* return number of jis chars in a string */ +/* Return 1 if p1 and p2 are the same */ +int jis_compare(byte *p1, byte *p2) +{ + /* If 1st char same and (len is 1 or 2nd char the same */ + return ( (*p1 == *p2) && (jis_char_length(p1)==1 || (*(p1+1) == *(p2+1))) ); +} + +int jis_ascii_index(byte *p, byte base) +{ +byte c; + pc_byte2upper(&c, p); + return((int) (c - base)); +} + +int jis_compare_nc(byte *p1, byte *p2) +{ +byte c,d; + if (jis_compare(p1, p2)) + return(1); + else if (jis_char_length(p1)==1 && jis_char_length(p2)==1) + { + pc_byte2upper(&c, p1); + pc_byte2upper(&d, p2); + return(c == d); + } + else + return(0); +} + + +void pc_ascii_strn2upper(byte *to, byte *from, int n) /* __fn__*/ +{ + int i; + byte c; + for (i = 0; i < n; i++) + { + if (jis_char_length(from) == 2) + { + *to++ = *from++; + *to++ = *from++; + } + else + { + c = *from++; + if ((c >= 'a') && (c <= 'z')) + c = (byte) ('A' + c - 'a'); + *to++ = c; + } + } +} + +void pc_byte2upper(byte *to, byte *from) /* __fn__*/ +{ +byte c; + c = *from; + if ((c >= 'a') && (c <= 'z')) + c = (byte) ('A' + c - 'a'); + *to = c; +} + + +void pc_ascii_str2upper(byte *to, byte *from) /* __fn__*/ +{ + byte c; + while(*from) + { + if (jis_char_length(from) == 2) + { + *to++ = *from++; + *to++ = *from++; + } + else + { + c = *from++; + if ((c >= 'a') && (c <= 'z')) + c = (byte) ('A' + c - 'a'); + *to++ = c; + } + } + *to = '\0'; +} +void pc_str2upper(byte *to, byte *from) /* __fn__*/ +{ + pc_ascii_str2upper(to, from); +} + +/* compares 2 strings; returns 0 if they match */ +int rtfs_cs_strcmp(byte * s1, byte * s2) /*__fn__*/ +{ + while (*s1 && *s2) + { + if (!jis_compare(s1, s2)) + return(1); + s1 = jis_increment(s1); + s2 = jis_increment(s2); + } + if (!*s1 && !*s2) + return(0); + else + return(1); +} +/* This works because JIS has no ZERO in hi byte of 16 bit jis chars */ +byte * rtfs_cs_strcat(byte * targ, byte * src) /*__fn__*/ +{ + /* Call the byte oriented function */ + return(rtfs_strcat(targ, src)); +} + +/* This works because JIS has no ZERO in hi byte of 16 bit jis chars */ +int rtfs_cs_strcpy(byte * targ, byte * src) +{ + /* Call the byte oriented function */ + return (rtfs_strcpy(targ, src)); +} + +/* return number of jis chars in a string */ +int rtfs_cs_strlen(byte * string) /*__fn__*/ +{ +int len=0; + while (*string) + { + string = jis_increment(string); + len++; + } + return len; +} +/* return number of bytes in a jis character string */ +int rtfs_cs_strlen_bytes(byte * string) /*__fn__*/ +{ +byte *s; +int len; + s = string; + while (*string) + { + string = jis_increment(string); + } + len = (int) (s-string); + return len; +} + +/*************************************************************************** + PC_MFILE - Build a file spec (xxx.yyy) from a file name and extension + + Description + Fill in to with a concatenation of file and ext. File and ext are + not assumed to be null terminated but must be blank filled to [8,3] + chars respectively. 'to' will be a null terminated string file.ext. + + ASCII character function only. Unicode not required + + Returns + A pointer to 'to'. + +****************************************************************************/ +byte *pc_ascii_mfile(byte *to, byte *filename, byte *ext) /* __fn__*/ +{ + byte *p; + int i,l; + byte *retval = to; + + p = filename; + i = 0; + while(*p) + { + l = jis_char_length(p); + + if (*p == ' ') + break; + else + { + if (l == 2) + { + if (i>6) + break; + *to++ = *p++; + } + *to++ = *p++; + i += l; + } + if (i == 8) + break; + } + if (p != filename) + { + p = ext; + if (*p && *p != ' ') + *to++ = '.'; + i = 0; + while(p && *p) + { + l = jis_char_length(p); + if (*p == ' ') + break; + else + { + if (l == 2) + { + if (i>1) + break; + *to++ = *p++; + } + *to++ = *p++; + i += l; + } + if (i == 3) + break; + } + } + *to = '\0'; + return (retval); +} +/* Version of MFILE that converts path from byte orientation to + native char set before returning. pc_mfile and pc_cs_mfile are + the same for jis */ +byte *pc_cs_mfile(byte *to, byte *filename, byte *ext) +{ + return(pc_ascii_mfile(to, filename, ext)); +} + +/*************************************************************************** +PC_FILEPARSE - Parse a file xxx.yyy into filename/pathname + + Description + Take a file named XXX.YY and return SPACE padded NULL terminated + filename [XXX ] and fileext [YY ] components. If the name or ext are + less than [8,3] characters the name/ext is space filled and null termed. + If the name/ext is greater than [8,3] the name/ext is truncated. '.' + is used to seperate file from ext, the special cases of . and .. are + also handled. + Returns + Returns TRUE + + ****************************************************************************/ + /* Take a string xxx[.yy] and put it into filename and fileext */ + /* Note: add a check legal later */ +BOOLEAN pc_ascii_fileparse(byte *filename, byte *fileext, byte *p) /* __fn__*/ +{ + int i; + int l; + + /* Defaults */ + rtfs_memset(filename, ' ', 8); + filename[8] = '\0'; + rtfs_memset(fileext, ' ', 3); + fileext[3] = '\0'; + + /* Special cases of . and .. */ + if (*p == '.') + { + *filename = '.'; + if (*(p+1) == '.') + { + *(++filename) = '.'; + return (TRUE); + } + else if (*(p + 1) == '\0') + return (TRUE); + else + return (FALSE); + } + + i = 0; + while (*p) + { + l = jis_char_length(p); + if (*p == '.') + { + p++; + break; + } + else + { + if (i + l <= 8) + { + jis_char_copy(filename, p); + filename += l; + i += l; + } + p += l; + } + } + + i = 0; + while (*p) + { + l = jis_char_length(p); + if (i + l <= 3) + { + jis_char_copy(fileext, p); + fileext += l; + i += l; + } + p += l; + } + return (TRUE); +} + + + +/* Return TRUE if illegal for an 8 dot 3 file */ +static BOOLEAN _illegal_jis_file_char(byte *p, int islfn) +{ +byte c; + if (jis_char_length(p) == 1) + { + c = *p; + /* Test for valid chars. Note: we accept lower case because that + is patched in pc_ino2dos() at a lower level */ + if ( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ) + return(FALSE); /* Valid */ + if (c >= 0xa1 && c <= 0xdf) /* Katakana */ + return(FALSE); /* Valid */ + if (islfn) + return (_illegal_lfn_char(c)); /* same as ascii */ + return (_illegal_alias_char(c)); /* same as ascii */ + } + else /* length is 2 */ + { + p++; + c = *p; + if (c >= 0x40 && c <= 0x7e) + return(FALSE); /* Valid */ + if (c >= 0x80 && c <= 0xfc) + return(FALSE); /* Valid */ + return(TRUE); /* Invalid */ + } +} +/* Return TRUE if illegal for an 8 dot 3 file */ +BOOLEAN _illegal_jis_alias_char(byte *p) +{ + return(_illegal_jis_file_char(p, 0)); +} + +/***************************************************************************** + PC_VALID_SFN - See if filename is a valid short file name + +Description + Determines validity of a short file name based on the following criteria: + - the file name must be between 0 and 8 characters + - the file extension must be between 0 and 3 characters + - the file name must not begin with a period + - it must not be a reserved DOS file name + - it must not contain any characters that are illegal within sfn's +Returns + TRUE if filename is a valid sfn, FALSE otherwise +*****************************************************************************/ + +BOOLEAN pc_valid_sfn(byte *filename) /* __fn__ */ +{ + int len,period_count,ext_start; + BOOLEAN badchar; + byte name_part[9]; + + int char_len; + byte *pname; + if(name_is_reserved(filename)) return(FALSE); + + pname = filename; + len = 0; + badchar=FALSE; + period_count=0; + ext_start=0; + name_part[0] = 0; + while(*pname) + { + if(_illegal_jis_alias_char(pname)) badchar = TRUE; + if(*pname == '.') + { + ext_start = len+1; + period_count++; + } + + char_len = jis_char_length(pname); + if (!ext_start && len < 8) + { + if (char_len == 2) /* end the test name for reserved */ + name_part[len] = 0; + else + name_part[len] = *pname; + name_part[len+1] = 0; + } + pname += char_len; + len += char_len; + } + if(name_is_reserved(name_part)) return(FALSE); + + if( (filename[0] == ' ') || /* 1st char is a space */ + (len == 0) || /* no name */ + badchar || /* contains illegal chars */ + (period_count > 1) || /* contains more than one extension */ + ((len-ext_start)>3 && period_count>0) || /* extension is longer than 3 chars */ + (period_count==0 && len > 8) || /* name is longer than 8 chars */ + (ext_start > 9) || /* name is longer than 8 chars */ + (ext_start==1) ) return(FALSE); /* no name; 1st char is a period */ + + return(TRUE); +} + +#if (VFAT) /* Small piece of compile time VFAT vs's NONVFAT code */ + +BOOLEAN _illegal_jis_lfn_char(byte *p) +{ + return(_illegal_jis_file_char(p, 1)); +} + + +/*************************************************************************** + validate_filename - See if filename is a valid long file name + +Description + Determines validity of a long file name based on the following criteria: + - the file must be between 0 and 256 characters in length + - it must not be a reserved DOS file name + - it must not contain any characters that are illegal within lfn's +Returns + TRUE if filename is a valid lfn, FALSE otherwise +*****************************************************************************/ + +BOOLEAN validate_filename(byte * filename, byte * ext) +{ + int len; + byte name_part[9]; + + RTFS_ARGSUSED_PVOID((void *) ext); + + + for(len=0; *filename; len++) + { + if (_illegal_jis_lfn_char(filename)) + return(FALSE); + if (len < 5) /* handles lpt1, aux, con etc */ + { + if(*filename == '.') + { + name_part[len] = 0; + if(name_is_reserved(name_part)) return(FALSE); + } + else + { + name_part[len] = *filename; + if (*(filename+1) == 0) + { + name_part[len+1] = 0; + if(name_is_reserved(name_part)) return(FALSE); + } + } + } + filename = jis_increment(filename); + } + + if( (len == 0) || (len > 255) ) return(FALSE); + return(TRUE); +} + +/* JIS Version */ +BOOLEAN pc_cs_malias(byte *alias, byte *input_file, int try) /*__fn__*/ +{ + int n,s; + byte filename[9],fileext[4]; + byte *p_in, *p_in_ext, *p_temp, *p_temp_2; + int char_len, jis_ext_len; + + /* Fill filename[8] with spaces before we start. */ + rtfs_memset(filename, ' ', 8); + + /* Check if invalid short file name.Case is ignored. If ignored we fix it later ! */ + if ((try == -1) && !pc_valid_sfn((byte *)input_file)) + return(FALSE); + + /* Process the JIS alias name */ + p_in = input_file; + while(*p_in =='.' || *p_in ==' ') p_in = jis_increment(p_in); + + /* find extension start */ + /* p_in_ext holds the position right */ + /* after the last period (or it is 0 */ + p_in_ext = 0; + p_temp = p_in; + while(*p_temp) + { + if (*p_temp =='.') + { + p_temp = jis_increment(p_temp); + p_in_ext = p_temp; + } + else + p_temp = jis_increment(p_temp); + } + + p_temp_2 = &fileext[0]; + jis_ext_len = 0; /* We'll use this later to append ext to alias */ + if(p_in_ext && *p_in_ext!=0){ + /* copy extension to fileext[] */ + p_temp = p_in_ext; + for(s=0; *p_temp!=0 && s<3;p_temp+=char_len) + { + char_len = jis_char_length(p_temp); + if(*p_temp!=' ') + { + /* finish if 2 bite jis overflows 3 */ + if(s==2&&char_len==2) + { + break; + } + /* use '_' if illegal*/ + else if(_illegal_jis_alias_char(p_temp)) + { + *p_temp_2++ = '_'; + s += 1; + } + else + { + *p_temp_2++ = *p_temp; + if (char_len==2) + *p_temp_2++ = *(p_temp+1); + s += char_len; + } + } + } + jis_ext_len = s; /* Save for later. bytes in */ + } + *p_temp_2 = 0; /* NULL terminate extention */ + + /* copy file name to filename[], filtering out spaces, periods and + replacing characters illegal in alias names with '_' */ + p_temp = input_file; + p_temp_2 = filename; + + for(s=0; *p_temp!=0 && s<8; p_temp += char_len) + { + if (p_in_ext && p_temp>=p_in_ext) /* hit extension ? */ + break; + char_len = jis_char_length(p_temp); + /* break and use ' ' if 2 bite jis overflows 6 */ + if(s==5&&char_len==2) + { + break; + } + else if(*p_temp!=' ' && *p_temp !='.') + { + if(_illegal_jis_alias_char(p_temp)) + { + *p_temp_2++ = '_'; + s += 1; + } + else + { + *p_temp_2++ = *p_temp; + if (char_len==2) + *p_temp_2++ = *(p_temp+1); + s += char_len; + } + } + } + filename[8]=0; /* null terminate filename[] */ + + pc_ascii_str2upper(filename,filename); + pc_ascii_str2upper(fileext,fileext); + + /* append (TEXT[])i to filename[] */ + if (try != -1) + { + for(n=7,s=try; s>0 && n>0; s/=10,n--) + { + filename[n] = (byte)(((byte)s%10)+'0'); + } + if(n==0 && s>0) + return(FALSE); + else + filename[n]='~'; + } + + p_temp_2 = alias; + p_temp = filename; + + /* copy filename[] to alias[], filtering out spaces */ + s = 0; + while(*p_temp) + { + char_len = jis_char_length(p_temp); + + if (s == 7 && char_len == 2) + break; + if(*p_temp!=' ') + { + *p_temp_2++=*p_temp++; + if (char_len == 2) + *p_temp_2++=*p_temp++; + s += char_len; + if (s == 8) + break; + + } + else + p_temp++; + + } + if(jis_ext_len != 0) + { + *p_temp_2++='.'; /* insert separating period */ + + /* copy fileext[] to alias[] */ + for(s=0; s < jis_ext_len; s++) + { + *p_temp_2++ = fileext[s]; + } + } + *p_temp_2=0; /* null terminate alias[] */ + return(TRUE); +} +#endif + +#if (!VFAT) /* Small piece of compile time VFAT vs's NONVFAT code */ + +BOOLEAN validate_8_3_name(byte * name,int len) /*__fn__*/ +{ + int i; + int last; + + last = len-1; + + for (i = 0; i < len; i++) + { + /* If we hit a space make sure the rest of the string is spaces */ + if (name[i] == ' ') + { + for (; i < len; i++) + { + if (name[i] != ' ') + return(FALSE); + } + break; + } + else + { + if (_illegal_jis_alias_char(&name[i])) + return(FALSE); + /* If it is a 2 byte char advance i */ + if (jis_char_length(&name[i])==2) + { + if (i == last) + return(FALSE); /* two byte at the end. no good */ + i++; + } + } + } + return(TRUE); +} + +BOOLEAN validate_filename(byte * name, byte * ext) /*__fn__*/ +{ +byte sfn_buffer[14]; /* Only uses 13 but keep declarations even */ + if (!( validate_8_3_name(name,8) && validate_8_3_name(ext,3)) ) + return(FALSE); + pc_ascii_mfile(sfn_buffer, name, ext); + return (pc_valid_sfn(sfn_buffer)); +} + +BOOLEAN pc_patcmp_8(byte *p, byte *pattern, BOOLEAN dowildcard) /* __fn__*/ +{ +int size = 8; +byte save_char; +byte *save_p; +BOOLEAN ret_val; + + /* Kludge. never match a deleted file */ + if (*p == PCDELETE) + return (FALSE); + save_p = p; + save_char = *p; + if(save_char == 0x05) + *p = 0xe5; /* JIS KANJI char */ + + ret_val = TRUE; + while (size > 0) + { + if(dowildcard) + { + if (*pattern == '*') /* '*' matches the rest of the name */ + goto ret; + if (*pattern != '?' && !jis_compare_nc(pattern,p)) + { + ret_val = FALSE; + goto ret; + } + } + else + { + if (!jis_compare_nc(pattern,p)) + { + ret_val = FALSE; + goto ret; + } + } + size -= jis_char_length(p); + p = jis_increment(p); + pattern = jis_increment(pattern); + } +ret: + *save_p = save_char; + return(ret_val); +} + +BOOLEAN pc_patcmp_3(byte *p, byte *pattern, BOOLEAN dowildcard) /* __fn__*/ +{ +int size = 3; +BOOLEAN ret_val; + + ret_val = TRUE; + while (size > 0) + { + if(dowildcard) + { + if (*pattern == '*') /* '*' matches the rest of the name */ + goto ret; + if (*pattern != '?' && !jis_compare_nc(pattern,p)) + { + ret_val = FALSE; + goto ret; + } + } + else + { + if (!jis_compare_nc(pattern,p)) + { + ret_val = FALSE; + goto ret; + } + } + size -= jis_char_length(p); + p = jis_increment(p); + pattern = jis_increment(pattern); + } +ret: + return(ret_val); +} + +#endif + + +/* map_jis_input() - take an ascii input string and return a string with + containing the input but convert escaped values into hex in the output + escape XXXX to 16 bit hex + Uses static storage, allows 3 buffers. More can be added + Used only in test programs. + MAPINPUT() calls this routine to convert input ASCII to JIS +*/ + +#define NJISINBUF 3 + +byte btoc(byte b) +{ +byte c; + if (b >= '0' && b <= '9') c = (byte)(b - '0'); + else if (b >= 'a' && b <= 'f') c = (byte)(10 + b - 'a'); + else if (b >= 'A' && b <= 'F') c = (byte)(10 + b - 'A'); + else c = 0; /* Default. bad input to zero */ + return(c);} + +byte btohex(byte *pb){ + byte c; + c = btoc(*pb); + c <<= 4; + pb++; + c = (byte) (c + btoc(*pb)); + return(c); +} + + +byte jis_in_buf[NJISINBUF][255]; +int cur_jis_in_buf = 0; + +byte *map_jis_input(byte *pin) +{ +byte *pout; + + + pout = &jis_in_buf[cur_jis_in_buf][0]; + while (*pin) + { + if (*pin == '<') /* 8 bit hex esc */ + { + pin++; + *pout++ = btohex(pin); + pin+=2; + } + else if (*pin == '>') /* 16 bit hex esc */ + { + pin++; + *pout++ = btohex(pin); + pin+=2; + *pout++ = btohex(pin); + pin+=2; + } + else + *pout++ = *pin++; + + } + *pout = 0; + + /* Set up return */ + pout = &jis_in_buf[cur_jis_in_buf][0]; + /* Increment the pointer */ + cur_jis_in_buf += 1; + if (cur_jis_in_buf == NJISINBUF) + cur_jis_in_buf = 0; + return(pout); +} + + + +#define NJISOUTBUF 3 + +byte btoal(byte c) +{byte a; c &= 0xf; if (c < 10) a = (byte) (c + '0'); else a = (byte) (c-10 + 'A'); + return(a);} +byte btoah(byte c) +{byte a;a = (byte)((c>>4)&0xf); return(btoal(a));} + + +byte jis_out_buf[NJISOUTBUF][255]; +int cur_jis_out_buf = 0; + +byte *map_jis_output(byte *pin) +{ +byte *pout; +int l; + + pout = &jis_out_buf[cur_jis_out_buf][0]; + while (*pin) + { + if (*pin & 0x80) /* JIS Character */ + { + l = jis_char_length(pin); + *pout++='['; + *pout++ = btoah(*pin); + *pout++ = btoal(*pin++); + if (l == 2) + { + *pout++ = btoah(*pin); + *pout++ = btoal(*pin++); + } + *pout++=']'; + } + else + *pout++ = *pin++; + + } + *pout = 0; + + /* Set up return */ + pout = &jis_out_buf[cur_jis_out_buf][0]; + /* Increment the pointer */ + if (cur_jis_out_buf == NJISOUTBUF) + cur_jis_out_buf = 0; + return((byte *)pout); +} + +#endif + diff --git a/build/libraries/fatfs/ARM7/csjistab.c b/build/libraries/fatfs/ARM7/csjistab.c new file mode 100644 index 0000000..a3aab36 --- /dev/null +++ b/build/libraries/fatfs/ARM7/csjistab.c @@ -0,0 +1,3970 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 2002,2004 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* JISTAB.C - Contains tables to convert JIS to unicode and reverse. + Note that these tables are actually Microsoft's CP932 + (also known as Windows-31J). They contain + JIS X 0201-1997 single-byte characters (159 characters), + JIS X 0211-1994 single-byte characters (32 characters), + JIS X 0211-1997 double-byte characters (6879 characters), + NEC special characters (83 characters), + NEC-selected IBM extended characters (374 characters), + and IBM extended characters (388 characters). + Microsoft's recommended round-trip mapping found in + Q170559 is used. + + These tables are based on + http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT. + See also jis.c */ + +#include + +#if (INCLUDE_CS_JIS) + +/* There are 398 non-round-trip mappings. See Microsoft's Q170559. */ +#define NUMJISCHARS 7724 +#define NUMUNICHARS 7326 + + +/* UCODE, JIS */ +KS_CONSTANT word KS_FAR ucode2jis[NUMUNICHARS][2] = { +{0x00A7,0x8198},{0x00A8,0x814E},{0x00B0,0x818B},{0x00B1,0x817D}, +{0x00B4,0x814C},{0x00B6,0x81F7},{0x00D7,0x817E},{0x00F7,0x8180}, +{0x0391,0x839F},{0x0392,0x83A0},{0x0393,0x83A1},{0x0394,0x83A2}, +{0x0395,0x83A3},{0x0396,0x83A4},{0x0397,0x83A5},{0x0398,0x83A6}, +{0x0399,0x83A7},{0x039A,0x83A8},{0x039B,0x83A9},{0x039C,0x83AA}, +{0x039D,0x83AB},{0x039E,0x83AC},{0x039F,0x83AD},{0x03A0,0x83AE}, +{0x03A1,0x83AF},{0x03A3,0x83B0},{0x03A4,0x83B1},{0x03A5,0x83B2}, +{0x03A6,0x83B3},{0x03A7,0x83B4},{0x03A8,0x83B5},{0x03A9,0x83B6}, +{0x03B1,0x83BF},{0x03B2,0x83C0},{0x03B3,0x83C1},{0x03B4,0x83C2}, +{0x03B5,0x83C3},{0x03B6,0x83C4},{0x03B7,0x83C5},{0x03B8,0x83C6}, +{0x03B9,0x83C7},{0x03BA,0x83C8},{0x03BB,0x83C9},{0x03BC,0x83CA}, +{0x03BD,0x83CB},{0x03BE,0x83CC},{0x03BF,0x83CD},{0x03C0,0x83CE}, +{0x03C1,0x83CF},{0x03C3,0x83D0},{0x03C4,0x83D1},{0x03C5,0x83D2}, +{0x03C6,0x83D3},{0x03C7,0x83D4},{0x03C8,0x83D5},{0x03C9,0x83D6}, +{0x0401,0x8446},{0x0410,0x8440},{0x0411,0x8441},{0x0412,0x8442}, +{0x0413,0x8443},{0x0414,0x8444},{0x0415,0x8445},{0x0416,0x8447}, +{0x0417,0x8448},{0x0418,0x8449},{0x0419,0x844A},{0x041A,0x844B}, +{0x041B,0x844C},{0x041C,0x844D},{0x041D,0x844E},{0x041E,0x844F}, +{0x041F,0x8450},{0x0420,0x8451},{0x0421,0x8452},{0x0422,0x8453}, +{0x0423,0x8454},{0x0424,0x8455},{0x0425,0x8456},{0x0426,0x8457}, +{0x0427,0x8458},{0x0428,0x8459},{0x0429,0x845A},{0x042A,0x845B}, +{0x042B,0x845C},{0x042C,0x845D},{0x042D,0x845E},{0x042E,0x845F}, +{0x042F,0x8460},{0x0430,0x8470},{0x0431,0x8471},{0x0432,0x8472}, +{0x0433,0x8473},{0x0434,0x8474},{0x0435,0x8475},{0x0436,0x8477}, +{0x0437,0x8478},{0x0438,0x8479},{0x0439,0x847A},{0x043A,0x847B}, +{0x043B,0x847C},{0x043C,0x847D},{0x043D,0x847E},{0x043E,0x8480}, +{0x043F,0x8481},{0x0440,0x8482},{0x0441,0x8483},{0x0442,0x8484}, +{0x0443,0x8485},{0x0444,0x8486},{0x0445,0x8487},{0x0446,0x8488}, +{0x0447,0x8489},{0x0448,0x848A},{0x0449,0x848B},{0x044A,0x848C}, +{0x044B,0x848D},{0x044C,0x848E},{0x044D,0x848F},{0x044E,0x8490}, +{0x044F,0x8491},{0x0451,0x8476},{0x2010,0x815D},{0x2015,0x815C}, +{0x2018,0x8165},{0x2019,0x8166},{0x201C,0x8167},{0x201D,0x8168}, +{0x2020,0x81F5},{0x2021,0x81F6},{0x2025,0x8164},{0x2026,0x8163}, +{0x2030,0x81F1},{0x2032,0x818C},{0x2033,0x818D},{0x203B,0x81A6}, +{0x2103,0x818E},{0x2116,0x8782},{0x2121,0x8784},{0x212B,0x81F0}, +{0x2160,0x8754},{0x2161,0x8755},{0x2162,0x8756},{0x2163,0x8757}, +{0x2164,0x8758},{0x2165,0x8759},{0x2166,0x875A},{0x2167,0x875B}, +{0x2168,0x875C},{0x2169,0x875D},{0x2170,0xFA40},{0x2171,0xFA41}, +{0x2172,0xFA42},{0x2173,0xFA43},{0x2174,0xFA44},{0x2175,0xFA45}, +{0x2176,0xFA46},{0x2177,0xFA47},{0x2178,0xFA48},{0x2179,0xFA49}, +{0x2190,0x81A9},{0x2191,0x81AA},{0x2192,0x81A8},{0x2193,0x81AB}, +{0x21D2,0x81CB},{0x21D4,0x81CC},{0x2200,0x81CD},{0x2202,0x81DD}, +{0x2203,0x81CE},{0x2207,0x81DE},{0x2208,0x81B8},{0x220B,0x81B9}, +{0x2211,0x8794},{0x221A,0x81E3},{0x221D,0x81E5},{0x221E,0x8187}, +{0x221F,0x8798},{0x2220,0x81DA},{0x2225,0x8161},{0x2227,0x81C8}, +{0x2228,0x81C9},{0x2229,0x81BF},{0x222A,0x81BE},{0x222B,0x81E7}, +{0x222C,0x81E8},{0x222E,0x8793},{0x2234,0x8188},{0x2235,0x81E6}, +{0x223D,0x81E4},{0x2252,0x81E0},{0x2260,0x8182},{0x2261,0x81DF}, +{0x2266,0x8185},{0x2267,0x8186},{0x226A,0x81E1},{0x226B,0x81E2}, +{0x2282,0x81BC},{0x2283,0x81BD},{0x2286,0x81BA},{0x2287,0x81BB}, +{0x22A5,0x81DB},{0x22BF,0x8799},{0x2312,0x81DC},{0x2460,0x8740}, +{0x2461,0x8741},{0x2462,0x8742},{0x2463,0x8743},{0x2464,0x8744}, +{0x2465,0x8745},{0x2466,0x8746},{0x2467,0x8747},{0x2468,0x8748}, +{0x2469,0x8749},{0x246A,0x874A},{0x246B,0x874B},{0x246C,0x874C}, +{0x246D,0x874D},{0x246E,0x874E},{0x246F,0x874F},{0x2470,0x8750}, +{0x2471,0x8751},{0x2472,0x8752},{0x2473,0x8753},{0x2500,0x849F}, +{0x2501,0x84AA},{0x2502,0x84A0},{0x2503,0x84AB},{0x250C,0x84A1}, +{0x250F,0x84AC},{0x2510,0x84A2},{0x2513,0x84AD},{0x2514,0x84A4}, +{0x2517,0x84AF},{0x2518,0x84A3},{0x251B,0x84AE},{0x251C,0x84A5}, +{0x251D,0x84BA},{0x2520,0x84B5},{0x2523,0x84B0},{0x2524,0x84A7}, +{0x2525,0x84BC},{0x2528,0x84B7},{0x252B,0x84B2},{0x252C,0x84A6}, +{0x252F,0x84B6},{0x2530,0x84BB},{0x2533,0x84B1},{0x2534,0x84A8}, +{0x2537,0x84B8},{0x2538,0x84BD},{0x253B,0x84B3},{0x253C,0x84A9}, +{0x253F,0x84B9},{0x2542,0x84BE},{0x254B,0x84B4},{0x25A0,0x81A1}, +{0x25A1,0x81A0},{0x25B2,0x81A3},{0x25B3,0x81A2},{0x25BC,0x81A5}, +{0x25BD,0x81A4},{0x25C6,0x819F},{0x25C7,0x819E},{0x25CB,0x819B}, +{0x25CE,0x819D},{0x25CF,0x819C},{0x25EF,0x81FC},{0x2605,0x819A}, +{0x2606,0x8199},{0x2640,0x818A},{0x2642,0x8189},{0x266A,0x81F4}, +{0x266D,0x81F3},{0x266F,0x81F2},{0x3000,0x8140},{0x3001,0x8141}, +{0x3002,0x8142},{0x3003,0x8156},{0x3005,0x8158},{0x3006,0x8159}, +{0x3007,0x815A},{0x3008,0x8171},{0x3009,0x8172},{0x300A,0x8173}, +{0x300B,0x8174},{0x300C,0x8175},{0x300D,0x8176},{0x300E,0x8177}, +{0x300F,0x8178},{0x3010,0x8179},{0x3011,0x817A},{0x3012,0x81A7}, +{0x3013,0x81AC},{0x3014,0x816B},{0x3015,0x816C},{0x301D,0x8780}, +{0x301F,0x8781},{0x3041,0x829F},{0x3042,0x82A0},{0x3043,0x82A1}, +{0x3044,0x82A2},{0x3045,0x82A3},{0x3046,0x82A4},{0x3047,0x82A5}, +{0x3048,0x82A6},{0x3049,0x82A7},{0x304A,0x82A8},{0x304B,0x82A9}, +{0x304C,0x82AA},{0x304D,0x82AB},{0x304E,0x82AC},{0x304F,0x82AD}, +{0x3050,0x82AE},{0x3051,0x82AF},{0x3052,0x82B0},{0x3053,0x82B1}, +{0x3054,0x82B2},{0x3055,0x82B3},{0x3056,0x82B4},{0x3057,0x82B5}, +{0x3058,0x82B6},{0x3059,0x82B7},{0x305A,0x82B8},{0x305B,0x82B9}, +{0x305C,0x82BA},{0x305D,0x82BB},{0x305E,0x82BC},{0x305F,0x82BD}, +{0x3060,0x82BE},{0x3061,0x82BF},{0x3062,0x82C0},{0x3063,0x82C1}, +{0x3064,0x82C2},{0x3065,0x82C3},{0x3066,0x82C4},{0x3067,0x82C5}, +{0x3068,0x82C6},{0x3069,0x82C7},{0x306A,0x82C8},{0x306B,0x82C9}, +{0x306C,0x82CA},{0x306D,0x82CB},{0x306E,0x82CC},{0x306F,0x82CD}, +{0x3070,0x82CE},{0x3071,0x82CF},{0x3072,0x82D0},{0x3073,0x82D1}, +{0x3074,0x82D2},{0x3075,0x82D3},{0x3076,0x82D4},{0x3077,0x82D5}, +{0x3078,0x82D6},{0x3079,0x82D7},{0x307A,0x82D8},{0x307B,0x82D9}, +{0x307C,0x82DA},{0x307D,0x82DB},{0x307E,0x82DC},{0x307F,0x82DD}, +{0x3080,0x82DE},{0x3081,0x82DF},{0x3082,0x82E0},{0x3083,0x82E1}, +{0x3084,0x82E2},{0x3085,0x82E3},{0x3086,0x82E4},{0x3087,0x82E5}, +{0x3088,0x82E6},{0x3089,0x82E7},{0x308A,0x82E8},{0x308B,0x82E9}, +{0x308C,0x82EA},{0x308D,0x82EB},{0x308E,0x82EC},{0x308F,0x82ED}, +{0x3090,0x82EE},{0x3091,0x82EF},{0x3092,0x82F0},{0x3093,0x82F1}, +{0x309B,0x814A},{0x309C,0x814B},{0x309D,0x8154},{0x309E,0x8155}, +{0x30A1,0x8340},{0x30A2,0x8341},{0x30A3,0x8342},{0x30A4,0x8343}, +{0x30A5,0x8344},{0x30A6,0x8345},{0x30A7,0x8346},{0x30A8,0x8347}, +{0x30A9,0x8348},{0x30AA,0x8349},{0x30AB,0x834A},{0x30AC,0x834B}, +{0x30AD,0x834C},{0x30AE,0x834D},{0x30AF,0x834E},{0x30B0,0x834F}, +{0x30B1,0x8350},{0x30B2,0x8351},{0x30B3,0x8352},{0x30B4,0x8353}, +{0x30B5,0x8354},{0x30B6,0x8355},{0x30B7,0x8356},{0x30B8,0x8357}, +{0x30B9,0x8358},{0x30BA,0x8359},{0x30BB,0x835A},{0x30BC,0x835B}, +{0x30BD,0x835C},{0x30BE,0x835D},{0x30BF,0x835E},{0x30C0,0x835F}, +{0x30C1,0x8360},{0x30C2,0x8361},{0x30C3,0x8362},{0x30C4,0x8363}, +{0x30C5,0x8364},{0x30C6,0x8365},{0x30C7,0x8366},{0x30C8,0x8367}, +{0x30C9,0x8368},{0x30CA,0x8369},{0x30CB,0x836A},{0x30CC,0x836B}, +{0x30CD,0x836C},{0x30CE,0x836D},{0x30CF,0x836E},{0x30D0,0x836F}, +{0x30D1,0x8370},{0x30D2,0x8371},{0x30D3,0x8372},{0x30D4,0x8373}, +{0x30D5,0x8374},{0x30D6,0x8375},{0x30D7,0x8376},{0x30D8,0x8377}, +{0x30D9,0x8378},{0x30DA,0x8379},{0x30DB,0x837A},{0x30DC,0x837B}, +{0x30DD,0x837C},{0x30DE,0x837D},{0x30DF,0x837E},{0x30E0,0x8380}, +{0x30E1,0x8381},{0x30E2,0x8382},{0x30E3,0x8383},{0x30E4,0x8384}, +{0x30E5,0x8385},{0x30E6,0x8386},{0x30E7,0x8387},{0x30E8,0x8388}, +{0x30E9,0x8389},{0x30EA,0x838A},{0x30EB,0x838B},{0x30EC,0x838C}, +{0x30ED,0x838D},{0x30EE,0x838E},{0x30EF,0x838F},{0x30F0,0x8390}, +{0x30F1,0x8391},{0x30F2,0x8392},{0x30F3,0x8393},{0x30F4,0x8394}, +{0x30F5,0x8395},{0x30F6,0x8396},{0x30FB,0x8145},{0x30FC,0x815B}, +{0x30FD,0x8152},{0x30FE,0x8153},{0x3231,0x878A},{0x3232,0x878B}, +{0x3239,0x878C},{0x32A4,0x8785},{0x32A5,0x8786},{0x32A6,0x8787}, +{0x32A7,0x8788},{0x32A8,0x8789},{0x3303,0x8765},{0x330D,0x8769}, +{0x3314,0x8760},{0x3318,0x8763},{0x3322,0x8761},{0x3323,0x876B}, +{0x3326,0x876A},{0x3327,0x8764},{0x332B,0x876C},{0x3336,0x8766}, +{0x333B,0x876E},{0x3349,0x875F},{0x334A,0x876D},{0x334D,0x8762}, +{0x3351,0x8767},{0x3357,0x8768},{0x337B,0x877E},{0x337C,0x878F}, +{0x337D,0x878E},{0x337E,0x878D},{0x338E,0x8772},{0x338F,0x8773}, +{0x339C,0x876F},{0x339D,0x8770},{0x339E,0x8771},{0x33A1,0x8775}, +{0x33C4,0x8774},{0x33CD,0x8783},{0x4E00,0x88EA},{0x4E01,0x929A}, +{0x4E03,0x8EB5},{0x4E07,0x969C},{0x4E08,0x8FE4},{0x4E09,0x8E4F}, +{0x4E0A,0x8FE3},{0x4E0B,0x89BA},{0x4E0D,0x9573},{0x4E0E,0x975E}, +{0x4E10,0x98A0},{0x4E11,0x894E},{0x4E14,0x8A8E},{0x4E15,0x98A1}, +{0x4E16,0x90A2},{0x4E17,0x99C0},{0x4E18,0x8B75},{0x4E19,0x95B8}, +{0x4E1E,0x8FE5},{0x4E21,0x97BC},{0x4E26,0x95C0},{0x4E28,0xFA68}, +{0x4E2A,0x98A2},{0x4E2D,0x9286},{0x4E31,0x98A3},{0x4E32,0x8BF8}, +{0x4E36,0x98A4},{0x4E38,0x8ADB},{0x4E39,0x924F},{0x4E3B,0x8EE5}, +{0x4E3C,0x98A5},{0x4E3F,0x98A6},{0x4E42,0x98A7},{0x4E43,0x9454}, +{0x4E45,0x8B76},{0x4E4B,0x9456},{0x4E4D,0x93E1},{0x4E4E,0x8CC1}, +{0x4E4F,0x9652},{0x4E55,0xE568},{0x4E56,0x98A8},{0x4E57,0x8FE6}, +{0x4E58,0x98A9},{0x4E59,0x89B3},{0x4E5D,0x8BE3},{0x4E5E,0x8CEE}, +{0x4E5F,0x96E7},{0x4E62,0x9BA4},{0x4E71,0x9790},{0x4E73,0x93FB}, +{0x4E7E,0x8AA3},{0x4E80,0x8B54},{0x4E82,0x98AA},{0x4E85,0x98AB}, +{0x4E86,0x97B9},{0x4E88,0x975C},{0x4E89,0x9188},{0x4E8A,0x98AD}, +{0x4E8B,0x8E96},{0x4E8C,0x93F1},{0x4E8E,0x98B0},{0x4E91,0x895D}, +{0x4E92,0x8CDD},{0x4E94,0x8CDC},{0x4E95,0x88E4},{0x4E98,0x986A}, +{0x4E99,0x9869},{0x4E9B,0x8DB1},{0x4E9C,0x889F},{0x4E9E,0x98B1}, +{0x4E9F,0x98B2},{0x4EA0,0x98B3},{0x4EA1,0x9653},{0x4EA2,0x98B4}, +{0x4EA4,0x8CF0},{0x4EA5,0x88E5},{0x4EA6,0x9692},{0x4EA8,0x8B9C}, +{0x4EAB,0x8B9D},{0x4EAC,0x8B9E},{0x4EAD,0x92E0},{0x4EAE,0x97BA}, +{0x4EB0,0x98B5},{0x4EB3,0x98B6},{0x4EB6,0x98B7},{0x4EBA,0x906C}, +{0x4EC0,0x8F59},{0x4EC1,0x906D},{0x4EC2,0x98BC},{0x4EC4,0x98BA}, +{0x4EC6,0x98BB},{0x4EC7,0x8B77},{0x4ECA,0x8DA1},{0x4ECB,0x89EE}, +{0x4ECD,0x98B9},{0x4ECE,0x98B8},{0x4ECF,0x95A7},{0x4ED4,0x8E65}, +{0x4ED5,0x8E64},{0x4ED6,0x91BC},{0x4ED7,0x98BD},{0x4ED8,0x9574}, +{0x4ED9,0x90E5},{0x4EDD,0x8157},{0x4EDE,0x98BE},{0x4EDF,0x98C0}, +{0x4EE1,0xFA69},{0x4EE3,0x91E3},{0x4EE4,0x97DF},{0x4EE5,0x88C8}, +{0x4EED,0x98BF},{0x4EEE,0x89BC},{0x4EF0,0x8BC2},{0x4EF2,0x9287}, +{0x4EF6,0x8C8F},{0x4EF7,0x98C1},{0x4EFB,0x9443},{0x4EFC,0xFA6A}, +{0x4F00,0xFA6B},{0x4F01,0x8AE9},{0x4F03,0xFA6C},{0x4F09,0x98C2}, +{0x4F0A,0x88C9},{0x4F0D,0x8CDE},{0x4F0E,0x8AEA},{0x4F0F,0x959A}, +{0x4F10,0x94B0},{0x4F11,0x8B78},{0x4F1A,0x89EF},{0x4F1C,0x98E5}, +{0x4F1D,0x9360},{0x4F2F,0x948C},{0x4F30,0x98C4},{0x4F34,0x94BA}, +{0x4F36,0x97E0},{0x4F38,0x904C},{0x4F39,0xFA6D},{0x4F3A,0x8E66}, +{0x4F3C,0x8E97},{0x4F3D,0x89BE},{0x4F43,0x92CF},{0x4F46,0x9241}, +{0x4F47,0x98C8},{0x4F4D,0x88CA},{0x4F4E,0x92E1},{0x4F4F,0x8F5A}, +{0x4F50,0x8DB2},{0x4F51,0x9743},{0x4F53,0x91CC},{0x4F55,0x89BD}, +{0x4F56,0xFA6E},{0x4F57,0x98C7},{0x4F59,0x975D},{0x4F5A,0x98C3}, +{0x4F5B,0x98C5},{0x4F5C,0x8DEC},{0x4F5D,0x98C6},{0x4F5E,0x9B43}, +{0x4F69,0x98CE},{0x4F6F,0x98D1},{0x4F70,0x98CF},{0x4F73,0x89C0}, +{0x4F75,0x95B9},{0x4F76,0x98C9},{0x4F7B,0x98CD},{0x4F7C,0x8CF1}, +{0x4F7F,0x8E67},{0x4F83,0x8AA4},{0x4F86,0x98D2},{0x4F88,0x98CA}, +{0x4F8A,0xFA70},{0x4F8B,0x97E1},{0x4F8D,0x8E98},{0x4F8F,0x98CB}, +{0x4F91,0x98D0},{0x4F92,0xFA6F},{0x4F94,0xFA72},{0x4F96,0x98D3}, +{0x4F98,0x98CC},{0x4F9A,0xFA71},{0x4F9B,0x8B9F},{0x4F9D,0x88CB}, +{0x4FA0,0x8BA0},{0x4FA1,0x89BF},{0x4FAB,0x9B44},{0x4FAD,0x9699}, +{0x4FAE,0x958E},{0x4FAF,0x8CF2},{0x4FB5,0x904E},{0x4FB6,0x97B5}, +{0x4FBF,0x95D6},{0x4FC2,0x8C57},{0x4FC3,0x91A3},{0x4FC4,0x89E2}, +{0x4FC9,0xFA61},{0x4FCA,0x8F72},{0x4FCD,0xFA73},{0x4FCE,0x98D7}, +{0x4FD0,0x98DC},{0x4FD1,0x98DA},{0x4FD4,0x98D5},{0x4FD7,0x91AD}, +{0x4FD8,0x98D8},{0x4FDA,0x98DB},{0x4FDB,0x98D9},{0x4FDD,0x95DB}, +{0x4FDF,0x98D6},{0x4FE1,0x904D},{0x4FE3,0x9693},{0x4FE4,0x98DD}, +{0x4FE5,0x98DE},{0x4FEE,0x8F43},{0x4FEF,0x98EB},{0x4FF3,0x946F}, +{0x4FF5,0x9555},{0x4FF6,0x98E6},{0x4FF8,0x95EE},{0x4FFA,0x89B4}, +{0x4FFE,0x98EA},{0x4FFF,0xFA76},{0x5005,0x98E4},{0x5006,0x98ED}, +{0x5009,0x9171},{0x500B,0x8CC2},{0x500D,0x947B},{0x500F,0xE0C5}, +{0x5011,0x98EC},{0x5012,0x937C},{0x5014,0x98E1},{0x5016,0x8CF4}, +{0x5019,0x8CF3},{0x501A,0x98DF},{0x501E,0xFA77},{0x501F,0x8ED8}, +{0x5021,0x98E7},{0x5022,0xFA75},{0x5023,0x95ED},{0x5024,0x926C}, +{0x5025,0x98E3},{0x5026,0x8C91},{0x5028,0x98E0},{0x5029,0x98E8}, +{0x502A,0x98E2},{0x502B,0x97CF},{0x502C,0x98E9},{0x502D,0x9860}, +{0x5036,0x8BE4},{0x5039,0x8C90},{0x5040,0xFA74},{0x5042,0xFA7A}, +{0x5043,0x98EE},{0x5046,0xFA78},{0x5047,0x98EF},{0x5048,0x98F3}, +{0x5049,0x88CC},{0x504F,0x95CE},{0x5050,0x98F2},{0x5055,0x98F1}, +{0x5056,0x98F5},{0x505A,0x98F4},{0x505C,0x92E2},{0x5065,0x8C92}, +{0x506C,0x98F6},{0x5070,0xFA79},{0x5072,0x8EC3},{0x5074,0x91A4}, +{0x5075,0x92E3},{0x5076,0x8BF4},{0x5078,0x98F7},{0x507D,0x8B55}, +{0x5080,0x98F8},{0x5085,0x98FA},{0x508D,0x9654},{0x5091,0x8C86}, +{0x5094,0xFA7B},{0x5098,0x8E50},{0x5099,0x94F5},{0x509A,0x98F9}, +{0x50AC,0x8DC3},{0x50AD,0x9762},{0x50B2,0x98FC},{0x50B3,0x9942}, +{0x50B4,0x98FB},{0x50B5,0x8DC2},{0x50B7,0x8F9D},{0x50BE,0x8C58}, +{0x50C2,0x9943},{0x50C5,0x8BCD},{0x50C9,0x9940},{0x50CA,0x9941}, +{0x50CD,0x93AD},{0x50CF,0x919C},{0x50D1,0x8BA1},{0x50D5,0x966C}, +{0x50D6,0x9944},{0x50D8,0xFA7D},{0x50DA,0x97BB},{0x50DE,0x9945}, +{0x50E3,0x9948},{0x50E5,0x9946},{0x50E7,0x916D},{0x50ED,0x9947}, +{0x50EE,0x9949},{0x50F4,0xFA7C},{0x50F5,0x994B},{0x50F9,0x994A}, +{0x50FB,0x95C6},{0x5100,0x8B56},{0x5101,0x994D},{0x5102,0x994E}, +{0x5104,0x89AD},{0x5109,0x994C},{0x5112,0x8EF2},{0x5114,0x9951}, +{0x5115,0x9950},{0x5116,0x994F},{0x5118,0x98D4},{0x511A,0x9952}, +{0x511F,0x8F9E},{0x5121,0x9953},{0x512A,0x9744},{0x5132,0x96D7}, +{0x5137,0x9955},{0x513A,0x9954},{0x513B,0x9957},{0x513C,0x9956}, +{0x513F,0x9958},{0x5140,0x9959},{0x5141,0x88F2},{0x5143,0x8CB3}, +{0x5144,0x8C5A},{0x5145,0x8F5B},{0x5146,0x929B},{0x5147,0x8BA2}, +{0x5148,0x90E6},{0x5149,0x8CF5},{0x514A,0xFA7E},{0x514B,0x8D8E}, +{0x514C,0x995B},{0x514D,0x96C6},{0x514E,0x9365},{0x5150,0x8E99}, +{0x5152,0x995A},{0x5154,0x995C},{0x515A,0x937D},{0x515C,0x8A95}, +{0x5162,0x995D},{0x5164,0xFA80},{0x5165,0x93FC},{0x5168,0x9153}, +{0x5169,0x995F},{0x516A,0x9960},{0x516B,0x94AA},{0x516C,0x8CF6}, +{0x516D,0x985A},{0x516E,0x9961},{0x5171,0x8BA4},{0x5175,0x95BA}, +{0x5176,0x91B4},{0x5177,0x8BEF},{0x5178,0x9354},{0x517C,0x8C93}, +{0x5180,0x9962},{0x5182,0x9963},{0x5185,0x93E0},{0x5186,0x897E}, +{0x5189,0x9966},{0x518A,0x8DFB},{0x518C,0x9965},{0x518D,0x8DC4}, +{0x518F,0x9967},{0x5190,0xE3EC},{0x5191,0x9968},{0x5192,0x9660}, +{0x5193,0x9969},{0x5195,0x996A},{0x5196,0x996B},{0x5197,0x8FE7}, +{0x5199,0x8ECA},{0x519D,0xFA81},{0x51A0,0x8AA5},{0x51A2,0x996E}, +{0x51A4,0x996C},{0x51A5,0x96BB},{0x51A6,0x996D},{0x51A8,0x9579}, +{0x51A9,0x996F},{0x51AA,0x9970},{0x51AB,0x9971},{0x51AC,0x937E}, +{0x51B0,0x9975},{0x51B1,0x9973},{0x51B2,0x9974},{0x51B3,0x9972}, +{0x51B4,0x8DE1},{0x51B5,0x9976},{0x51B6,0x96E8},{0x51B7,0x97E2}, +{0x51BD,0x9977},{0x51BE,0xFA82},{0x51C4,0x90A6},{0x51C5,0x9978}, +{0x51C6,0x8F79},{0x51C9,0x9979},{0x51CB,0x929C},{0x51CC,0x97BD}, +{0x51CD,0x9380},{0x51D6,0x99C3},{0x51DB,0x997A},{0x51DC,0xEAA3}, +{0x51DD,0x8BC3},{0x51E0,0x997B},{0x51E1,0x967D},{0x51E6,0x8F88}, +{0x51E7,0x91FA},{0x51E9,0x997D},{0x51EA,0x93E2},{0x51EC,0xFA83}, +{0x51ED,0x997E},{0x51F0,0x9980},{0x51F1,0x8A4D},{0x51F5,0x9981}, +{0x51F6,0x8BA5},{0x51F8,0x93CA},{0x51F9,0x899A},{0x51FA,0x8F6F}, +{0x51FD,0x949F},{0x51FE,0x9982},{0x5200,0x9381},{0x5203,0x906E}, +{0x5204,0x9983},{0x5206,0x95AA},{0x5207,0x90D8},{0x5208,0x8AA0}, +{0x520A,0x8AA7},{0x520B,0x9984},{0x520E,0x9986},{0x5211,0x8C59}, +{0x5214,0x9985},{0x5215,0xFA84},{0x5217,0x97F1},{0x521D,0x8F89}, +{0x5224,0x94BB},{0x5225,0x95CA},{0x5227,0x9987},{0x5229,0x9798}, +{0x522A,0x9988},{0x522E,0x9989},{0x5230,0x939E},{0x5233,0x998A}, +{0x5236,0x90A7},{0x5237,0x8DFC},{0x5238,0x8C94},{0x5239,0x998B}, +{0x523A,0x8E68},{0x523B,0x8D8F},{0x5243,0x92E4},{0x5244,0x998D}, +{0x5247,0x91A5},{0x524A,0x8DED},{0x524B,0x998E},{0x524C,0x998F}, +{0x524D,0x914F},{0x524F,0x998C},{0x5254,0x9991},{0x5256,0x9655}, +{0x525B,0x8D84},{0x525E,0x9990},{0x5263,0x8C95},{0x5264,0x8DDC}, +{0x5265,0x948D},{0x5269,0x9994},{0x526A,0x9992},{0x526F,0x959B}, +{0x5270,0x8FE8},{0x5271,0x999B},{0x5272,0x8A84},{0x5273,0x9995}, +{0x5274,0x9993},{0x5275,0x916E},{0x527D,0x9997},{0x527F,0x9996}, +{0x5283,0x8A63},{0x5287,0x8C80},{0x5288,0x999C},{0x5289,0x97AB}, +{0x528D,0x9998},{0x5291,0x999D},{0x5292,0x999A},{0x5294,0x9999}, +{0x529B,0x97CD},{0x529C,0xFA85},{0x529F,0x8CF7},{0x52A0,0x89C1}, +{0x52A3,0x97F2},{0x52A6,0xFA86},{0x52A9,0x8F95},{0x52AA,0x9377}, +{0x52AB,0x8D85},{0x52AC,0x99A0},{0x52AD,0x99A1},{0x52AF,0xFB77}, +{0x52B1,0x97E3},{0x52B4,0x984A},{0x52B5,0x99A3},{0x52B9,0x8CF8}, +{0x52BC,0x99A2},{0x52BE,0x8A4E},{0x52C0,0xFA87},{0x52C1,0x99A4}, +{0x52C3,0x9675},{0x52C5,0x92BA},{0x52C7,0x9745},{0x52C9,0x95D7}, +{0x52CD,0x99A5},{0x52D2,0xE8D3},{0x52D5,0x93AE},{0x52D7,0x99A6}, +{0x52D8,0x8AA8},{0x52D9,0x96B1},{0x52DB,0xFA88},{0x52DD,0x8F9F}, +{0x52DE,0x99A7},{0x52DF,0x95E5},{0x52E0,0x99AB},{0x52E2,0x90A8}, +{0x52E3,0x99A8},{0x52E4,0x8BCE},{0x52E6,0x99A9},{0x52E7,0x8AA9}, +{0x52F2,0x8C4D},{0x52F3,0x99AC},{0x52F5,0x99AD},{0x52F8,0x99AE}, +{0x52F9,0x99AF},{0x52FA,0x8ED9},{0x52FE,0x8CF9},{0x52FF,0x96DC}, +{0x5300,0xFA89},{0x5301,0x96E6},{0x5302,0x93F5},{0x5305,0x95EF}, +{0x5306,0x99B0},{0x5307,0xFA8A},{0x5308,0x99B1},{0x530D,0x99B3}, +{0x530F,0x99B5},{0x5310,0x99B4},{0x5315,0x99B6},{0x5316,0x89BB}, +{0x5317,0x966B},{0x5319,0x8DFA},{0x531A,0x99B7},{0x531D,0x9178}, +{0x5320,0x8FA0},{0x5321,0x8BA7},{0x5323,0x99B8},{0x5324,0xFA8B}, +{0x532A,0x94D9},{0x532F,0x99B9},{0x5331,0x99BA},{0x5333,0x99BB}, +{0x5338,0x99BC},{0x5339,0x9543},{0x533A,0x8BE6},{0x533B,0x88E3}, +{0x533F,0x93BD},{0x5340,0x99BD},{0x5341,0x8F5C},{0x5343,0x90E7}, +{0x5345,0x99BF},{0x5346,0x99BE},{0x5347,0x8FA1},{0x5348,0x8CDF}, +{0x5349,0x99C1},{0x534A,0x94BC},{0x534D,0x99C2},{0x5351,0x94DA}, +{0x5352,0x91B2},{0x5353,0x91EC},{0x5354,0x8BA6},{0x5357,0x93EC}, +{0x5358,0x9250},{0x535A,0x948E},{0x535C,0x966D},{0x535E,0x99C4}, +{0x5360,0x90E8},{0x5366,0x8C54},{0x5369,0x99C5},{0x536E,0x99C6}, +{0x536F,0x894B},{0x5370,0x88F3},{0x5371,0x8AEB},{0x5372,0xFA8C}, +{0x5373,0x91A6},{0x5374,0x8B70},{0x5375,0x9791},{0x5377,0x99C9}, +{0x5378,0x89B5},{0x537B,0x99C8},{0x537F,0x8BA8},{0x5382,0x99CA}, +{0x5384,0x96EF},{0x5393,0xFA8D},{0x5396,0x99CB},{0x5398,0x97D0}, +{0x539A,0x8CFA},{0x539F,0x8CB4},{0x53A0,0x99CC},{0x53A5,0x99CE}, +{0x53A6,0x99CD},{0x53A8,0x907E},{0x53A9,0x8958},{0x53AD,0x897D}, +{0x53AE,0x99CF},{0x53B0,0x99D0},{0x53B2,0xFA8E},{0x53B3,0x8CB5}, +{0x53B6,0x99D1},{0x53BB,0x8B8E},{0x53C2,0x8E51},{0x53C3,0x99D2}, +{0x53C8,0x9694},{0x53C9,0x8DB3},{0x53CA,0x8B79},{0x53CB,0x9746}, +{0x53CC,0x916F},{0x53CD,0x94BD},{0x53CE,0x8EFB},{0x53D4,0x8F66}, +{0x53D6,0x8EE6},{0x53D7,0x8EF3},{0x53D9,0x8F96},{0x53DB,0x94BE}, +{0x53DD,0xFA8F},{0x53DF,0x99D5},{0x53E1,0x8962},{0x53E2,0x9170}, +{0x53E3,0x8CFB},{0x53E4,0x8CC3},{0x53E5,0x8BE5},{0x53E8,0x99D9}, +{0x53E9,0x9240},{0x53EA,0x91FC},{0x53EB,0x8BA9},{0x53EC,0x8FA2}, +{0x53ED,0x99DA},{0x53EE,0x99D8},{0x53EF,0x89C2},{0x53F0,0x91E4}, +{0x53F1,0x8EB6},{0x53F2,0x8E6A},{0x53F3,0x8945},{0x53F6,0x8A90}, +{0x53F7,0x8D86},{0x53F8,0x8E69},{0x53FA,0x99DB},{0x5401,0x99DC}, +{0x5403,0x8B68},{0x5404,0x8A65},{0x5408,0x8D87},{0x5409,0x8B67}, +{0x540A,0x92DD},{0x540B,0x8944},{0x540C,0x93AF},{0x540D,0x96BC}, +{0x540E,0x8D40},{0x540F,0x9799},{0x5410,0x9366},{0x5411,0x8CFC}, +{0x541B,0x8C4E},{0x541D,0x99E5},{0x541F,0x8BE1},{0x5420,0x9669}, +{0x5426,0x94DB},{0x5429,0x99E4},{0x542B,0x8ADC},{0x542C,0x99DF}, +{0x542D,0x99E0},{0x542E,0x99E2},{0x5436,0x99E3},{0x5438,0x8B7A}, +{0x5439,0x9081},{0x543B,0x95AB},{0x543C,0x99E1},{0x543D,0x99DD}, +{0x543E,0x8CE1},{0x5440,0x99DE},{0x5442,0x9843},{0x5446,0x95F0}, +{0x5448,0x92E6},{0x5449,0x8CE0},{0x544A,0x8D90},{0x544E,0x99E6}, +{0x5451,0x93DB},{0x545F,0x99EA},{0x5468,0x8EFC},{0x546A,0x8EF4}, +{0x5470,0x99ED},{0x5471,0x99EB},{0x5473,0x96A1},{0x5475,0x99E8}, +{0x5476,0x99F1},{0x5477,0x99EC},{0x547B,0x99EF},{0x547C,0x8CC4}, +{0x547D,0x96BD},{0x5480,0x99F0},{0x5484,0x99F2},{0x5486,0x99F4}, +{0x548A,0xFA92},{0x548B,0x8DEE},{0x548C,0x9861},{0x548E,0x99E9}, +{0x548F,0x99E7},{0x5490,0x99F3},{0x5492,0x99EE},{0x549C,0xFA91}, +{0x54A2,0x99F6},{0x54A4,0x9A42},{0x54A5,0x99F8},{0x54A8,0x99FC}, +{0x54A9,0xFA93},{0x54AB,0x9A40},{0x54AC,0x99F9},{0x54AF,0x9A5D}, +{0x54B2,0x8DE7},{0x54B3,0x8A50},{0x54B8,0x99F7},{0x54BC,0x9A44}, +{0x54BD,0x88F4},{0x54BE,0x9A43},{0x54C0,0x88A3},{0x54C1,0x9569}, +{0x54C2,0x9A41},{0x54C4,0x99FA},{0x54C7,0x99F5},{0x54C8,0x99FB}, +{0x54C9,0x8DC6},{0x54D8,0x9A45},{0x54E1,0x88F5},{0x54E2,0x9A4E}, +{0x54E5,0x9A46},{0x54E6,0x9A47},{0x54E8,0x8FA3},{0x54E9,0x9689}, +{0x54ED,0x9A4C},{0x54EE,0x9A4B},{0x54F2,0x934E},{0x54FA,0x9A4D}, +{0x54FD,0x9A4A},{0x54FF,0xFA94},{0x5504,0x8953},{0x5506,0x8DB4}, +{0x5507,0x904F},{0x550F,0x9A48},{0x5510,0x9382},{0x5514,0x9A49}, +{0x5516,0x88A0},{0x552E,0x9A53},{0x552F,0x9742},{0x5531,0x8FA5}, +{0x5533,0x9A59},{0x5538,0x9A58},{0x5539,0x9A4F},{0x553E,0x91C1}, +{0x5540,0x9A50},{0x5544,0x91ED},{0x5545,0x9A55},{0x5546,0x8FA4}, +{0x554C,0x9A52},{0x554F,0x96E2},{0x5553,0x8C5B},{0x5556,0x9A56}, +{0x5557,0x9A57},{0x555C,0x9A54},{0x555D,0x9A5A},{0x5563,0x9A51}, +{0x557B,0x9A60},{0x557C,0x9A65},{0x557E,0x9A61},{0x5580,0x9A5C}, +{0x5583,0x9A66},{0x5584,0x9150},{0x5586,0xFA95},{0x5587,0x9A68}, +{0x5589,0x8D41},{0x558A,0x9A5E},{0x558B,0x929D},{0x5598,0x9A62}, +{0x5599,0x9A5B},{0x559A,0x8AAB},{0x559C,0x8AEC},{0x559D,0x8A85}, +{0x559E,0x9A63},{0x559F,0x9A5F},{0x55A7,0x8C96},{0x55A8,0x9A69}, +{0x55A9,0x9A67},{0x55AA,0x9172},{0x55AB,0x8B69},{0x55AC,0x8BAA}, +{0x55AE,0x9A64},{0x55B0,0x8BF2},{0x55B6,0x8963},{0x55C4,0x9A6D}, +{0x55C5,0x9A6B},{0x55C7,0x9AA5},{0x55D4,0x9A70},{0x55DA,0x9A6A}, +{0x55DC,0x9A6E},{0x55DF,0x9A6C},{0x55E3,0x8E6B},{0x55E4,0x9A6F}, +{0x55F7,0x9A72},{0x55F9,0x9A77},{0x55FD,0x9A75},{0x55FE,0x9A74}, +{0x5606,0x9251},{0x5609,0x89C3},{0x5614,0x9A71},{0x5616,0x9A73}, +{0x5617,0x8FA6},{0x5618,0x8952},{0x561B,0x9A76},{0x5629,0x89DC}, +{0x562F,0x9A82},{0x5631,0x8FFA},{0x5632,0x9A7D},{0x5634,0x9A7B}, +{0x5636,0x9A7C},{0x5638,0x9A7E},{0x5642,0x895C},{0x564C,0x9158}, +{0x564E,0x9A78},{0x5650,0x9A79},{0x565B,0x8A9A},{0x5664,0x9A81}, +{0x5668,0x8AED},{0x566A,0x9A84},{0x566B,0x9A80},{0x566C,0x9A83}, +{0x5674,0x95AC},{0x5678,0x93D3},{0x567A,0x94B6},{0x5680,0x9A86}, +{0x5686,0x9A85},{0x5687,0x8A64},{0x568A,0x9A87},{0x568F,0x9A8A}, +{0x5694,0x9A89},{0x56A0,0x9A88},{0x56A2,0x9458},{0x56A5,0x9A8B}, +{0x56AE,0x9A8C},{0x56B4,0x9A8E},{0x56B6,0x9A8D},{0x56BC,0x9A90}, +{0x56C0,0x9A93},{0x56C1,0x9A91},{0x56C2,0x9A8F},{0x56C3,0x9A92}, +{0x56C8,0x9A94},{0x56CE,0x9A95},{0x56D1,0x9A96},{0x56D3,0x9A97}, +{0x56D7,0x9A98},{0x56D8,0x9964},{0x56DA,0x8EFA},{0x56DB,0x8E6C}, +{0x56DE,0x89F1},{0x56E0,0x88F6},{0x56E3,0x9263},{0x56EE,0x9A99}, +{0x56F0,0x8DA2},{0x56F2,0x88CD},{0x56F3,0x907D},{0x56F9,0x9A9A}, +{0x56FA,0x8CC5},{0x56FD,0x8D91},{0x56FF,0x9A9C},{0x5700,0x9A9B}, +{0x5703,0x95DE},{0x5704,0x9A9D},{0x5708,0x9A9F},{0x5709,0x9A9E}, +{0x570B,0x9AA0},{0x570D,0x9AA1},{0x570F,0x8C97},{0x5712,0x8980}, +{0x5713,0x9AA2},{0x5716,0x9AA4},{0x5718,0x9AA3},{0x571C,0x9AA6}, +{0x571F,0x9379},{0x5726,0x9AA7},{0x5727,0x88B3},{0x5728,0x8DDD}, +{0x572D,0x8C5C},{0x5730,0x926E},{0x5737,0x9AA8},{0x5738,0x9AA9}, +{0x573B,0x9AAB},{0x5740,0x9AAC},{0x5742,0x8DE2},{0x5747,0x8BCF}, +{0x574A,0x9656},{0x574E,0x9AAA},{0x574F,0x9AAD},{0x5750,0x8DBF}, +{0x5751,0x8D42},{0x5759,0xFA96},{0x5761,0x9AB1},{0x5764,0x8DA3}, +{0x5765,0xFA97},{0x5766,0x9252},{0x5769,0x9AAE},{0x576A,0x92D8}, +{0x577F,0x9AB2},{0x5782,0x9082},{0x5788,0x9AB0},{0x5789,0x9AB3}, +{0x578B,0x8C5E},{0x5793,0x9AB4},{0x57A0,0x9AB5},{0x57A2,0x8D43}, +{0x57A3,0x8A5F},{0x57A4,0x9AB7},{0x57AA,0x9AB8},{0x57AC,0xFA98}, +{0x57B0,0x9AB9},{0x57B3,0x9AB6},{0x57C0,0x9AAF},{0x57C3,0x9ABA}, +{0x57C6,0x9ABB},{0x57C7,0xFA9A},{0x57C8,0xFA99},{0x57CB,0x9684}, +{0x57CE,0x8FE9},{0x57D2,0x9ABD},{0x57D3,0x9ABE},{0x57D4,0x9ABC}, +{0x57D6,0x9AC0},{0x57DC,0x9457},{0x57DF,0x88E6},{0x57E0,0x9575}, +{0x57E3,0x9AC1},{0x57F4,0x8FFB},{0x57F7,0x8EB7},{0x57F9,0x947C}, +{0x57FA,0x8AEE},{0x57FC,0x8DE9},{0x5800,0x9678},{0x5802,0x93B0}, +{0x5805,0x8C98},{0x5806,0x91CD},{0x580A,0x9ABF},{0x580B,0x9AC2}, +{0x5815,0x91C2},{0x5819,0x9AC3},{0x581D,0x9AC4},{0x5821,0x9AC6}, +{0x5824,0x92E7},{0x582A,0x8AAC},{0x582F,0xEA9F},{0x5830,0x8981}, +{0x5831,0x95F1},{0x5834,0x8FEA},{0x5835,0x9367},{0x583A,0x8DE4}, +{0x583D,0x9ACC},{0x5840,0x95BB},{0x5841,0x97DB},{0x584A,0x89F2}, +{0x584B,0x9AC8},{0x5851,0x9159},{0x5852,0x9ACB},{0x5854,0x9383}, +{0x5857,0x9368},{0x5858,0x9384},{0x5859,0x94B7},{0x585A,0x92CB}, +{0x585E,0x8DC7},{0x5862,0x9AC7},{0x5869,0x8996},{0x586B,0x9355}, +{0x5870,0x9AC9},{0x5872,0x9AC5},{0x5875,0x906F},{0x5879,0x9ACD}, +{0x587E,0x8F6D},{0x5883,0x8BAB},{0x5885,0x9ACE},{0x5893,0x95E6}, +{0x5897,0x919D},{0x589C,0x92C4},{0x589E,0xFA9D},{0x589F,0x9AD0}, +{0x58A8,0x966E},{0x58AB,0x9AD1},{0x58AE,0x9AD6},{0x58B2,0xFA9E}, +{0x58B3,0x95AD},{0x58B8,0x9AD5},{0x58B9,0x9ACF},{0x58BA,0x9AD2}, +{0x58BB,0x9AD4},{0x58BE,0x8DA4},{0x58C1,0x95C7},{0x58C5,0x9AD7}, +{0x58C7,0x9264},{0x58CA,0x89F3},{0x58CC,0x8FEB},{0x58D1,0x9AD9}, +{0x58D3,0x9AD8},{0x58D5,0x8D88},{0x58D7,0x9ADA},{0x58D8,0x9ADC}, +{0x58D9,0x9ADB},{0x58DC,0x9ADE},{0x58DE,0x9AD3},{0x58DF,0x9AE0}, +{0x58E4,0x9ADF},{0x58E5,0x9ADD},{0x58EB,0x8E6D},{0x58EC,0x9070}, +{0x58EE,0x9173},{0x58EF,0x9AE1},{0x58F0,0x90BA},{0x58F1,0x88EB}, +{0x58F2,0x9484},{0x58F7,0x92D9},{0x58F9,0x9AE3},{0x58FA,0x9AE2}, +{0x58FB,0x9AE4},{0x58FC,0x9AE5},{0x58FD,0x9AE6},{0x5902,0x9AE7}, +{0x5909,0x95CF},{0x590A,0x9AE8},{0x590B,0xFA9F},{0x590F,0x89C4}, +{0x5910,0x9AE9},{0x5915,0x975B},{0x5916,0x8A4F},{0x5918,0x99C7}, +{0x5919,0x8F67},{0x591A,0x91BD},{0x591B,0x9AEA},{0x591C,0x96E9}, +{0x5922,0x96B2},{0x5925,0x9AEC},{0x5927,0x91E5},{0x5929,0x9356}, +{0x592A,0x91BE},{0x592B,0x9576},{0x592C,0x9AED},{0x592D,0x9AEE}, +{0x592E,0x899B},{0x5931,0x8EB8},{0x5932,0x9AEF},{0x5937,0x88CE}, +{0x5938,0x9AF0},{0x593E,0x9AF1},{0x5944,0x8982},{0x5947,0x8AEF}, +{0x5948,0x93DE},{0x5949,0x95F2},{0x594E,0x9AF5},{0x594F,0x9174}, +{0x5950,0x9AF4},{0x5951,0x8C5F},{0x5953,0xFAA0},{0x5954,0x967A}, +{0x5955,0x9AF3},{0x5957,0x9385},{0x5958,0x9AF7},{0x595A,0x9AF6}, +{0x595B,0xFAA1},{0x595D,0xFAA2},{0x5960,0x9AF9},{0x5962,0x9AF8}, +{0x5963,0xFAA3},{0x5965,0x899C},{0x5967,0x9AFA},{0x5968,0x8FA7}, +{0x5969,0x9AFC},{0x596A,0x9244},{0x596C,0x9AFB},{0x596E,0x95B1}, +{0x5973,0x8F97},{0x5974,0x937A},{0x5978,0x9B40},{0x597D,0x8D44}, +{0x5981,0x9B41},{0x5982,0x9440},{0x5983,0x94DC},{0x5984,0x96CF}, +{0x598A,0x9444},{0x598D,0x9B4A},{0x5993,0x8B57},{0x5996,0x9764}, +{0x5999,0x96AD},{0x599B,0x9BAA},{0x599D,0x9B42},{0x59A3,0x9B45}, +{0x59A4,0xFAA4},{0x59A5,0x91C3},{0x59A8,0x9657},{0x59AC,0x9369}, +{0x59B2,0x9B46},{0x59B9,0x9685},{0x59BA,0xFAA5},{0x59BB,0x8DC8}, +{0x59BE,0x8FA8},{0x59C6,0x9B47},{0x59C9,0x8E6F},{0x59CB,0x8E6E}, +{0x59D0,0x88B7},{0x59D1,0x8CC6},{0x59D3,0x90A9},{0x59D4,0x88CF}, +{0x59D9,0x9B4B},{0x59DA,0x9B4C},{0x59DC,0x9B49},{0x59E5,0x8957}, +{0x59E6,0x8AAD},{0x59E8,0x9B48},{0x59EA,0x96C3},{0x59EB,0x9550}, +{0x59F6,0x88A6},{0x59FB,0x88F7},{0x59FF,0x8E70},{0x5A01,0x88D0}, +{0x5A03,0x88A1},{0x5A09,0x9B51},{0x5A11,0x9B4F},{0x5A18,0x96BA}, +{0x5A1A,0x9B52},{0x5A1C,0x9B50},{0x5A1F,0x9B4E},{0x5A20,0x9050}, +{0x5A25,0x9B4D},{0x5A29,0x95D8},{0x5A2F,0x8CE2},{0x5A35,0x9B56}, +{0x5A36,0x9B57},{0x5A3C,0x8FA9},{0x5A40,0x9B53},{0x5A41,0x984B}, +{0x5A46,0x946B},{0x5A49,0x9B55},{0x5A5A,0x8DA5},{0x5A62,0x9B58}, +{0x5A66,0x9577},{0x5A6A,0x9B59},{0x5A6C,0x9B54},{0x5A7F,0x96B9}, +{0x5A92,0x947D},{0x5A9A,0x9B5A},{0x5A9B,0x9551},{0x5ABC,0x9B5B}, +{0x5ABD,0x9B5F},{0x5ABE,0x9B5C},{0x5AC1,0x89C5},{0x5AC2,0x9B5E}, +{0x5AC9,0x8EB9},{0x5ACB,0x9B5D},{0x5ACC,0x8C99},{0x5AD0,0x9B6B}, +{0x5AD6,0x9B64},{0x5AD7,0x9B61},{0x5AE1,0x9284},{0x5AE3,0x9B60}, +{0x5AE6,0x9B62},{0x5AE9,0x9B63},{0x5AFA,0x9B65},{0x5AFB,0x9B66}, +{0x5B09,0x8AF0},{0x5B0B,0x9B68},{0x5B0C,0x9B67},{0x5B16,0x9B69}, +{0x5B22,0x8FEC},{0x5B2A,0x9B6C},{0x5B2C,0x92DA},{0x5B30,0x8964}, +{0x5B32,0x9B6A},{0x5B36,0x9B6D},{0x5B3E,0x9B6E},{0x5B40,0x9B71}, +{0x5B43,0x9B6F},{0x5B45,0x9B70},{0x5B50,0x8E71},{0x5B51,0x9B72}, +{0x5B54,0x8D45},{0x5B55,0x9B73},{0x5B56,0xFAA6},{0x5B57,0x8E9A}, +{0x5B58,0x91B6},{0x5B5A,0x9B74},{0x5B5B,0x9B75},{0x5B5C,0x8E79}, +{0x5B5D,0x8D46},{0x5B5F,0x96D0},{0x5B63,0x8B47},{0x5B64,0x8CC7}, +{0x5B65,0x9B76},{0x5B66,0x8A77},{0x5B69,0x9B77},{0x5B6B,0x91B7}, +{0x5B70,0x9B78},{0x5B71,0x9BA1},{0x5B73,0x9B79},{0x5B75,0x9B7A}, +{0x5B78,0x9B7B},{0x5B7A,0x9B7D},{0x5B80,0x9B7E},{0x5B83,0x9B80}, +{0x5B85,0x91EE},{0x5B87,0x8946},{0x5B88,0x8EE7},{0x5B89,0x88C0}, +{0x5B8B,0x9176},{0x5B8C,0x8AAE},{0x5B8D,0x8EB3},{0x5B8F,0x8D47}, +{0x5B95,0x9386},{0x5B97,0x8F40},{0x5B98,0x8AAF},{0x5B99,0x9288}, +{0x5B9A,0x92E8},{0x5B9B,0x88B6},{0x5B9C,0x8B58},{0x5B9D,0x95F3}, +{0x5B9F,0x8EC0},{0x5BA2,0x8B71},{0x5BA3,0x90E9},{0x5BA4,0x8EBA}, +{0x5BA5,0x9747},{0x5BA6,0x9B81},{0x5BAE,0x8B7B},{0x5BB0,0x8DC9}, +{0x5BB3,0x8A51},{0x5BB4,0x8983},{0x5BB5,0x8FAA},{0x5BB6,0x89C6}, +{0x5BB8,0x9B82},{0x5BB9,0x9765},{0x5BBF,0x8F68},{0x5BC0,0xFAA7}, +{0x5BC2,0x8EE2},{0x5BC3,0x9B83},{0x5BC4,0x8AF1},{0x5BC5,0x93D0}, +{0x5BC6,0x96A7},{0x5BC7,0x9B84},{0x5BC9,0x9B85},{0x5BCC,0x9578}, +{0x5BD0,0x9B87},{0x5BD2,0x8AA6},{0x5BD3,0x8BF5},{0x5BD4,0x9B86}, +{0x5BD8,0xFAA9},{0x5BDB,0x8AB0},{0x5BDD,0x9051},{0x5BDE,0x9B8B}, +{0x5BDF,0x8E40},{0x5BE1,0x89C7},{0x5BE2,0x9B8A},{0x5BE4,0x9B88}, +{0x5BE5,0x9B8C},{0x5BE6,0x9B89},{0x5BE7,0x944A},{0x5BE8,0x9ECB}, +{0x5BE9,0x9052},{0x5BEB,0x9B8D},{0x5BEC,0xFAAA},{0x5BEE,0x97BE}, +{0x5BF0,0x9B8E},{0x5BF3,0x9B90},{0x5BF5,0x929E},{0x5BF6,0x9B8F}, +{0x5BF8,0x90A1},{0x5BFA,0x8E9B},{0x5BFE,0x91CE},{0x5BFF,0x8EF5}, +{0x5C01,0x9595},{0x5C02,0x90EA},{0x5C04,0x8ECB},{0x5C05,0x9B91}, +{0x5C06,0x8FAB},{0x5C07,0x9B92},{0x5C08,0x9B93},{0x5C09,0x88D1}, +{0x5C0A,0x91B8},{0x5C0B,0x9071},{0x5C0D,0x9B94},{0x5C0E,0x93B1}, +{0x5C0F,0x8FAC},{0x5C11,0x8FAD},{0x5C13,0x9B95},{0x5C16,0x90EB}, +{0x5C1A,0x8FAE},{0x5C1E,0xFAAB},{0x5C20,0x9B96},{0x5C22,0x9B97}, +{0x5C24,0x96DE},{0x5C28,0x9B98},{0x5C2D,0x8BC4},{0x5C31,0x8F41}, +{0x5C38,0x9B99},{0x5C39,0x9B9A},{0x5C3A,0x8EDA},{0x5C3B,0x904B}, +{0x5C3C,0x93F2},{0x5C3D,0x9073},{0x5C3E,0x94F6},{0x5C3F,0x9441}, +{0x5C40,0x8BC7},{0x5C41,0x9B9B},{0x5C45,0x8B8F},{0x5C46,0x9B9C}, +{0x5C48,0x8BFC},{0x5C4A,0x93CD},{0x5C4B,0x89AE},{0x5C4D,0x8E72}, +{0x5C4E,0x9B9D},{0x5C4F,0x9BA0},{0x5C50,0x9B9F},{0x5C51,0x8BFB}, +{0x5C53,0x9B9E},{0x5C55,0x9357},{0x5C5E,0x91AE},{0x5C60,0x936A}, +{0x5C61,0x8EC6},{0x5C64,0x9177},{0x5C65,0x979A},{0x5C6C,0x9BA2}, +{0x5C6E,0x9BA3},{0x5C6F,0x93D4},{0x5C71,0x8E52},{0x5C76,0x9BA5}, +{0x5C79,0x9BA6},{0x5C8C,0x9BA7},{0x5C90,0x8AF2},{0x5C91,0x9BA8}, +{0x5C94,0x9BA9},{0x5CA1,0x89AA},{0x5CA6,0xFAAC},{0x5CA8,0x915A}, +{0x5CA9,0x8AE2},{0x5CAB,0x9BAB},{0x5CAC,0x96A6},{0x5CB1,0x91D0}, +{0x5CB3,0x8A78},{0x5CB6,0x9BAD},{0x5CB7,0x9BAF},{0x5CB8,0x8ADD}, +{0x5CBA,0xFAAD},{0x5CBB,0x9BAC},{0x5CBC,0x9BAE},{0x5CBE,0x9BB1}, +{0x5CC5,0x9BB0},{0x5CC7,0x9BB2},{0x5CD9,0x9BB3},{0x5CE0,0x93BB}, +{0x5CE1,0x8BAC},{0x5CE8,0x89E3},{0x5CE9,0x9BB4},{0x5CEA,0x9BB9}, +{0x5CED,0x9BB7},{0x5CEF,0x95F5},{0x5CF0,0x95F4},{0x5CF5,0xFAAE}, +{0x5CF6,0x9387},{0x5CFA,0x9BB6},{0x5CFB,0x8F73},{0x5CFD,0x9BB5}, +{0x5D07,0x9092},{0x5D0B,0x9BBA},{0x5D0E,0x8DE8},{0x5D11,0x9BC0}, +{0x5D14,0x9BC1},{0x5D15,0x9BBB},{0x5D16,0x8A52},{0x5D17,0x9BBC}, +{0x5D18,0x9BC5},{0x5D19,0x9BC4},{0x5D1A,0x9BC3},{0x5D1B,0x9BBF}, +{0x5D1F,0x9BBE},{0x5D22,0x9BC2},{0x5D27,0xFAAF},{0x5D29,0x95F6}, +{0x5D42,0xFAB2},{0x5D4B,0x9BC9},{0x5D4C,0x9BC6},{0x5D4E,0x9BC8}, +{0x5D50,0x9792},{0x5D52,0x9BC7},{0x5D53,0xFAB0},{0x5D5C,0x9BBD}, +{0x5D69,0x9093},{0x5D6C,0x9BCA},{0x5D6D,0xFAB3},{0x5D6F,0x8DB5}, +{0x5D73,0x9BCB},{0x5D76,0x9BCC},{0x5D82,0x9BCF},{0x5D84,0x9BCE}, +{0x5D87,0x9BCD},{0x5D8B,0x9388},{0x5D8C,0x9BB8},{0x5D90,0x9BD5}, +{0x5D9D,0x9BD1},{0x5DA2,0x9BD0},{0x5DAC,0x9BD2},{0x5DAE,0x9BD3}, +{0x5DB7,0x9BD6},{0x5DB8,0xFAB4},{0x5DB9,0xFAB5},{0x5DBA,0x97E4}, +{0x5DBC,0x9BD7},{0x5DBD,0x9BD4},{0x5DC9,0x9BD8},{0x5DCC,0x8ADE}, +{0x5DCD,0x9BD9},{0x5DD0,0xFAB6},{0x5DD2,0x9BDB},{0x5DD3,0x9BDA}, +{0x5DD6,0x9BDC},{0x5DDB,0x9BDD},{0x5DDD,0x90EC},{0x5DDE,0x8F42}, +{0x5DE1,0x8F84},{0x5DE3,0x9183},{0x5DE5,0x8D48},{0x5DE6,0x8DB6}, +{0x5DE7,0x8D49},{0x5DE8,0x8B90},{0x5DEB,0x9BDE},{0x5DEE,0x8DB7}, +{0x5DF1,0x8CC8},{0x5DF2,0x9BDF},{0x5DF3,0x96A4},{0x5DF4,0x9462}, +{0x5DF5,0x9BE0},{0x5DF7,0x8D4A},{0x5DFB,0x8AAA},{0x5DFD,0x9246}, +{0x5DFE,0x8BD0},{0x5E02,0x8E73},{0x5E03,0x957A},{0x5E06,0x94BF}, +{0x5E0B,0x9BE1},{0x5E0C,0x8AF3},{0x5E11,0x9BE4},{0x5E16,0x929F}, +{0x5E19,0x9BE3},{0x5E1A,0x9BE2},{0x5E1B,0x9BE5},{0x5E1D,0x92E9}, +{0x5E25,0x9083},{0x5E2B,0x8E74},{0x5E2D,0x90C8},{0x5E2F,0x91D1}, +{0x5E30,0x8B41},{0x5E33,0x92A0},{0x5E36,0x9BE6},{0x5E37,0x9BE7}, +{0x5E38,0x8FED},{0x5E3D,0x9658},{0x5E40,0x9BEA},{0x5E43,0x9BE9}, +{0x5E44,0x9BE8},{0x5E45,0x959D},{0x5E47,0x9BF1},{0x5E4C,0x9679}, +{0x5E4E,0x9BEB},{0x5E54,0x9BED},{0x5E55,0x968B},{0x5E57,0x9BEC}, +{0x5E5F,0x9BEE},{0x5E61,0x94A6},{0x5E62,0x9BEF},{0x5E63,0x95BC}, +{0x5E64,0x9BF0},{0x5E72,0x8AB1},{0x5E73,0x95BD},{0x5E74,0x944E}, +{0x5E75,0x9BF2},{0x5E76,0x9BF3},{0x5E78,0x8D4B},{0x5E79,0x8AB2}, +{0x5E7A,0x9BF4},{0x5E7B,0x8CB6},{0x5E7C,0x9763},{0x5E7D,0x9748}, +{0x5E7E,0x8AF4},{0x5E7F,0x9BF6},{0x5E81,0x92A1},{0x5E83,0x8D4C}, +{0x5E84,0x8FAF},{0x5E87,0x94DD},{0x5E8A,0x8FB0},{0x5E8F,0x8F98}, +{0x5E95,0x92EA},{0x5E96,0x95F7},{0x5E97,0x9358},{0x5E9A,0x8D4D}, +{0x5E9C,0x957B},{0x5EA0,0x9BF7},{0x5EA6,0x9378},{0x5EA7,0x8DC0}, +{0x5EAB,0x8CC9},{0x5EAD,0x92EB},{0x5EB5,0x88C1},{0x5EB6,0x8F8E}, +{0x5EB7,0x8D4E},{0x5EB8,0x9766},{0x5EC1,0x9BF8},{0x5EC2,0x9BF9}, +{0x5EC3,0x9470},{0x5EC8,0x9BFA},{0x5EC9,0x97F5},{0x5ECA,0x984C}, +{0x5ECF,0x9BFC},{0x5ED0,0x9BFB},{0x5ED3,0x8A66},{0x5ED6,0x9C40}, +{0x5EDA,0x9C43},{0x5EDB,0x9C44},{0x5EDD,0x9C42},{0x5EDF,0x955F}, +{0x5EE0,0x8FB1},{0x5EE1,0x9C46},{0x5EE2,0x9C45},{0x5EE3,0x9C41}, +{0x5EE8,0x9C47},{0x5EE9,0x9C48},{0x5EEC,0x9C49},{0x5EF0,0x9C4C}, +{0x5EF1,0x9C4A},{0x5EF3,0x9C4B},{0x5EF4,0x9C4D},{0x5EF6,0x8984}, +{0x5EF7,0x92EC},{0x5EF8,0x9C4E},{0x5EFA,0x8C9A},{0x5EFB,0x89F4}, +{0x5EFC,0x9455},{0x5EFE,0x9C4F},{0x5EFF,0x93F9},{0x5F01,0x95D9}, +{0x5F03,0x9C50},{0x5F04,0x984D},{0x5F09,0x9C51},{0x5F0A,0x95BE}, +{0x5F0B,0x9C54},{0x5F0C,0x989F},{0x5F0D,0x98AF},{0x5F0F,0x8EAE}, +{0x5F10,0x93F3},{0x5F11,0x9C55},{0x5F13,0x8B7C},{0x5F14,0x92A2}, +{0x5F15,0x88F8},{0x5F16,0x9C56},{0x5F17,0x95A4},{0x5F18,0x8D4F}, +{0x5F1B,0x926F},{0x5F1F,0x92ED},{0x5F21,0xFAB7},{0x5F25,0x96ED}, +{0x5F26,0x8CB7},{0x5F27,0x8CCA},{0x5F29,0x9C57},{0x5F2D,0x9C58}, +{0x5F2F,0x9C5E},{0x5F31,0x8EE3},{0x5F34,0xFAB8},{0x5F35,0x92A3}, +{0x5F37,0x8BAD},{0x5F38,0x9C59},{0x5F3C,0x954A},{0x5F3E,0x9265}, +{0x5F41,0x9C5A},{0x5F45,0xFA67},{0x5F48,0x9C5B},{0x5F4A,0x8BAE}, +{0x5F4C,0x9C5C},{0x5F4E,0x9C5D},{0x5F51,0x9C5F},{0x5F53,0x9396}, +{0x5F56,0x9C60},{0x5F57,0x9C61},{0x5F59,0x9C62},{0x5F5C,0x9C53}, +{0x5F5D,0x9C52},{0x5F61,0x9C63},{0x5F62,0x8C60},{0x5F66,0x9546}, +{0x5F67,0xFAB9},{0x5F69,0x8DCA},{0x5F6A,0x9556},{0x5F6B,0x92A4}, +{0x5F6C,0x956A},{0x5F6D,0x9C64},{0x5F70,0x8FB2},{0x5F71,0x8965}, +{0x5F73,0x9C65},{0x5F77,0x9C66},{0x5F79,0x96F0},{0x5F7C,0x94DE}, +{0x5F7F,0x9C69},{0x5F80,0x899D},{0x5F81,0x90AA},{0x5F82,0x9C68}, +{0x5F83,0x9C67},{0x5F84,0x8C61},{0x5F85,0x91D2},{0x5F87,0x9C6D}, +{0x5F88,0x9C6B},{0x5F8A,0x9C6A},{0x5F8B,0x97A5},{0x5F8C,0x8CE3}, +{0x5F90,0x8F99},{0x5F91,0x9C6C},{0x5F92,0x936B},{0x5F93,0x8F5D}, +{0x5F97,0x93BE},{0x5F98,0x9C70},{0x5F99,0x9C6F},{0x5F9E,0x9C6E}, +{0x5FA0,0x9C71},{0x5FA1,0x8CE4},{0x5FA8,0x9C72},{0x5FA9,0x959C}, +{0x5FAA,0x8F7A},{0x5FAD,0x9C73},{0x5FAE,0x94F7},{0x5FB3,0x93BF}, +{0x5FB4,0x92A5},{0x5FB7,0xFABA},{0x5FB9,0x934F},{0x5FBC,0x9C74}, +{0x5FBD,0x8B4A},{0x5FC3,0x9053},{0x5FC5,0x954B},{0x5FCC,0x8AF5}, +{0x5FCD,0x9445},{0x5FD6,0x9C75},{0x5FD7,0x8E75},{0x5FD8,0x9659}, +{0x5FD9,0x965A},{0x5FDC,0x899E},{0x5FDD,0x9C7A},{0x5FDE,0xFABB}, +{0x5FE0,0x9289},{0x5FE4,0x9C77},{0x5FEB,0x89F5},{0x5FF0,0x9CAB}, +{0x5FF1,0x9C79},{0x5FF5,0x944F},{0x5FF8,0x9C78},{0x5FFB,0x9C76}, +{0x5FFD,0x8D9A},{0x5FFF,0x9C7C},{0x600E,0x9C83},{0x600F,0x9C89}, +{0x6010,0x9C81},{0x6012,0x937B},{0x6015,0x9C86},{0x6016,0x957C}, +{0x6019,0x9C80},{0x601B,0x9C85},{0x601C,0x97E5},{0x601D,0x8E76}, +{0x6020,0x91D3},{0x6021,0x9C7D},{0x6025,0x8B7D},{0x6026,0x9C88}, +{0x6027,0x90AB},{0x6028,0x8985},{0x6029,0x9C82},{0x602A,0x89F6}, +{0x602B,0x9C87},{0x602F,0x8BAF},{0x6031,0x9C84},{0x603A,0x9C8A}, +{0x6041,0x9C8C},{0x6042,0x9C96},{0x6043,0x9C94},{0x6046,0x9C91}, +{0x604A,0x9C90},{0x604B,0x97F6},{0x604D,0x9C92},{0x6050,0x8BB0}, +{0x6052,0x8D50},{0x6055,0x8F9A},{0x6059,0x9C99},{0x605A,0x9C8B}, +{0x605D,0xFABC},{0x605F,0x9C8F},{0x6060,0x9C7E},{0x6062,0x89F8}, +{0x6063,0x9C93},{0x6064,0x9C95},{0x6065,0x9270},{0x6068,0x8DA6}, +{0x6069,0x89B6},{0x606A,0x9C8D},{0x606B,0x9C98},{0x606C,0x9C97}, +{0x606D,0x8BB1},{0x606F,0x91A7},{0x6070,0x8A86},{0x6075,0x8C62}, +{0x6077,0x9C8E},{0x6081,0x9C9A},{0x6083,0x9C9D},{0x6084,0x9C9F}, +{0x6085,0xFABD},{0x6089,0x8EBB},{0x608A,0xFABE},{0x608B,0x9CA5}, +{0x608C,0x92EE},{0x608D,0x9C9B},{0x6092,0x9CA3},{0x6094,0x89F7}, +{0x6096,0x9CA1},{0x6097,0x9CA2},{0x609A,0x9C9E},{0x609B,0x9CA0}, +{0x609F,0x8CE5},{0x60A0,0x9749},{0x60A3,0x8AB3},{0x60A6,0x8978}, +{0x60A7,0x9CA4},{0x60A9,0x9459},{0x60AA,0x88AB},{0x60B2,0x94DF}, +{0x60B3,0x9C7B},{0x60B4,0x9CAA},{0x60B5,0x9CAE},{0x60B6,0x96E3}, +{0x60B8,0x9CA7},{0x60BC,0x9389},{0x60BD,0x9CAC},{0x60C5,0x8FEE}, +{0x60C6,0x9CAD},{0x60C7,0x93D5},{0x60D1,0x9866},{0x60D3,0x9CA9}, +{0x60D5,0xFAC0},{0x60D8,0x9CAF},{0x60DA,0x8D9B},{0x60DC,0x90C9}, +{0x60DE,0xFABF},{0x60DF,0x88D2},{0x60E0,0x9CA8},{0x60E1,0x9CA6}, +{0x60E3,0x9179},{0x60E7,0x9C9C},{0x60E8,0x8E53},{0x60F0,0x91C4}, +{0x60F1,0x9CBB},{0x60F2,0xFAC2},{0x60F3,0x917A},{0x60F4,0x9CB6}, +{0x60F6,0x9CB3},{0x60F7,0x9CB4},{0x60F9,0x8EE4},{0x60FA,0x9CB7}, +{0x60FB,0x9CBA},{0x6100,0x9CB5},{0x6101,0x8F44},{0x6103,0x9CB8}, +{0x6106,0x9CB2},{0x6108,0x96FA},{0x6109,0x96F9},{0x610D,0x9CBC}, +{0x610E,0x9CBD},{0x610F,0x88D3},{0x6111,0xFAC3},{0x6115,0x9CB1}, +{0x611A,0x8BF0},{0x611B,0x88A4},{0x611F,0x8AB4},{0x6120,0xFAC1}, +{0x6121,0x9CB9},{0x6127,0x9CC1},{0x6128,0x9CC0},{0x612C,0x9CC5}, +{0x6130,0xFAC5},{0x6134,0x9CC6},{0x6137,0xFAC4},{0x613C,0x9CC4}, +{0x613D,0x9CC7},{0x613E,0x9CBF},{0x613F,0x9CC3},{0x6142,0x9CC8}, +{0x6144,0x9CC9},{0x6147,0x9CBE},{0x6148,0x8E9C},{0x614A,0x9CC2}, +{0x614B,0x91D4},{0x614C,0x8D51},{0x614D,0x9CB0},{0x614E,0x9054}, +{0x6153,0x9CD6},{0x6155,0x95E7},{0x6158,0x9CCC},{0x6159,0x9CCD}, +{0x615A,0x9CCE},{0x615D,0x9CD5},{0x615F,0x9CD4},{0x6162,0x969D}, +{0x6163,0x8AB5},{0x6165,0x9CD2},{0x6167,0x8C64},{0x6168,0x8A53}, +{0x616B,0x9CCF},{0x616E,0x97B6},{0x616F,0x9CD1},{0x6170,0x88D4}, +{0x6171,0x9CD3},{0x6173,0x9CCA},{0x6174,0x9CD0},{0x6175,0x9CD7}, +{0x6176,0x8C63},{0x6177,0x9CCB},{0x617E,0x977C},{0x6182,0x974A}, +{0x6187,0x9CDA},{0x618A,0x9CDE},{0x618E,0x919E},{0x6190,0x97F7}, +{0x6191,0x9CDF},{0x6194,0x9CDC},{0x6196,0x9CD9},{0x6198,0xFAC6}, +{0x6199,0x9CD8},{0x619A,0x9CDD},{0x61A4,0x95AE},{0x61A7,0x93B2}, +{0x61A9,0x8C65},{0x61AB,0x9CE0},{0x61AC,0x9CDB},{0x61AE,0x9CE1}, +{0x61B2,0x8C9B},{0x61B6,0x89AF},{0x61BA,0x9CE9},{0x61BE,0x8AB6}, +{0x61C3,0x9CE7},{0x61C6,0x9CE8},{0x61C7,0x8DA7},{0x61C8,0x9CE6}, +{0x61C9,0x9CE4},{0x61CA,0x9CE3},{0x61CB,0x9CEA},{0x61CC,0x9CE2}, +{0x61CD,0x9CEC},{0x61D0,0x89F9},{0x61E3,0x9CEE},{0x61E6,0x9CED}, +{0x61F2,0x92A6},{0x61F4,0x9CF1},{0x61F6,0x9CEF},{0x61F7,0x9CE5}, +{0x61F8,0x8C9C},{0x61FA,0x9CF0},{0x61FC,0x9CF4},{0x61FD,0x9CF3}, +{0x61FE,0x9CF5},{0x61FF,0x9CF2},{0x6200,0x9CF6},{0x6208,0x9CF7}, +{0x6209,0x9CF8},{0x620A,0x95E8},{0x620C,0x9CFA},{0x620D,0x9CF9}, +{0x620E,0x8F5E},{0x6210,0x90AC},{0x6211,0x89E4},{0x6212,0x89FA}, +{0x6213,0xFAC7},{0x6214,0x9CFB},{0x6216,0x88BD},{0x621A,0x90CA}, +{0x621B,0x9CFC},{0x621D,0xE6C1},{0x621E,0x9D40},{0x621F,0x8C81}, +{0x6221,0x9D41},{0x6226,0x90ED},{0x622A,0x9D42},{0x622E,0x9D43}, +{0x622F,0x8B59},{0x6230,0x9D44},{0x6232,0x9D45},{0x6233,0x9D46}, +{0x6234,0x91D5},{0x6238,0x8CCB},{0x623B,0x96DF},{0x623F,0x965B}, +{0x6240,0x8F8A},{0x6241,0x9D47},{0x6247,0x90EE},{0x6248,0xE7BB}, +{0x6249,0x94E0},{0x624B,0x8EE8},{0x624D,0x8DCB},{0x624E,0x9D48}, +{0x6253,0x91C5},{0x6255,0x95A5},{0x6258,0x91EF},{0x625B,0x9D4B}, +{0x625E,0x9D49},{0x6260,0x9D4C},{0x6263,0x9D4A},{0x6268,0x9D4D}, +{0x626E,0x95AF},{0x6271,0x88B5},{0x6276,0x957D},{0x6279,0x94E1}, +{0x627C,0x9D4E},{0x627E,0x9D51},{0x627F,0x8FB3},{0x6280,0x8B5A}, +{0x6282,0x9D4F},{0x6283,0x9D56},{0x6284,0x8FB4},{0x6289,0x9D50}, +{0x628A,0x9463},{0x6291,0x977D},{0x6292,0x9D52},{0x6293,0x9D53}, +{0x6294,0x9D57},{0x6295,0x938A},{0x6296,0x9D54},{0x6297,0x8D52}, +{0x6298,0x90DC},{0x629B,0x9D65},{0x629C,0x94B2},{0x629E,0x91F0}, +{0x62A6,0xFAC8},{0x62AB,0x94E2},{0x62AC,0x9DAB},{0x62B1,0x95F8}, +{0x62B5,0x92EF},{0x62B9,0x9695},{0x62BB,0x9D5A},{0x62BC,0x899F}, +{0x62BD,0x928A},{0x62C2,0x9D63},{0x62C5,0x9253},{0x62C6,0x9D5D}, +{0x62C7,0x9D64},{0x62C8,0x9D5F},{0x62C9,0x9D66},{0x62CA,0x9D62}, +{0x62CC,0x9D61},{0x62CD,0x948F},{0x62CF,0x9D5B},{0x62D0,0x89FB}, +{0x62D1,0x9D59},{0x62D2,0x8B91},{0x62D3,0x91F1},{0x62D4,0x9D55}, +{0x62D7,0x9D58},{0x62D8,0x8D53},{0x62D9,0x90D9},{0x62DB,0x8FB5}, +{0x62DC,0x9D60},{0x62DD,0x9471},{0x62E0,0x8B92},{0x62E1,0x8A67}, +{0x62EC,0x8A87},{0x62ED,0x9040},{0x62EE,0x9D68},{0x62EF,0x9D6D}, +{0x62F1,0x9D69},{0x62F3,0x8C9D},{0x62F5,0x9D6E},{0x62F6,0x8E41}, +{0x62F7,0x8D89},{0x62FE,0x8F45},{0x62FF,0x9D5C},{0x6301,0x8E9D}, +{0x6302,0x9D6B},{0x6307,0x8E77},{0x6308,0x9D6C},{0x6309,0x88C2}, +{0x630C,0x9D67},{0x6311,0x92A7},{0x6319,0x8B93},{0x631F,0x8BB2}, +{0x6327,0x9D6A},{0x6328,0x88A5},{0x632B,0x8DC1},{0x632F,0x9055}, +{0x633A,0x92F0},{0x633D,0x94D2},{0x633E,0x9D70},{0x633F,0x917D}, +{0x6349,0x91A8},{0x634C,0x8E4A},{0x634D,0x9D71},{0x634F,0x9D73}, +{0x6350,0x9D6F},{0x6355,0x95DF},{0x6357,0x92BB},{0x635C,0x917B}, +{0x6367,0x95F9},{0x6368,0x8ECC},{0x6369,0x9D80},{0x636B,0x9D7E}, +{0x636E,0x9098},{0x6372,0x8C9E},{0x6376,0x9D78},{0x6377,0x8FB7}, +{0x637A,0x93E6},{0x637B,0x9450},{0x6380,0x9D76},{0x6383,0x917C}, +{0x6388,0x8EF6},{0x6389,0x9D7B},{0x638C,0x8FB6},{0x638E,0x9D75}, +{0x638F,0x9D7A},{0x6392,0x9472},{0x6396,0x9D74},{0x6398,0x8C40}, +{0x639B,0x8A7C},{0x639F,0x9D7C},{0x63A0,0x97A9},{0x63A1,0x8DCC}, +{0x63A2,0x9254},{0x63A3,0x9D79},{0x63A5,0x90DA},{0x63A7,0x8D54}, +{0x63A8,0x9084},{0x63A9,0x8986},{0x63AA,0x915B},{0x63AB,0x9D77}, +{0x63AC,0x8B64},{0x63B2,0x8C66},{0x63B4,0x92CD},{0x63B5,0x9D7D}, +{0x63BB,0x917E},{0x63BE,0x9D81},{0x63C0,0x9D83},{0x63C3,0x91B5}, +{0x63C4,0x9D89},{0x63C6,0x9D84},{0x63C9,0x9D86},{0x63CF,0x9560}, +{0x63D0,0x92F1},{0x63D2,0x9D87},{0x63D6,0x974B},{0x63DA,0x9767}, +{0x63DB,0x8AB7},{0x63E1,0x88AC},{0x63E3,0x9D85},{0x63E9,0x9D82}, +{0x63EE,0x8AF6},{0x63F4,0x8987},{0x63F5,0xFAC9},{0x63F6,0x9D88}, +{0x63FA,0x9768},{0x6406,0x9D8C},{0x640D,0x91B9},{0x640F,0x9D93}, +{0x6413,0x9D8D},{0x6416,0x9D8A},{0x6417,0x9D91},{0x641C,0x9D72}, +{0x6426,0x9D8E},{0x6428,0x9D92},{0x642C,0x94C0},{0x642D,0x938B}, +{0x6434,0x9D8B},{0x6436,0x9D8F},{0x643A,0x8C67},{0x643E,0x8DEF}, +{0x6442,0x90DB},{0x644E,0x9D97},{0x6458,0x9345},{0x6460,0xFACA}, +{0x6467,0x9D94},{0x6469,0x9680},{0x646F,0x9D95},{0x6476,0x9D96}, +{0x6478,0x96CC},{0x647A,0x90A0},{0x6483,0x8C82},{0x6488,0x9D9D}, +{0x6492,0x8E54},{0x6493,0x9D9A},{0x6495,0x9D99},{0x649A,0x9451}, +{0x649D,0xFACB},{0x649E,0x93B3},{0x64A4,0x9350},{0x64A5,0x9D9B}, +{0x64A9,0x9D9C},{0x64AB,0x958F},{0x64AD,0x9464},{0x64AE,0x8E42}, +{0x64B0,0x90EF},{0x64B2,0x966F},{0x64B9,0x8A68},{0x64BB,0x9DA3}, +{0x64BC,0x9D9E},{0x64C1,0x9769},{0x64C2,0x9DA5},{0x64C5,0x9DA1}, +{0x64C7,0x9DA2},{0x64CD,0x9180},{0x64CE,0xFACC},{0x64D2,0x9DA0}, +{0x64D4,0x9D5E},{0x64D8,0x9DA4},{0x64DA,0x9D9F},{0x64E0,0x9DA9}, +{0x64E1,0x9DAA},{0x64E2,0x9346},{0x64E3,0x9DAC},{0x64E6,0x8E43}, +{0x64E7,0x9DA7},{0x64EC,0x8B5B},{0x64EF,0x9DAD},{0x64F1,0x9DA6}, +{0x64F2,0x9DB1},{0x64F4,0x9DB0},{0x64F6,0x9DAF},{0x64FA,0x9DB2}, +{0x64FD,0x9DB4},{0x64FE,0x8FEF},{0x6500,0x9DB3},{0x6505,0x9DB7}, +{0x6518,0x9DB5},{0x651C,0x9DB6},{0x651D,0x9D90},{0x6523,0x9DB9}, +{0x6524,0x9DB8},{0x652A,0x9D98},{0x652B,0x9DBA},{0x652C,0x9DAE}, +{0x652F,0x8E78},{0x6534,0x9DBB},{0x6535,0x9DBC},{0x6536,0x9DBE}, +{0x6537,0x9DBD},{0x6538,0x9DBF},{0x6539,0x89FC},{0x653B,0x8D55}, +{0x653E,0x95FA},{0x653F,0x90AD},{0x6545,0x8CCC},{0x6548,0x9DC1}, +{0x654D,0x9DC4},{0x654E,0xFACD},{0x654F,0x9571},{0x6551,0x8B7E}, +{0x6555,0x9DC3},{0x6556,0x9DC2},{0x6557,0x9473},{0x6558,0x9DC5}, +{0x6559,0x8BB3},{0x655D,0x9DC7},{0x655E,0x9DC6},{0x6562,0x8AB8}, +{0x6563,0x8E55},{0x6566,0x93D6},{0x656C,0x8C68},{0x6570,0x9094}, +{0x6572,0x9DC8},{0x6574,0x90AE},{0x6575,0x9347},{0x6577,0x957E}, +{0x6578,0x9DC9},{0x6582,0x9DCA},{0x6583,0x9DCB},{0x6587,0x95B6}, +{0x6588,0x9B7C},{0x6589,0x90C4},{0x658C,0x956B},{0x658E,0x8DD6}, +{0x6590,0x94E3},{0x6591,0x94C1},{0x6597,0x936C},{0x6599,0x97BF}, +{0x659B,0x9DCD},{0x659C,0x8ECE},{0x659F,0x9DCE},{0x65A1,0x88B4}, +{0x65A4,0x8BD2},{0x65A5,0x90CB},{0x65A7,0x9580},{0x65AB,0x9DCF}, +{0x65AC,0x8E61},{0x65AD,0x9266},{0x65AF,0x8E7A},{0x65B0,0x9056}, +{0x65B7,0x9DD0},{0x65B9,0x95FB},{0x65BC,0x8997},{0x65BD,0x8E7B}, +{0x65C1,0x9DD3},{0x65C3,0x9DD1},{0x65C4,0x9DD4},{0x65C5,0x97B7}, +{0x65C6,0x9DD2},{0x65CB,0x90F9},{0x65CC,0x9DD5},{0x65CF,0x91B0}, +{0x65D2,0x9DD6},{0x65D7,0x8AF8},{0x65D9,0x9DD8},{0x65DB,0x9DD7}, +{0x65E0,0x9DD9},{0x65E1,0x9DDA},{0x65E2,0x8AF9},{0x65E5,0x93FA}, +{0x65E6,0x9255},{0x65E7,0x8B8C},{0x65E8,0x8E7C},{0x65E9,0x9181}, +{0x65EC,0x8F7B},{0x65ED,0x88AE},{0x65F1,0x9DDB},{0x65FA,0x89A0}, +{0x65FB,0x9DDF},{0x6600,0xFACE},{0x6602,0x8D56},{0x6603,0x9DDE}, +{0x6606,0x8DA9},{0x6607,0x8FB8},{0x6609,0xFAD1},{0x660A,0x9DDD}, +{0x660C,0x8FB9},{0x660E,0x96BE},{0x660F,0x8DA8},{0x6613,0x88D5}, +{0x6614,0x90CC},{0x6615,0xFACF},{0x661C,0x9DE4},{0x661E,0xFAD3}, +{0x661F,0x90AF},{0x6620,0x8966},{0x6624,0xFAD4},{0x6625,0x8F74}, +{0x6627,0x9686},{0x6628,0x8DF0},{0x662D,0x8FBA},{0x662E,0xFAD2}, +{0x662F,0x90A5},{0x6631,0xFA63},{0x6634,0x9DE3},{0x6635,0x9DE1}, +{0x6636,0x9DE2},{0x663B,0xFAD0},{0x663C,0x928B},{0x663F,0x9E45}, +{0x6641,0x9DE8},{0x6642,0x8E9E},{0x6643,0x8D57},{0x6644,0x9DE6}, +{0x6649,0x9DE7},{0x664B,0x9057},{0x664F,0x9DE5},{0x6652,0x8E4E}, +{0x6657,0xFAD6},{0x6659,0xFAD7},{0x665D,0x9DEA},{0x665E,0x9DE9}, +{0x665F,0x9DEE},{0x6662,0x9DEF},{0x6664,0x9DEB},{0x6665,0xFAD5}, +{0x6666,0x8A41},{0x6667,0x9DEC},{0x6668,0x9DED},{0x6669,0x94D3}, +{0x666E,0x9581},{0x666F,0x8C69},{0x6670,0x9DF0},{0x6673,0xFAD9}, +{0x6674,0x90B0},{0x6676,0x8FBB},{0x667A,0x9271},{0x6681,0x8BC5}, +{0x6683,0x9DF1},{0x6684,0x9DF5},{0x6687,0x89C9},{0x6688,0x9DF2}, +{0x6689,0x9DF4},{0x668E,0x9DF3},{0x6691,0x8F8B},{0x6696,0x9267}, +{0x6697,0x88C3},{0x6698,0x9DF6},{0x6699,0xFADA},{0x669D,0x9DF7}, +{0x66A0,0xFADB},{0x66A2,0x92A8},{0x66A6,0x97EF},{0x66AB,0x8E62}, +{0x66AE,0x95E9},{0x66B2,0xFADC},{0x66B4,0x965C},{0x66B8,0x9E41}, +{0x66B9,0x9DF9},{0x66BC,0x9DFC},{0x66BE,0x9DFB},{0x66BF,0xFADD}, +{0x66C1,0x9DF8},{0x66C4,0x9E40},{0x66C7,0x93DC},{0x66C9,0x9DFA}, +{0x66D6,0x9E42},{0x66D9,0x8F8C},{0x66DA,0x9E43},{0x66DC,0x976A}, +{0x66DD,0x9498},{0x66E0,0x9E44},{0x66E6,0x9E46},{0x66E9,0x9E47}, +{0x66F0,0x9E48},{0x66F2,0x8BC8},{0x66F3,0x8967},{0x66F4,0x8D58}, +{0x66F5,0x9E49},{0x66F7,0x9E4A},{0x66F8,0x8F91},{0x66F9,0x9182}, +{0x66FA,0xFADE},{0x66FB,0xFA66},{0x66FC,0x99D6},{0x66FD,0x915D}, +{0x66FE,0x915C},{0x66FF,0x91D6},{0x6700,0x8DC5},{0x6703,0x98F0}, +{0x6708,0x8C8E},{0x6709,0x974C},{0x670B,0x95FC},{0x670D,0x959E}, +{0x670E,0xFADF},{0x670F,0x9E4B},{0x6714,0x8DF1},{0x6715,0x92BD}, +{0x6716,0x9E4C},{0x6717,0x984E},{0x671B,0x965D},{0x671D,0x92A9}, +{0x671E,0x9E4D},{0x671F,0x8AFA},{0x6726,0x9E4E},{0x6727,0x9E4F}, +{0x6728,0x96D8},{0x672A,0x96A2},{0x672B,0x9696},{0x672C,0x967B}, +{0x672D,0x8E44},{0x672E,0x9E51},{0x6731,0x8EE9},{0x6734,0x9670}, +{0x6736,0x9E53},{0x6737,0x9E56},{0x6738,0x9E55},{0x673A,0x8AF7}, +{0x673D,0x8B80},{0x673F,0x9E52},{0x6741,0x9E54},{0x6746,0x9E57}, +{0x6749,0x9099},{0x674E,0x979B},{0x674F,0x88C7},{0x6750,0x8DDE}, +{0x6751,0x91BA},{0x6753,0x8EDB},{0x6756,0x8FF1},{0x6759,0x9E5A}, +{0x675C,0x936D},{0x675E,0x9E58},{0x675F,0x91A9},{0x6760,0x9E59}, +{0x6761,0x8FF0},{0x6762,0x96DB},{0x6763,0x9E5B},{0x6764,0x9E5C}, +{0x6765,0x9788},{0x6766,0xFAE1},{0x676A,0x9E61},{0x676D,0x8D59}, +{0x676F,0x9474},{0x6770,0x9E5E},{0x6771,0x938C},{0x6772,0x9DDC}, +{0x6773,0x9DE0},{0x6775,0x8B6E},{0x6777,0x9466},{0x677C,0x9E60}, +{0x677E,0x8FBC},{0x677F,0x94C2},{0x6785,0x9E66},{0x6787,0x94F8}, +{0x6789,0x9E5D},{0x678B,0x9E63},{0x678C,0x9E62},{0x6790,0x90CD}, +{0x6795,0x968D},{0x6797,0x97D1},{0x679A,0x9687},{0x679C,0x89CA}, +{0x679D,0x8E7D},{0x67A0,0x9867},{0x67A1,0x9E65},{0x67A2,0x9095}, +{0x67A6,0x9E64},{0x67A9,0x9E5F},{0x67AF,0x8CCD},{0x67B3,0x9E6B}, +{0x67B4,0x9E69},{0x67B6,0x89CB},{0x67B7,0x9E67},{0x67B8,0x9E6D}, +{0x67B9,0x9E73},{0x67BB,0xFAE2},{0x67C0,0xFAE4},{0x67C1,0x91C6}, +{0x67C4,0x95BF},{0x67C6,0x9E75},{0x67CA,0x9541},{0x67CE,0x9E74}, +{0x67CF,0x9490},{0x67D0,0x965E},{0x67D1,0x8AB9},{0x67D3,0x90F5}, +{0x67D4,0x8F5F},{0x67D8,0x92D1},{0x67DA,0x974D},{0x67DD,0x9E70}, +{0x67DE,0x9E6F},{0x67E2,0x9E71},{0x67E4,0x9E6E},{0x67E7,0x9E76}, +{0x67E9,0x9E6C},{0x67EC,0x9E6A},{0x67EE,0x9E72},{0x67EF,0x9E68}, +{0x67F1,0x928C},{0x67F3,0x96F6},{0x67F4,0x8EC4},{0x67F5,0x8DF2}, +{0x67FB,0x8DB8},{0x67FE,0x968F},{0x67FF,0x8A60},{0x6801,0xFAE5}, +{0x6802,0x92CC},{0x6803,0x93C8},{0x6804,0x8968},{0x6813,0x90F0}, +{0x6816,0x90B2},{0x6817,0x8C49},{0x681E,0x9E78},{0x6821,0x8D5A}, +{0x6822,0x8A9C},{0x6829,0x9E7A},{0x682A,0x8A94},{0x682B,0x9E81}, +{0x6832,0x9E7D},{0x6834,0x90F1},{0x6838,0x8A6A},{0x6839,0x8DAA}, +{0x683C,0x8A69},{0x683D,0x8DCD},{0x6840,0x9E7B},{0x6841,0x8C85}, +{0x6842,0x8C6A},{0x6843,0x938D},{0x6844,0xFAE6},{0x6846,0x9E79}, +{0x6848,0x88C4},{0x684D,0x9E7C},{0x684E,0x9E7E},{0x6850,0x8BCB}, +{0x6851,0x8C4B},{0x6852,0xFAE3},{0x6853,0x8ABA},{0x6854,0x8B6A}, +{0x6859,0x9E82},{0x685C,0x8DF7},{0x685D,0x9691},{0x685F,0x8E56}, +{0x6863,0x9E83},{0x6867,0x954F},{0x6874,0x9E8F},{0x6876,0x89B1}, +{0x6877,0x9E84},{0x687E,0x9E95},{0x687F,0x9E85},{0x6881,0x97C0}, +{0x6883,0x9E8C},{0x6885,0x947E},{0x688D,0x9E94},{0x688F,0x9E87}, +{0x6893,0x88B2},{0x6894,0x9E89},{0x6897,0x8D5B},{0x689B,0x9E8B}, +{0x689D,0x9E8A},{0x689F,0x9E86},{0x68A0,0x9E91},{0x68A2,0x8FBD}, +{0x68A6,0x9AEB},{0x68A7,0x8CE6},{0x68A8,0x979C},{0x68AD,0x9E88}, +{0x68AF,0x92F2},{0x68B0,0x8A42},{0x68B1,0x8DAB},{0x68B3,0x9E80}, +{0x68B5,0x9E90},{0x68B6,0x8A81},{0x68B9,0x9E8E},{0x68BA,0x9E92}, +{0x68BC,0x938E},{0x68C4,0x8AFC},{0x68C6,0x9EB0},{0x68C8,0xFA64}, +{0x68C9,0x96C7},{0x68CA,0x9E97},{0x68CB,0x8AFB},{0x68CD,0x9E9E}, +{0x68CF,0xFAE7},{0x68D2,0x965F},{0x68D4,0x9E9F},{0x68D5,0x9EA1}, +{0x68D7,0x9EA5},{0x68D8,0x9E99},{0x68DA,0x9249},{0x68DF,0x938F}, +{0x68E0,0x9EA9},{0x68E1,0x9E9C},{0x68E3,0x9EA6},{0x68E7,0x9EA0}, +{0x68EE,0x9058},{0x68EF,0x9EAA},{0x68F2,0x90B1},{0x68F9,0x9EA8}, +{0x68FA,0x8ABB},{0x6900,0x986F},{0x6901,0x9E96},{0x6904,0x9EA4}, +{0x6905,0x88D6},{0x6908,0x9E98},{0x690B,0x96B8},{0x690C,0x9E9D}, +{0x690D,0x9041},{0x690E,0x92C5},{0x690F,0x9E93},{0x6912,0x9EA3}, +{0x6919,0x909A},{0x691A,0x9EAD},{0x691B,0x8A91},{0x691C,0x8C9F}, +{0x6921,0x9EAF},{0x6922,0x9E9A},{0x6923,0x9EAE},{0x6925,0x9EA7}, +{0x6926,0x9E9B},{0x6928,0x9EAB},{0x692A,0x9EAC},{0x6930,0x9EBD}, +{0x6934,0x93CC},{0x6936,0x9EA2},{0x6939,0x9EB9},{0x693D,0x9EBB}, +{0x693F,0x92D6},{0x694A,0x976B},{0x6953,0x9596},{0x6954,0x9EB6}, +{0x6955,0x91C8},{0x6959,0x9EBC},{0x695A,0x915E},{0x695C,0x9EB3}, +{0x695D,0x9EC0},{0x695E,0x9EBF},{0x6960,0x93ED},{0x6961,0x9EBE}, +{0x6962,0x93E8},{0x6968,0xFAE9},{0x696A,0x9EC2},{0x696B,0x9EB5}, +{0x696D,0x8BC6},{0x696E,0x9EB8},{0x696F,0x8F7C},{0x6973,0x9480}, +{0x6974,0x9EBA},{0x6975,0x8BC9},{0x6977,0x9EB2},{0x6978,0x9EB4}, +{0x6979,0x9EB1},{0x697C,0x984F},{0x697D,0x8A79},{0x697E,0x9EB7}, +{0x6981,0x9EC1},{0x6982,0x8A54},{0x698A,0x8DE5},{0x698E,0x897C}, +{0x6991,0x9ED2},{0x6994,0x9850},{0x6995,0x9ED5},{0x6998,0xFAEB}, +{0x699B,0x9059},{0x699C,0x9ED4},{0x69A0,0x9ED3},{0x69A7,0x9ED0}, +{0x69AE,0x9EC4},{0x69B1,0x9EE1},{0x69B2,0x9EC3},{0x69B4,0x9ED6}, +{0x69BB,0x9ECE},{0x69BE,0x9EC9},{0x69BF,0x9EC6},{0x69C1,0x9EC7}, +{0x69C3,0x9ECF},{0x69C7,0xEAA0},{0x69CA,0x9ECC},{0x69CB,0x8D5C}, +{0x69CC,0x92C6},{0x69CD,0x9184},{0x69CE,0x9ECA},{0x69D0,0x9EC5}, +{0x69D3,0x9EC8},{0x69D8,0x976C},{0x69D9,0x968A},{0x69DD,0x9ECD}, +{0x69DE,0x9ED7},{0x69E2,0xFAEC},{0x69E7,0x9EDF},{0x69E8,0x9ED8}, +{0x69EB,0x9EE5},{0x69ED,0x9EE3},{0x69F2,0x9EDE},{0x69F9,0x9EDD}, +{0x69FB,0x92CE},{0x69FD,0x9185},{0x69FF,0x9EDB},{0x6A02,0x9ED9}, +{0x6A05,0x9EE0},{0x6A0A,0x9EE6},{0x6A0B,0x94F3},{0x6A0C,0x9EEC}, +{0x6A12,0x9EE7},{0x6A13,0x9EEA},{0x6A14,0x9EE4},{0x6A17,0x9294}, +{0x6A19,0x9557},{0x6A1B,0x9EDA},{0x6A1E,0x9EE2},{0x6A1F,0x8FBE}, +{0x6A21,0x96CD},{0x6A22,0x9EF6},{0x6A23,0x9EE9},{0x6A29,0x8CA0}, +{0x6A2A,0x89A1},{0x6A2B,0x8A7E},{0x6A2E,0x9ED1},{0x6A30,0xFAED}, +{0x6A35,0x8FBF},{0x6A36,0x9EEE},{0x6A38,0x9EF5},{0x6A39,0x8EF7}, +{0x6A3A,0x8A92},{0x6A3D,0x924D},{0x6A44,0x9EEB},{0x6A46,0xFAEF}, +{0x6A47,0x9EF0},{0x6A48,0x9EF4},{0x6A4B,0x8BB4},{0x6A58,0x8B6B}, +{0x6A59,0x9EF2},{0x6A5F,0x8B40},{0x6A61,0x93C9},{0x6A62,0x9EF1}, +{0x6A66,0x9EF3},{0x6A6B,0xFAEE},{0x6A72,0x9EED},{0x6A73,0xFAF0}, +{0x6A78,0x9EEF},{0x6A7E,0xFAF1},{0x6A7F,0x8A80},{0x6A80,0x9268}, +{0x6A84,0x9EFA},{0x6A8D,0x9EF8},{0x6A8E,0x8CE7},{0x6A90,0x9EF7}, +{0x6A97,0x9F40},{0x6A9C,0x9E77},{0x6AA0,0x9EF9},{0x6AA2,0x9EFB}, +{0x6AA3,0x9EFC},{0x6AAA,0x9F4B},{0x6AAC,0x9F47},{0x6AAE,0x9E8D}, +{0x6AB3,0x9F46},{0x6AB8,0x9F45},{0x6ABB,0x9F42},{0x6AC1,0x9EE8}, +{0x6AC2,0x9F44},{0x6AC3,0x9F43},{0x6AD1,0x9F49},{0x6AD3,0x9845}, +{0x6ADA,0x9F4C},{0x6ADB,0x8BF9},{0x6ADE,0x9F48},{0x6ADF,0x9F4A}, +{0x6AE2,0xFAF2},{0x6AE4,0xFAF3},{0x6AE8,0x94A5},{0x6AEA,0x9F4D}, +{0x6AFA,0x9F51},{0x6AFB,0x9F4E},{0x6B04,0x9793},{0x6B05,0x9F4F}, +{0x6B0A,0x9EDC},{0x6B12,0x9F52},{0x6B16,0x9F53},{0x6B1D,0x8954}, +{0x6B1F,0x9F55},{0x6B20,0x8C87},{0x6B21,0x8E9F},{0x6B23,0x8BD3}, +{0x6B27,0x89A2},{0x6B32,0x977E},{0x6B37,0x9F57},{0x6B38,0x9F56}, +{0x6B39,0x9F59},{0x6B3A,0x8B5C},{0x6B3D,0x8BD4},{0x6B3E,0x8ABC}, +{0x6B43,0x9F5C},{0x6B47,0x9F5B},{0x6B49,0x9F5D},{0x6B4C,0x89CC}, +{0x6B4E,0x9256},{0x6B50,0x9F5E},{0x6B53,0x8ABD},{0x6B54,0x9F60}, +{0x6B59,0x9F5F},{0x6B5B,0x9F61},{0x6B5F,0x9F62},{0x6B61,0x9F63}, +{0x6B62,0x8E7E},{0x6B63,0x90B3},{0x6B64,0x8D9F},{0x6B66,0x9590}, +{0x6B69,0x95E0},{0x6B6A,0x9863},{0x6B6F,0x8E95},{0x6B73,0x8DCE}, +{0x6B74,0x97F0},{0x6B78,0x9F64},{0x6B79,0x9F65},{0x6B7B,0x8E80}, +{0x6B7F,0x9F66},{0x6B80,0x9F67},{0x6B83,0x9F69},{0x6B84,0x9F68}, +{0x6B86,0x9677},{0x6B89,0x8F7D},{0x6B8A,0x8EEA},{0x6B8B,0x8E63}, +{0x6B8D,0x9F6A},{0x6B95,0x9F6C},{0x6B96,0x9042},{0x6B98,0x9F6B}, +{0x6B9E,0x9F6D},{0x6BA4,0x9F6E},{0x6BAA,0x9F6F},{0x6BAB,0x9F70}, +{0x6BAF,0x9F71},{0x6BB1,0x9F73},{0x6BB2,0x9F72},{0x6BB3,0x9F74}, +{0x6BB4,0x89A3},{0x6BB5,0x9269},{0x6BB7,0x9F75},{0x6BBA,0x8E45}, +{0x6BBB,0x8A6B},{0x6BBC,0x9F76},{0x6BBF,0x9361},{0x6BC0,0x9ACA}, +{0x6BC5,0x8B42},{0x6BC6,0x9F77},{0x6BCB,0x9F78},{0x6BCD,0x95EA}, +{0x6BCE,0x9688},{0x6BD2,0x93C5},{0x6BD3,0x9F79},{0x6BD4,0x94E4}, +{0x6BD6,0xFAF4},{0x6BD8,0x94F9},{0x6BDB,0x96D1},{0x6BDF,0x9F7A}, +{0x6BEB,0x9F7C},{0x6BEC,0x9F7B},{0x6BEF,0x9F7E},{0x6BF3,0x9F7D}, +{0x6C08,0x9F81},{0x6C0F,0x8E81},{0x6C11,0x96AF},{0x6C13,0x9F82}, +{0x6C14,0x9F83},{0x6C17,0x8B43},{0x6C1B,0x9F84},{0x6C23,0x9F86}, +{0x6C24,0x9F85},{0x6C34,0x9085},{0x6C37,0x9558},{0x6C38,0x8969}, +{0x6C3E,0x94C3},{0x6C3F,0xFAF5},{0x6C40,0x92F3},{0x6C41,0x8F60}, +{0x6C42,0x8B81},{0x6C4E,0x94C4},{0x6C50,0x8EAC},{0x6C55,0x9F88}, +{0x6C57,0x8ABE},{0x6C5A,0x8998},{0x6C5C,0xFAF6},{0x6C5D,0x93F0}, +{0x6C5E,0x9F87},{0x6C5F,0x8D5D},{0x6C60,0x9272},{0x6C62,0x9F89}, +{0x6C68,0x9F91},{0x6C6A,0x9F8A},{0x6C6F,0xFAF8},{0x6C70,0x91BF}, +{0x6C72,0x8B82},{0x6C73,0x9F92},{0x6C7A,0x8C88},{0x6C7D,0x8B44}, +{0x6C7E,0x9F90},{0x6C81,0x9F8E},{0x6C82,0x9F8B},{0x6C83,0x9780}, +{0x6C86,0xFAF7},{0x6C88,0x92BE},{0x6C8C,0x93D7},{0x6C8D,0x9F8C}, +{0x6C90,0x9F94},{0x6C92,0x9F93},{0x6C93,0x8C42},{0x6C96,0x89AB}, +{0x6C99,0x8DB9},{0x6C9A,0x9F8D},{0x6C9B,0x9F8F},{0x6CA1,0x9676}, +{0x6CA2,0x91F2},{0x6CAB,0x9697},{0x6CAE,0x9F9C},{0x6CB1,0x9F9D}, +{0x6CB3,0x89CD},{0x6CB8,0x95A6},{0x6CB9,0x96FB},{0x6CBA,0x9F9F}, +{0x6CBB,0x8EA1},{0x6CBC,0x8FC0},{0x6CBD,0x9F98},{0x6CBE,0x9F9E}, +{0x6CBF,0x8988},{0x6CC1,0x8BB5},{0x6CC4,0x9F95},{0x6CC5,0x9F9A}, +{0x6CC9,0x90F2},{0x6CCA,0x9491},{0x6CCC,0x94E5},{0x6CD3,0x9F97}, +{0x6CD5,0x9640},{0x6CD7,0x9F99},{0x6CD9,0x9FA2},{0x6CDA,0xFAF9}, +{0x6CDB,0x9FA0},{0x6CDD,0x9F9B},{0x6CE1,0x9641},{0x6CE2,0x9467}, +{0x6CE3,0x8B83},{0x6CE5,0x9344},{0x6CE8,0x928D},{0x6CEA,0x9FA3}, +{0x6CEF,0x9FA1},{0x6CF0,0x91D7},{0x6CF1,0x9F96},{0x6CF3,0x896A}, +{0x6D04,0xFAFA},{0x6D0B,0x976D},{0x6D0C,0x9FAE},{0x6D12,0x9FAD}, +{0x6D17,0x90F4},{0x6D19,0x9FAA},{0x6D1B,0x978C},{0x6D1E,0x93B4}, +{0x6D1F,0x9FA4},{0x6D25,0x92C3},{0x6D29,0x896B},{0x6D2A,0x8D5E}, +{0x6D2B,0x9FA7},{0x6D32,0x8F46},{0x6D33,0x9FAC},{0x6D35,0x9FAB}, +{0x6D36,0x9FA6},{0x6D38,0x9FA9},{0x6D3B,0x8A88},{0x6D3D,0x9FA8}, +{0x6D3E,0x9468},{0x6D41,0x97AC},{0x6D44,0x8FF2},{0x6D45,0x90F3}, +{0x6D59,0x9FB4},{0x6D5A,0x9FB2},{0x6D5C,0x956C},{0x6D63,0x9FAF}, +{0x6D64,0x9FB1},{0x6D66,0x8959},{0x6D69,0x8D5F},{0x6D6A,0x9851}, +{0x6D6C,0x8A5C},{0x6D6E,0x9582},{0x6D6F,0xFAFC},{0x6D74,0x9781}, +{0x6D77,0x8A43},{0x6D78,0x905A},{0x6D79,0x9FB3},{0x6D85,0x9FB8}, +{0x6D87,0xFAFB},{0x6D88,0x8FC1},{0x6D8C,0x974F},{0x6D8E,0x9FB5}, +{0x6D93,0x9FB0},{0x6D95,0x9FB6},{0x6D96,0xFB40},{0x6D99,0x97DC}, +{0x6D9B,0x9393},{0x6D9C,0x93C0},{0x6DAC,0xFB41},{0x6DAF,0x8A55}, +{0x6DB2,0x8974},{0x6DB5,0x9FBC},{0x6DB8,0x9FBF},{0x6DBC,0x97C1}, +{0x6DC0,0x9784},{0x6DC5,0x9FC6},{0x6DC6,0x9FC0},{0x6DC7,0x9FBD}, +{0x6DCB,0x97D2},{0x6DCC,0x9FC3},{0x6DCF,0xFB42},{0x6DD1,0x8F69}, +{0x6DD2,0x9FC5},{0x6DD5,0x9FCA},{0x6DD8,0x9391},{0x6DD9,0x9FC8}, +{0x6DDE,0x9FC2},{0x6DE1,0x9257},{0x6DE4,0x9FC9},{0x6DE6,0x9FBE}, +{0x6DE8,0x9FC4},{0x6DEA,0x9FCB},{0x6DEB,0x88FA},{0x6DEC,0x9FC1}, +{0x6DEE,0x9FCC},{0x6DF1,0x905B},{0x6DF2,0xFB44},{0x6DF3,0x8F7E}, +{0x6DF5,0x95A3},{0x6DF7,0x8DAC},{0x6DF8,0xFB43},{0x6DF9,0x9FB9}, +{0x6DFA,0x9FC7},{0x6DFB,0x9359},{0x6DFC,0xFB45},{0x6E05,0x90B4}, +{0x6E07,0x8A89},{0x6E08,0x8DCF},{0x6E09,0x8FC2},{0x6E0A,0x9FBB}, +{0x6E0B,0x8F61},{0x6E13,0x8C6B},{0x6E15,0x9FBA},{0x6E19,0x9FD0}, +{0x6E1A,0x8F8D},{0x6E1B,0x8CB8},{0x6E1D,0x9FDF},{0x6E1F,0x9FD9}, +{0x6E20,0x8B94},{0x6E21,0x936E},{0x6E23,0x9FD4},{0x6E24,0x9FDD}, +{0x6E25,0x88AD},{0x6E26,0x8951},{0x6E27,0xFB48},{0x6E29,0x89B7}, +{0x6E2B,0x9FD6},{0x6E2C,0x91AA},{0x6E2D,0x9FCD},{0x6E2E,0x9FCF}, +{0x6E2F,0x8D60},{0x6E38,0x9FE0},{0x6E39,0xFB46},{0x6E3A,0x9FDB}, +{0x6E3C,0xFB49},{0x6E3E,0x9FD3},{0x6E43,0x9FDA},{0x6E4A,0x96A9}, +{0x6E4D,0x9FD8},{0x6E4E,0x9FDC},{0x6E56,0x8CCE},{0x6E58,0x8FC3}, +{0x6E5B,0x9258},{0x6E5C,0xFB47},{0x6E5F,0x9FD2},{0x6E67,0x974E}, +{0x6E6B,0x9FD5},{0x6E6E,0x9FCE},{0x6E6F,0x9392},{0x6E72,0x9FD1}, +{0x6E76,0x9FD7},{0x6E7E,0x9870},{0x6E7F,0x8EBC},{0x6E80,0x969E}, +{0x6E82,0x9FE1},{0x6E8C,0x94AC},{0x6E8F,0x9FED},{0x6E90,0x8CB9}, +{0x6E96,0x8F80},{0x6E98,0x9FE3},{0x6E9C,0x97AD},{0x6E9D,0x8D61}, +{0x6E9F,0x9FF0},{0x6EA2,0x88EC},{0x6EA5,0x9FEE},{0x6EAA,0x9FE2}, +{0x6EAF,0x9FE8},{0x6EB2,0x9FEA},{0x6EB6,0x976E},{0x6EB7,0x9FE5}, +{0x6EBA,0x934D},{0x6EBD,0x9FE7},{0x6EBF,0xFB4A},{0x6EC2,0x9FEF}, +{0x6EC4,0x9FE9},{0x6EC5,0x96C5},{0x6EC9,0x9FE4},{0x6ECB,0x8EA0}, +{0x6ECC,0x9FFC},{0x6ED1,0x8A8A},{0x6ED3,0x9FE6},{0x6ED4,0x9FEB}, +{0x6ED5,0x9FEC},{0x6EDD,0x91EA},{0x6EDE,0x91D8},{0x6EEC,0x9FF4}, +{0x6EEF,0x9FFA},{0x6EF2,0x9FF8},{0x6EF4,0x9348},{0x6EF7,0xE042}, +{0x6EF8,0x9FF5},{0x6EFE,0x9FF6},{0x6EFF,0x9FDE},{0x6F01,0x8B99}, +{0x6F02,0x9559},{0x6F06,0x8EBD},{0x6F09,0x8D97},{0x6F0F,0x9852}, +{0x6F11,0x9FF2},{0x6F13,0xE041},{0x6F14,0x8989},{0x6F15,0x9186}, +{0x6F20,0x9499},{0x6F22,0x8ABF},{0x6F23,0x97F8},{0x6F2B,0x969F}, +{0x6F2C,0x92D0},{0x6F31,0x9FF9},{0x6F32,0x9FFB},{0x6F38,0x9151}, +{0x6F3E,0xE040},{0x6F3F,0x9FF7},{0x6F41,0x9FF1},{0x6F45,0x8AC1}, +{0x6F54,0x8C89},{0x6F58,0xE04E},{0x6F5B,0xE049},{0x6F5C,0x90F6}, +{0x6F5F,0x8A83},{0x6F64,0x8F81},{0x6F66,0xE052},{0x6F6D,0xE04B}, +{0x6F6E,0x92AA},{0x6F6F,0xE048},{0x6F70,0x92D7},{0x6F74,0xE06B}, +{0x6F78,0xE045},{0x6F7A,0xE044},{0x6F7C,0xE04D},{0x6F80,0xE047}, +{0x6F81,0xE046},{0x6F82,0xE04C},{0x6F84,0x909F},{0x6F86,0xE043}, +{0x6F88,0xFB4B},{0x6F8E,0xE04F},{0x6F91,0xE050},{0x6F97,0x8AC0}, +{0x6FA1,0xE055},{0x6FA3,0xE054},{0x6FA4,0xE056},{0x6FAA,0xE059}, +{0x6FB1,0x9362},{0x6FB3,0xE053},{0x6FB5,0xFB4C},{0x6FB9,0xE057}, +{0x6FC0,0x8C83},{0x6FC1,0x91F7},{0x6FC2,0xE051},{0x6FC3,0x945A}, +{0x6FC6,0xE058},{0x6FD4,0xE05D},{0x6FD5,0xE05B},{0x6FD8,0xE05E}, +{0x6FDB,0xE061},{0x6FDF,0xE05A},{0x6FE0,0x8D8A},{0x6FE1,0x9447}, +{0x6FE4,0x9FB7},{0x6FEB,0x9794},{0x6FEC,0xE05C},{0x6FEE,0xE060}, +{0x6FEF,0x91F3},{0x6FF1,0xE05F},{0x6FF3,0xE04A},{0x6FF5,0xFB4D}, +{0x6FF6,0xE889},{0x6FFA,0xE064},{0x6FFE,0xE068},{0x7001,0xE066}, +{0x7005,0xFB4E},{0x7007,0xFB4F},{0x7009,0xE062},{0x700B,0xE063}, +{0x700F,0xE067},{0x7011,0xE065},{0x7015,0x956D},{0x7018,0xE06D}, +{0x701A,0xE06A},{0x701B,0xE069},{0x701D,0xE06C},{0x701E,0x93D2}, +{0x701F,0xE06E},{0x7026,0x9295},{0x7027,0x91EB},{0x7028,0xFB50}, +{0x702C,0x90A3},{0x7030,0xE06F},{0x7032,0xE071},{0x703E,0xE070}, +{0x704C,0x9FF3},{0x7051,0xE072},{0x7058,0x93E5},{0x7063,0xE073}, +{0x706B,0x89CE},{0x706F,0x9394},{0x7070,0x8A44},{0x7078,0x8B84}, +{0x707C,0x8EDC},{0x707D,0x8DD0},{0x7085,0xFB51},{0x7089,0x9846}, +{0x708A,0x9086},{0x708E,0x898A},{0x7092,0xE075},{0x7099,0xE074}, +{0x70AB,0xFB52},{0x70AC,0xE078},{0x70AD,0x9259},{0x70AE,0xE07B}, +{0x70AF,0xE076},{0x70B3,0xE07A},{0x70B8,0xE079},{0x70B9,0x935F}, +{0x70BA,0x88D7},{0x70BB,0xFA62},{0x70C8,0x97F3},{0x70CB,0xE07D}, +{0x70CF,0x8947},{0x70D9,0xE080},{0x70DD,0xE07E},{0x70DF,0xE07C}, +{0x70F1,0xE077},{0x70F9,0x9642},{0x70FD,0xE082},{0x7104,0xFB54}, +{0x7109,0xE081},{0x710F,0xFB53},{0x7114,0x898B},{0x7119,0xE084}, +{0x711A,0x95B0},{0x711C,0xE083},{0x7121,0x96B3},{0x7126,0x8FC5}, +{0x7136,0x9152},{0x713C,0x8FC4},{0x7146,0xFB56},{0x7147,0xFB57}, +{0x7149,0x97F9},{0x714C,0xE08A},{0x714E,0x90F7},{0x7155,0xE086}, +{0x7156,0xE08B},{0x7159,0x898C},{0x715C,0xFB55},{0x7162,0xE089}, +{0x7164,0x9481},{0x7165,0xE085},{0x7166,0xE088},{0x7167,0x8FC6}, +{0x7169,0x94CF},{0x716C,0xE08C},{0x716E,0x8ECF},{0x717D,0x90F8}, +{0x7184,0xE08F},{0x7188,0xE087},{0x718A,0x8C46},{0x718F,0xE08D}, +{0x7194,0x976F},{0x7195,0xE090},{0x7199,0xEAA4},{0x719F,0x8F6E}, +{0x71A8,0xE091},{0x71AC,0xE092},{0x71B1,0x944D},{0x71B9,0xE094}, +{0x71BE,0xE095},{0x71C1,0xFB59},{0x71C3,0x9452},{0x71C8,0x9395}, +{0x71C9,0xE097},{0x71CE,0xE099},{0x71D0,0x97D3},{0x71D2,0xE096}, +{0x71D4,0xE098},{0x71D5,0x898D},{0x71D7,0xE093},{0x71DF,0x9A7A}, +{0x71E0,0xE09A},{0x71E5,0x9187},{0x71E6,0x8E57},{0x71E7,0xE09C}, +{0x71EC,0xE09B},{0x71ED,0x9043},{0x71EE,0x99D7},{0x71F5,0xE09D}, +{0x71F9,0xE09F},{0x71FB,0xE08E},{0x71FC,0xE09E},{0x71FE,0xFB5A}, +{0x71FF,0xE0A0},{0x7206,0x949A},{0x720D,0xE0A1},{0x7210,0xE0A2}, +{0x721B,0xE0A3},{0x7228,0xE0A4},{0x722A,0x92DC},{0x722C,0xE0A6}, +{0x722D,0xE0A5},{0x7230,0xE0A7},{0x7232,0xE0A8},{0x7235,0x8EDD}, +{0x7236,0x9583},{0x723A,0x96EA},{0x723B,0xE0A9},{0x723C,0xE0AA}, +{0x723D,0x9175},{0x723E,0x8EA2},{0x723F,0xE0AB},{0x7240,0xE0AC}, +{0x7246,0xE0AD},{0x7247,0x95D0},{0x7248,0x94C5},{0x724B,0xE0AE}, +{0x724C,0x9476},{0x7252,0x92AB},{0x7258,0xE0AF},{0x7259,0x89E5}, +{0x725B,0x8B8D},{0x725D,0x96C4},{0x725F,0x96B4},{0x7261,0x89B2}, +{0x7262,0x9853},{0x7267,0x9671},{0x7269,0x95A8},{0x7272,0x90B5}, +{0x7274,0xE0B0},{0x7279,0x93C1},{0x727D,0x8CA1},{0x727E,0xE0B1}, +{0x7280,0x8DD2},{0x7281,0xE0B3},{0x7282,0xE0B2},{0x7287,0xE0B4}, +{0x7292,0xE0B5},{0x7296,0xE0B6},{0x72A0,0x8B5D},{0x72A2,0xE0B7}, +{0x72A7,0xE0B8},{0x72AC,0x8CA2},{0x72AF,0x94C6},{0x72B1,0xFB5B}, +{0x72B2,0xE0BA},{0x72B6,0x8FF3},{0x72B9,0xE0B9},{0x72BE,0xFB5C}, +{0x72C2,0x8BB6},{0x72C3,0xE0BB},{0x72C4,0xE0BD},{0x72C6,0xE0BC}, +{0x72CE,0xE0BE},{0x72D0,0x8CCF},{0x72D2,0xE0BF},{0x72D7,0x8BE7}, +{0x72D9,0x915F},{0x72DB,0x8D9D},{0x72E0,0xE0C1},{0x72E1,0xE0C2}, +{0x72E2,0xE0C0},{0x72E9,0x8EEB},{0x72EC,0x93C6},{0x72ED,0x8BB7}, +{0x72F7,0xE0C4},{0x72F8,0x924B},{0x72F9,0xE0C3},{0x72FC,0x9854}, +{0x72FD,0x9482},{0x730A,0xE0C7},{0x7316,0xE0C9},{0x7317,0xE0C6}, +{0x731B,0x96D2},{0x731C,0xE0C8},{0x731D,0xE0CA},{0x731F,0x97C2}, +{0x7324,0xFB5D},{0x7325,0xE0CE},{0x7329,0xE0CD},{0x732A,0x9296}, +{0x732B,0x944C},{0x732E,0x8CA3},{0x732F,0xE0CC},{0x7334,0xE0CB}, +{0x7336,0x9750},{0x7337,0x9751},{0x733E,0xE0CF},{0x733F,0x898E}, +{0x7344,0x8D96},{0x7345,0x8E82},{0x734E,0xE0D0},{0x734F,0xE0D1}, +{0x7357,0xE0D3},{0x7363,0x8F62},{0x7368,0xE0D5},{0x736A,0xE0D4}, +{0x7370,0xE0D6},{0x7372,0x8A6C},{0x7375,0xE0D8},{0x7377,0xFB5F}, +{0x7378,0xE0D7},{0x737A,0xE0DA},{0x737B,0xE0D9},{0x7384,0x8CBA}, +{0x7387,0x97A6},{0x7389,0x8BCA},{0x738B,0x89A4},{0x7396,0x8BE8}, +{0x73A9,0x8ADF},{0x73B2,0x97E6},{0x73B3,0xE0DC},{0x73BB,0xE0DE}, +{0x73BD,0xFB60},{0x73C0,0xE0DF},{0x73C2,0x89CF},{0x73C8,0xE0DB}, +{0x73C9,0xFB61},{0x73CA,0x8E58},{0x73CD,0x92BF},{0x73CE,0xE0DD}, +{0x73D2,0xFB64},{0x73D6,0xFB62},{0x73DE,0xE0E2},{0x73E0,0x8EEC}, +{0x73E3,0xFB63},{0x73E5,0xE0E0},{0x73EA,0x8C5D},{0x73ED,0x94C7}, +{0x73EE,0xE0E1},{0x73F1,0xE0FC},{0x73F5,0xFB66},{0x73F8,0xE0E7}, +{0x73FE,0x8CBB},{0x7403,0x8B85},{0x7405,0xE0E4},{0x7406,0x979D}, +{0x7407,0xFB65},{0x7409,0x97AE},{0x7422,0x91F4},{0x7425,0xE0E6}, +{0x7426,0xFB67},{0x7429,0xFB69},{0x742A,0xFB68},{0x742E,0xFB6A}, +{0x7432,0xE0E8},{0x7433,0x97D4},{0x7434,0x8BD5},{0x7435,0x94FA}, +{0x7436,0x9469},{0x743A,0xE0E9},{0x743F,0xE0EB},{0x7441,0xE0EE}, +{0x7455,0xE0EA},{0x7459,0xE0ED},{0x745A,0x8CE8},{0x745B,0x896C}, +{0x745C,0xE0EF},{0x745E,0x9090},{0x745F,0xE0EC},{0x7460,0x97DA}, +{0x7462,0xFB6B},{0x7463,0xE0F2},{0x7464,0xEAA2},{0x7469,0xE0F0}, +{0x746A,0xE0F3},{0x746F,0xE0E5},{0x7470,0xE0F1},{0x7473,0x8DBA}, +{0x7476,0xE0F4},{0x747E,0xE0F5},{0x7483,0x979E},{0x7489,0xFB6C}, +{0x748B,0xE0F6},{0x749E,0xE0F7},{0x749F,0xFB6D},{0x74A2,0xE0E3}, +{0x74A7,0xE0F8},{0x74B0,0x8AC2},{0x74BD,0x8EA3},{0x74CA,0xE0F9}, +{0x74CF,0xE0FA},{0x74D4,0xE0FB},{0x74DC,0x895A},{0x74E0,0xE140}, +{0x74E2,0x955A},{0x74E3,0xE141},{0x74E6,0x8AA2},{0x74E7,0xE142}, +{0x74E9,0xE143},{0x74EE,0xE144},{0x74F0,0xE146},{0x74F1,0xE147}, +{0x74F2,0xE145},{0x74F6,0x9572},{0x74F7,0xE149},{0x74F8,0xE148}, +{0x7501,0xFB6E},{0x7503,0xE14B},{0x7504,0xE14A},{0x7505,0xE14C}, +{0x750C,0xE14D},{0x750D,0xE14F},{0x750E,0xE14E},{0x7511,0x8D99}, +{0x7513,0xE151},{0x7515,0xE150},{0x7518,0x8AC3},{0x751A,0x9072}, +{0x751C,0x935B},{0x751E,0xE152},{0x751F,0x90B6},{0x7523,0x8E59}, +{0x7525,0x8999},{0x7526,0xE153},{0x7528,0x9770},{0x752B,0x95E1}, +{0x752C,0xE154},{0x752F,0xFAA8},{0x7530,0x9363},{0x7531,0x9752}, +{0x7532,0x8D62},{0x7533,0x905C},{0x7537,0x926A},{0x7538,0x99B2}, +{0x753A,0x92AC},{0x753B,0x89E6},{0x753C,0xE155},{0x7544,0xE156}, +{0x7546,0xE15B},{0x7549,0xE159},{0x754A,0xE158},{0x754B,0x9DC0}, +{0x754C,0x8A45},{0x754D,0xE157},{0x754F,0x88D8},{0x7551,0x94A8}, +{0x7554,0x94C8},{0x7559,0x97AF},{0x755A,0xE15C},{0x755B,0xE15A}, +{0x755C,0x927B},{0x755D,0x90A4},{0x7560,0x94A9},{0x7562,0x954C}, +{0x7564,0xE15E},{0x7565,0x97AA},{0x7566,0x8C6C},{0x7567,0xE15F}, +{0x7569,0xE15D},{0x756A,0x94D4},{0x756B,0xE160},{0x756D,0xE161}, +{0x756F,0xFB6F},{0x7570,0x88D9},{0x7573,0x8FF4},{0x7574,0xE166}, +{0x7576,0xE163},{0x7577,0x93EB},{0x7578,0xE162},{0x757F,0x8B45}, +{0x7582,0xE169},{0x7586,0xE164},{0x7587,0xE165},{0x7589,0xE168}, +{0x758A,0xE167},{0x758B,0x9544},{0x758E,0x9161},{0x758F,0x9160}, +{0x7591,0x8B5E},{0x7594,0xE16A},{0x759A,0xE16B},{0x759D,0xE16C}, +{0x75A3,0xE16E},{0x75A5,0xE16D},{0x75AB,0x8975},{0x75B1,0xE176}, +{0x75B2,0x94E6},{0x75B3,0xE170},{0x75B5,0xE172},{0x75B8,0xE174}, +{0x75B9,0x905D},{0x75BC,0xE175},{0x75BD,0xE173},{0x75BE,0x8EBE}, +{0x75C2,0xE16F},{0x75C3,0xE171},{0x75C5,0x9561},{0x75C7,0x8FC7}, +{0x75CA,0xE178},{0x75CD,0xE177},{0x75D2,0xE179},{0x75D4,0x8EA4}, +{0x75D5,0x8DAD},{0x75D8,0x9397},{0x75D9,0xE17A},{0x75DB,0x92C9}, +{0x75DE,0xE17C},{0x75E2,0x979F},{0x75E3,0xE17B},{0x75E9,0x9189}, +{0x75F0,0xE182},{0x75F2,0xE184},{0x75F3,0xE185},{0x75F4,0x9273}, +{0x75FA,0xE183},{0x75FC,0xE180},{0x75FE,0xE17D},{0x75FF,0xE17E}, +{0x7601,0xE181},{0x7609,0xE188},{0x760B,0xE186},{0x760D,0xE187}, +{0x761F,0xE189},{0x7620,0xE18B},{0x7621,0xE18C},{0x7622,0xE18D}, +{0x7624,0xE18E},{0x7627,0xE18A},{0x7630,0xE190},{0x7634,0xE18F}, +{0x763B,0xE191},{0x7642,0x97C3},{0x7646,0xE194},{0x7647,0xE192}, +{0x7648,0xE193},{0x764C,0x8AE0},{0x7652,0x96FC},{0x7656,0x95C8}, +{0x7658,0xE196},{0x765C,0xE195},{0x7661,0xE197},{0x7662,0xE198}, +{0x7667,0xE19C},{0x7668,0xE199},{0x7669,0xE19A},{0x766A,0xE19B}, +{0x766C,0xE19D},{0x7670,0xE19E},{0x7672,0xE19F},{0x7676,0xE1A0}, +{0x7678,0xE1A1},{0x767A,0x94AD},{0x767B,0x936F},{0x767C,0xE1A2}, +{0x767D,0x9492},{0x767E,0x9553},{0x7680,0xE1A3},{0x7682,0xFB70}, +{0x7683,0xE1A4},{0x7684,0x9349},{0x7686,0x8A46},{0x7687,0x8D63}, +{0x7688,0xE1A5},{0x768B,0xE1A6},{0x768E,0xE1A7},{0x7690,0x8E48}, +{0x7693,0xE1A9},{0x7696,0xE1A8},{0x7699,0xE1AA},{0x769A,0xE1AB}, +{0x769B,0xFB73},{0x769C,0xFB71},{0x769E,0xFB72},{0x76A6,0xFB74}, +{0x76AE,0x94E7},{0x76B0,0xE1AC},{0x76B4,0xE1AD},{0x76B7,0xEA89}, +{0x76B8,0xE1AE},{0x76B9,0xE1AF},{0x76BA,0xE1B0},{0x76BF,0x8E4D}, +{0x76C2,0xE1B1},{0x76C3,0x9475},{0x76C6,0x967E},{0x76C8,0x896D}, +{0x76CA,0x8976},{0x76CD,0xE1B2},{0x76D2,0xE1B4},{0x76D6,0xE1B3}, +{0x76D7,0x9390},{0x76DB,0x90B7},{0x76DC,0x9F58},{0x76DE,0xE1B5}, +{0x76DF,0x96BF},{0x76E1,0xE1B6},{0x76E3,0x8AC4},{0x76E4,0x94D5}, +{0x76E5,0xE1B7},{0x76E7,0xE1B8},{0x76EA,0xE1B9},{0x76EE,0x96DA}, +{0x76F2,0x96D3},{0x76F4,0x92BC},{0x76F8,0x918A},{0x76FB,0xE1BB}, +{0x76FE,0x8F82},{0x7701,0x8FC8},{0x7704,0xE1BE},{0x7707,0xE1BD}, +{0x7708,0xE1BC},{0x7709,0x94FB},{0x770B,0x8AC5},{0x770C,0x8CA7}, +{0x771B,0xE1C4},{0x771E,0xE1C1},{0x771F,0x905E},{0x7720,0x96B0}, +{0x7724,0xE1C0},{0x7725,0xE1C2},{0x7726,0xE1C3},{0x7729,0xE1BF}, +{0x7737,0xE1C5},{0x7738,0xE1C6},{0x773A,0x92AD},{0x773C,0x8AE1}, +{0x7740,0x9285},{0x7746,0xFB76},{0x7747,0xE1C7},{0x775A,0xE1C8}, +{0x775B,0xE1CB},{0x7761,0x9087},{0x7763,0x93C2},{0x7765,0xE1CC}, +{0x7766,0x9672},{0x7768,0xE1C9},{0x776B,0xE1CA},{0x7779,0xE1CF}, +{0x777E,0xE1CE},{0x777F,0xE1CD},{0x778B,0xE1D1},{0x778E,0xE1D0}, +{0x7791,0xE1D2},{0x779E,0xE1D4},{0x77A0,0xE1D3},{0x77A5,0x95CB}, +{0x77AC,0x8F75},{0x77AD,0x97C4},{0x77B0,0xE1D5},{0x77B3,0x93B5}, +{0x77B6,0xE1D6},{0x77B9,0xE1D7},{0x77BB,0xE1DB},{0x77BC,0xE1D9}, +{0x77BD,0xE1DA},{0x77BF,0xE1D8},{0x77C7,0xE1DC},{0x77CD,0xE1DD}, +{0x77D7,0xE1DE},{0x77DA,0xE1DF},{0x77DB,0x96B5},{0x77DC,0xE1E0}, +{0x77E2,0x96EE},{0x77E3,0xE1E1},{0x77E5,0x926D},{0x77E7,0x948A}, +{0x77E9,0x8BE9},{0x77ED,0x925A},{0x77EE,0xE1E2},{0x77EF,0x8BB8}, +{0x77F3,0x90CE},{0x77FC,0xE1E3},{0x7802,0x8DBB},{0x780C,0xE1E4}, +{0x7812,0xE1E5},{0x7814,0x8CA4},{0x7815,0x8DD3},{0x7820,0xE1E7}, +{0x7821,0xFB78},{0x7825,0x9375},{0x7826,0x8DD4},{0x7827,0x8B6D}, +{0x7832,0x9643},{0x7834,0x946A},{0x783A,0x9376},{0x783F,0x8D7B}, +{0x7845,0xE1E9},{0x784E,0xFB79},{0x785D,0x8FC9},{0x7864,0xFB7A}, +{0x786B,0x97B0},{0x786C,0x8D64},{0x786F,0x8CA5},{0x7872,0x94A1}, +{0x7874,0xE1EB},{0x787A,0xFB7B},{0x787C,0xE1ED},{0x7881,0x8CE9}, +{0x7886,0xE1EC},{0x7887,0x92F4},{0x788C,0xE1EF},{0x788D,0x8A56}, +{0x788E,0xE1EA},{0x7891,0x94E8},{0x7893,0x894F},{0x7895,0x8DEA}, +{0x7897,0x9871},{0x789A,0xE1EE},{0x78A3,0xE1F0},{0x78A7,0x95C9}, +{0x78A9,0x90D7},{0x78AA,0xE1F2},{0x78AF,0xE1F3},{0x78B5,0xE1F1}, +{0x78BA,0x8A6D},{0x78BC,0xE1F9},{0x78BE,0xE1F8},{0x78C1,0x8EA5}, +{0x78C5,0xE1FA},{0x78C6,0xE1F5},{0x78CA,0xE1FB},{0x78CB,0xE1F6}, +{0x78D0,0x94D6},{0x78D1,0xE1F4},{0x78D4,0xE1F7},{0x78DA,0xE241}, +{0x78E7,0xE240},{0x78E8,0x9681},{0x78EC,0xE1FC},{0x78EF,0x88E9}, +{0x78F4,0xE243},{0x78FD,0xE242},{0x7901,0x8FCA},{0x7907,0xE244}, +{0x790E,0x9162},{0x7911,0xE246},{0x7912,0xE245},{0x7919,0xE247}, +{0x7926,0xE1E6},{0x792A,0xE1E8},{0x792B,0xE249},{0x792C,0xE248}, +{0x7930,0xFB7C},{0x793A,0x8EA6},{0x793C,0x97E7},{0x793E,0x8ED0}, +{0x7940,0xE24A},{0x7941,0x8C56},{0x7947,0x8B5F},{0x7948,0x8B46}, +{0x7949,0x8E83},{0x7950,0x9753},{0x7953,0xE250},{0x7955,0xE24F}, +{0x7956,0x9163},{0x7957,0xE24C},{0x795A,0xE24E},{0x795D,0x8F6A}, +{0x795E,0x905F},{0x795F,0xE24D},{0x7960,0xE24B},{0x7962,0x9449}, +{0x7965,0x8FCB},{0x7968,0x955B},{0x796D,0x8DD5},{0x7977,0x9398}, +{0x797A,0xE251},{0x797F,0xE252},{0x7980,0xE268},{0x7981,0x8BD6}, +{0x7984,0x985C},{0x7985,0x9154},{0x798A,0xE253},{0x798D,0x89D0}, +{0x798E,0x92F5},{0x798F,0x959F},{0x7994,0xFB81},{0x799B,0xFB83}, +{0x799D,0xE254},{0x79A6,0x8B9A},{0x79A7,0xE255},{0x79AA,0xE257}, +{0x79AE,0xE258},{0x79B0,0x9448},{0x79B3,0xE259},{0x79B9,0xE25A}, +{0x79BA,0xE25B},{0x79BD,0x8BD7},{0x79BE,0x89D1},{0x79BF,0x93C3}, +{0x79C0,0x8F47},{0x79C1,0x8E84},{0x79C9,0xE25C},{0x79CB,0x8F48}, +{0x79D1,0x89C8},{0x79D2,0x9562},{0x79D5,0xE25D},{0x79D8,0x94E9}, +{0x79DF,0x9164},{0x79E1,0xE260},{0x79E3,0xE261},{0x79E4,0x9489}, +{0x79E6,0x9060},{0x79E7,0xE25E},{0x79E9,0x9281},{0x79EC,0xE25F}, +{0x79F0,0x8FCC},{0x79FB,0x88DA},{0x7A00,0x8B48},{0x7A08,0xE262}, +{0x7A0B,0x92F6},{0x7A0D,0xE263},{0x7A0E,0x90C5},{0x7A14,0x96AB}, +{0x7A17,0x9542},{0x7A18,0xE264},{0x7A19,0xE265},{0x7A1A,0x9274}, +{0x7A1C,0x97C5},{0x7A1F,0xE267},{0x7A20,0xE266},{0x7A2E,0x8EED}, +{0x7A31,0xE269},{0x7A32,0x88EE},{0x7A37,0xE26C},{0x7A3B,0xE26A}, +{0x7A3C,0x89D2},{0x7A3D,0x8C6D},{0x7A3E,0xE26B},{0x7A3F,0x8D65}, +{0x7A40,0x8D92},{0x7A42,0x95E4},{0x7A43,0xE26D},{0x7A46,0x9673}, +{0x7A49,0xE26F},{0x7A4D,0x90CF},{0x7A4E,0x896E},{0x7A4F,0x89B8}, +{0x7A50,0x88AA},{0x7A57,0xE26E},{0x7A61,0xE270},{0x7A62,0xE271}, +{0x7A63,0x8FF5},{0x7A69,0xE272},{0x7A6B,0x8A6E},{0x7A70,0xE274}, +{0x7A74,0x8C8A},{0x7A76,0x8B86},{0x7A79,0xE275},{0x7A7A,0x8BF3}, +{0x7A7D,0xE276},{0x7A7F,0x90FA},{0x7A81,0x93CB},{0x7A83,0x90DE}, +{0x7A84,0x8DF3},{0x7A88,0xE277},{0x7A92,0x9282},{0x7A93,0x918B}, +{0x7A95,0xE279},{0x7A96,0xE27B},{0x7A97,0xE278},{0x7A98,0xE27A}, +{0x7A9F,0x8C41},{0x7AA9,0xE27C},{0x7AAA,0x8C45},{0x7AAE,0x8B87}, +{0x7AAF,0x9771},{0x7AB0,0xE27E},{0x7AB6,0xE280},{0x7ABA,0x894D}, +{0x7ABF,0xE283},{0x7AC3,0x8A96},{0x7AC4,0xE282},{0x7AC5,0xE281}, +{0x7AC7,0xE285},{0x7AC8,0xE27D},{0x7ACA,0xE286},{0x7ACB,0x97A7}, +{0x7ACD,0xE287},{0x7ACF,0xE288},{0x7AD1,0xFB84},{0x7AD2,0x9AF2}, +{0x7AD3,0xE28A},{0x7AD5,0xE289},{0x7AD9,0xE28B},{0x7ADA,0xE28C}, +{0x7ADC,0x97B3},{0x7ADD,0xE28D},{0x7ADF,0xE8ED},{0x7AE0,0x8FCD}, +{0x7AE1,0xE28E},{0x7AE2,0xE28F},{0x7AE3,0x8F76},{0x7AE5,0x93B6}, +{0x7AE6,0xE290},{0x7AE7,0xFB85},{0x7AEA,0x9247},{0x7AEB,0xFB87}, +{0x7AED,0xE291},{0x7AEF,0x925B},{0x7AF0,0xE292},{0x7AF6,0x8BA3}, +{0x7AF8,0x995E},{0x7AF9,0x927C},{0x7AFA,0x8EB1},{0x7AFF,0x8AC6}, +{0x7B02,0xE293},{0x7B04,0xE2A0},{0x7B06,0xE296},{0x7B08,0x8B88}, +{0x7B0A,0xE295},{0x7B0B,0xE2A2},{0x7B0F,0xE294},{0x7B11,0x8FCE}, +{0x7B18,0xE298},{0x7B19,0xE299},{0x7B1B,0x934A},{0x7B1E,0xE29A}, +{0x7B20,0x8A7D},{0x7B25,0x9079},{0x7B26,0x9584},{0x7B28,0xE29C}, +{0x7B2C,0x91E6},{0x7B33,0xE297},{0x7B35,0xE29B},{0x7B36,0xE29D}, +{0x7B39,0x8DF9},{0x7B45,0xE2A4},{0x7B46,0x954D},{0x7B48,0x94A4}, +{0x7B49,0x9399},{0x7B4B,0x8BD8},{0x7B4C,0xE2A3},{0x7B4D,0xE2A1}, +{0x7B4F,0x94B3},{0x7B50,0xE29E},{0x7B51,0x927D},{0x7B52,0x939B}, +{0x7B54,0x939A},{0x7B56,0x8DF4},{0x7B5D,0xE2B6},{0x7B65,0xE2A6}, +{0x7B67,0xE2A8},{0x7B6C,0xE2AB},{0x7B6E,0xE2AC},{0x7B70,0xE2A9}, +{0x7B71,0xE2AA},{0x7B74,0xE2A7},{0x7B75,0xE2A5},{0x7B7A,0xE29F}, +{0x7B86,0x95CD},{0x7B87,0x89D3},{0x7B8B,0xE2B3},{0x7B8D,0xE2B0}, +{0x7B8F,0xE2B5},{0x7B92,0xE2B4},{0x7B94,0x9493},{0x7B95,0x96A5}, +{0x7B97,0x8E5A},{0x7B98,0xE2AE},{0x7B99,0xE2B7},{0x7B9A,0xE2B2}, +{0x7B9C,0xE2B1},{0x7B9D,0xE2AD},{0x7B9E,0xFB88},{0x7B9F,0xE2AF}, +{0x7BA1,0x8AC7},{0x7BAA,0x925C},{0x7BAD,0x90FB},{0x7BB1,0x94A0}, +{0x7BB4,0xE2BC},{0x7BB8,0x94A2},{0x7BC0,0x90DF},{0x7BC1,0xE2B9}, +{0x7BC4,0x94CD},{0x7BC6,0xE2BD},{0x7BC7,0x95D1},{0x7BC9,0x927A}, +{0x7BCB,0xE2B8},{0x7BCC,0xE2BA},{0x7BCF,0xE2BB},{0x7BDD,0xE2BE}, +{0x7BE0,0x8EC2},{0x7BE4,0x93C4},{0x7BE5,0xE2C3},{0x7BE6,0xE2C2}, +{0x7BE9,0xE2BF},{0x7BED,0x9855},{0x7BF3,0xE2C8},{0x7BF6,0xE2CC}, +{0x7BF7,0xE2C9},{0x7C00,0xE2C5},{0x7C07,0xE2C6},{0x7C0D,0xE2CB}, +{0x7C11,0xE2C0},{0x7C12,0x99D3},{0x7C13,0xE2C7},{0x7C14,0xE2C1}, +{0x7C17,0xE2CA},{0x7C1F,0xE2D0},{0x7C21,0x8AC8},{0x7C23,0xE2CD}, +{0x7C27,0xE2CE},{0x7C2A,0xE2CF},{0x7C2B,0xE2D2},{0x7C37,0xE2D1}, +{0x7C38,0x94F4},{0x7C3D,0xE2D3},{0x7C3E,0x97FA},{0x7C3F,0x95EB}, +{0x7C40,0xE2D8},{0x7C43,0xE2D5},{0x7C4C,0xE2D4},{0x7C4D,0x90D0}, +{0x7C4F,0xE2D7},{0x7C50,0xE2D9},{0x7C54,0xE2D6},{0x7C56,0xE2DD}, +{0x7C58,0xE2DA},{0x7C5F,0xE2DB},{0x7C60,0xE2C4},{0x7C64,0xE2DC}, +{0x7C65,0xE2DE},{0x7C6C,0xE2DF},{0x7C73,0x95C4},{0x7C75,0xE2E0}, +{0x7C7E,0x96E0},{0x7C81,0x8BCC},{0x7C82,0x8C48},{0x7C83,0xE2E1}, +{0x7C89,0x95B2},{0x7C8B,0x9088},{0x7C8D,0x96AE},{0x7C90,0xE2E2}, +{0x7C92,0x97B1},{0x7C95,0x9494},{0x7C97,0x9165},{0x7C98,0x9453}, +{0x7C9B,0x8F6C},{0x7C9F,0x88BE},{0x7CA1,0xE2E7},{0x7CA2,0xE2E5}, +{0x7CA4,0xE2E3},{0x7CA5,0x8A9F},{0x7CA7,0x8FCF},{0x7CA8,0xE2E8}, +{0x7CAB,0xE2E6},{0x7CAD,0xE2E4},{0x7CAE,0xE2EC},{0x7CB1,0xE2EB}, +{0x7CB2,0xE2EA},{0x7CB3,0xE2E9},{0x7CB9,0xE2ED},{0x7CBD,0xE2EE}, +{0x7CBE,0x90B8},{0x7CC0,0xE2EF},{0x7CC2,0xE2F1},{0x7CC5,0xE2F0}, +{0x7CCA,0x8CD0},{0x7CCE,0x9157},{0x7CD2,0xE2F3},{0x7CD6,0x939C}, +{0x7CD8,0xE2F2},{0x7CDC,0xE2F4},{0x7CDE,0x95B3},{0x7CDF,0x918C}, +{0x7CE0,0x8D66},{0x7CE2,0xE2F5},{0x7CE7,0x97C6},{0x7CEF,0xE2F7}, +{0x7CF2,0xE2F8},{0x7CF4,0xE2F9},{0x7CF6,0xE2FA},{0x7CF8,0x8E85}, +{0x7CFA,0xE2FB},{0x7CFB,0x8C6E},{0x7CFE,0x8B8A},{0x7D00,0x8B49}, +{0x7D02,0xE340},{0x7D04,0x96F1},{0x7D05,0x8D67},{0x7D06,0xE2FC}, +{0x7D0A,0xE343},{0x7D0B,0x96E4},{0x7D0D,0x945B},{0x7D10,0x9552}, +{0x7D14,0x8F83},{0x7D15,0xE342},{0x7D17,0x8ED1},{0x7D18,0x8D68}, +{0x7D19,0x8E86},{0x7D1A,0x8B89},{0x7D1B,0x95B4},{0x7D1C,0xE341}, +{0x7D20,0x9166},{0x7D21,0x9661},{0x7D22,0x8DF5},{0x7D2B,0x8E87}, +{0x7D2C,0x92DB},{0x7D2E,0xE346},{0x7D2F,0x97DD},{0x7D30,0x8DD7}, +{0x7D32,0xE347},{0x7D33,0x9061},{0x7D35,0xE349},{0x7D39,0x8FD0}, +{0x7D3A,0x8DAE},{0x7D3F,0xE348},{0x7D42,0x8F49},{0x7D43,0x8CBC}, +{0x7D44,0x9167},{0x7D45,0xE344},{0x7D46,0xE34A},{0x7D48,0xFB8A}, +{0x7D4B,0xE345},{0x7D4C,0x8C6F},{0x7D4E,0xE34D},{0x7D4F,0xE351}, +{0x7D50,0x8C8B},{0x7D56,0xE34C},{0x7D5B,0xE355},{0x7D5C,0xFB8B}, +{0x7D5E,0x8D69},{0x7D61,0x978D},{0x7D62,0x88BA},{0x7D63,0xE352}, +{0x7D66,0x8B8B},{0x7D68,0xE34F},{0x7D6E,0xE350},{0x7D71,0x939D}, +{0x7D72,0xE34E},{0x7D73,0xE34B},{0x7D75,0x8A47},{0x7D76,0x90E2}, +{0x7D79,0x8CA6},{0x7D7D,0xE357},{0x7D89,0xE354},{0x7D8F,0xE356}, +{0x7D93,0xE353},{0x7D99,0x8C70},{0x7D9A,0x91B1},{0x7D9B,0xE358}, +{0x7D9C,0x918E},{0x7D9F,0xE365},{0x7DA0,0xFB8D},{0x7DA2,0xE361}, +{0x7DA3,0xE35B},{0x7DAB,0xE35F},{0x7DAC,0x8EF8},{0x7DAD,0x88DB}, +{0x7DAE,0xE35A},{0x7DAF,0xE362},{0x7DB0,0xE366},{0x7DB1,0x8D6A}, +{0x7DB2,0x96D4},{0x7DB4,0x92D4},{0x7DB5,0xE35C},{0x7DB7,0xFB8C}, +{0x7DB8,0xE364},{0x7DBA,0xE359},{0x7DBB,0x925D},{0x7DBD,0xE35E}, +{0x7DBE,0x88BB},{0x7DBF,0x96C8},{0x7DC7,0xE35D},{0x7DCA,0x8BD9}, +{0x7DCB,0x94EA},{0x7DCF,0x918D},{0x7DD1,0x97CE},{0x7DD2,0x8F8F}, +{0x7DD5,0xE38E},{0x7DD6,0xFB8E},{0x7DD8,0xE367},{0x7DDA,0x90FC}, +{0x7DDC,0xE363},{0x7DDD,0xE368},{0x7DDE,0xE36A},{0x7DE0,0x92F7}, +{0x7DE1,0xE36D},{0x7DE4,0xE369},{0x7DE8,0x95D2},{0x7DE9,0x8AC9}, +{0x7DEC,0x96C9},{0x7DEF,0x88DC},{0x7DF2,0xE36C},{0x7DF4,0x97FB}, +{0x7DFB,0xE36B},{0x7E01,0x898F},{0x7E04,0x93EA},{0x7E05,0xE36E}, +{0x7E09,0xE375},{0x7E0A,0xE36F},{0x7E0B,0xE376},{0x7E12,0xE372}, +{0x7E1B,0x949B},{0x7E1E,0x8EC8},{0x7E1F,0xE374},{0x7E21,0xE371}, +{0x7E22,0xE377},{0x7E23,0xE370},{0x7E26,0x8F63},{0x7E2B,0x9644}, +{0x7E2E,0x8F6B},{0x7E31,0xE373},{0x7E32,0xE380},{0x7E35,0xE37B}, +{0x7E37,0xE37E},{0x7E39,0xE37C},{0x7E3A,0xE381},{0x7E3B,0xE37A}, +{0x7E3D,0xE360},{0x7E3E,0x90D1},{0x7E41,0x94C9},{0x7E43,0xE37D}, +{0x7E46,0xE378},{0x7E4A,0x9140},{0x7E4B,0x8C71},{0x7E4D,0x8F4A}, +{0x7E52,0xFB8F},{0x7E54,0x9044},{0x7E55,0x9155},{0x7E56,0xE384}, +{0x7E59,0xE386},{0x7E5A,0xE387},{0x7E5D,0xE383},{0x7E5E,0xE385}, +{0x7E66,0xE379},{0x7E67,0xE382},{0x7E69,0xE38A},{0x7E6A,0xE389}, +{0x7E6D,0x969A},{0x7E70,0x8C4A},{0x7E79,0xE388},{0x7E7B,0xE38C}, +{0x7E7C,0xE38B},{0x7E7D,0xE38F},{0x7E7F,0xE391},{0x7E82,0x8E5B}, +{0x7E83,0xE38D},{0x7E88,0xE392},{0x7E89,0xE393},{0x7E8A,0xFA5C}, +{0x7E8C,0xE394},{0x7E8E,0xE39A},{0x7E8F,0x935A},{0x7E90,0xE396}, +{0x7E92,0xE395},{0x7E93,0xE397},{0x7E94,0xE398},{0x7E96,0xE399}, +{0x7E9B,0xE39B},{0x7E9C,0xE39C},{0x7F36,0x8ACA},{0x7F38,0xE39D}, +{0x7F3A,0xE39E},{0x7F45,0xE39F},{0x7F47,0xFB90},{0x7F4C,0xE3A0}, +{0x7F4D,0xE3A1},{0x7F4E,0xE3A2},{0x7F50,0xE3A3},{0x7F51,0xE3A4}, +{0x7F54,0xE3A6},{0x7F55,0xE3A5},{0x7F58,0xE3A7},{0x7F5F,0xE3A8}, +{0x7F60,0xE3A9},{0x7F67,0xE3AC},{0x7F68,0xE3AA},{0x7F69,0xE3AB}, +{0x7F6A,0x8DDF},{0x7F6B,0x8C72},{0x7F6E,0x9275},{0x7F70,0x94B1}, +{0x7F72,0x8F90},{0x7F75,0x946C},{0x7F77,0x94EB},{0x7F78,0xE3AD}, +{0x7F79,0x9CEB},{0x7F82,0xE3AE},{0x7F83,0xE3B0},{0x7F85,0x9785}, +{0x7F86,0xE3AF},{0x7F87,0xE3B2},{0x7F88,0xE3B1},{0x7F8A,0x9772}, +{0x7F8C,0xE3B3},{0x7F8E,0x94FC},{0x7F94,0xE3B4},{0x7F9A,0xE3B7}, +{0x7F9D,0xE3B6},{0x7F9E,0xE3B5},{0x7FA1,0xFB91},{0x7FA3,0xE3B8}, +{0x7FA4,0x8C51},{0x7FA8,0x9141},{0x7FA9,0x8B60},{0x7FAE,0xE3BC}, +{0x7FAF,0xE3B9},{0x7FB2,0xE3BA},{0x7FB6,0xE3BD},{0x7FB8,0xE3BE}, +{0x7FB9,0xE3BB},{0x7FBD,0x8948},{0x7FC1,0x89A5},{0x7FC5,0xE3C0}, +{0x7FC6,0xE3C1},{0x7FCA,0xE3C2},{0x7FCC,0x9782},{0x7FD2,0x8F4B}, +{0x7FD4,0xE3C4},{0x7FD5,0xE3C3},{0x7FE0,0x9089},{0x7FE1,0xE3C5}, +{0x7FE6,0xE3C6},{0x7FE9,0xE3C7},{0x7FEB,0x8AE3},{0x7FF0,0x8ACB}, +{0x7FF3,0xE3C8},{0x7FF9,0xE3C9},{0x7FFB,0x967C},{0x7FFC,0x9783}, +{0x8000,0x9773},{0x8001,0x9856},{0x8003,0x8D6C},{0x8004,0xE3CC}, +{0x8005,0x8ED2},{0x8006,0xE3CB},{0x800B,0xE3CD},{0x800C,0x8EA7}, +{0x8010,0x91CF},{0x8012,0xE3CE},{0x8015,0x8D6B},{0x8017,0x96D5}, +{0x8018,0xE3CF},{0x8019,0xE3D0},{0x801C,0xE3D1},{0x8021,0xE3D2}, +{0x8028,0xE3D3},{0x8033,0x8EA8},{0x8036,0x96EB},{0x803B,0xE3D5}, +{0x803D,0x925E},{0x803F,0xE3D4},{0x8046,0xE3D7},{0x804A,0xE3D6}, +{0x8052,0xE3D8},{0x8056,0x90B9},{0x8058,0xE3D9},{0x805A,0xE3DA}, +{0x805E,0x95B7},{0x805F,0xE3DB},{0x8061,0x918F},{0x8062,0xE3DC}, +{0x8068,0xE3DD},{0x806F,0x97FC},{0x8070,0xE3E0},{0x8072,0xE3DF}, +{0x8073,0xE3DE},{0x8074,0x92AE},{0x8076,0xE3E1},{0x8077,0x9045}, +{0x8079,0xE3E2},{0x807D,0xE3E3},{0x807E,0x9857},{0x807F,0xE3E4}, +{0x8084,0xE3E5},{0x8085,0xE3E7},{0x8086,0xE3E6},{0x8087,0x94A3}, +{0x8089,0x93F7},{0x808B,0x985D},{0x808C,0x94A7},{0x8093,0xE3E9}, +{0x8096,0x8FD1},{0x8098,0x9549},{0x809A,0xE3EA},{0x809B,0xE3E8}, +{0x809D,0x8ACC},{0x80A1,0x8CD2},{0x80A2,0x8E88},{0x80A5,0x94EC}, +{0x80A9,0x8CA8},{0x80AA,0x9662},{0x80AC,0xE3ED},{0x80AD,0xE3EB}, +{0x80AF,0x8D6D},{0x80B1,0x8D6E},{0x80B2,0x88E7},{0x80B4,0x8DE6}, +{0x80BA,0x9478},{0x80C3,0x88DD},{0x80C4,0xE3F2},{0x80C6,0x925F}, +{0x80CC,0x9477},{0x80CE,0x91D9},{0x80D6,0xE3F4},{0x80D9,0xE3F0}, +{0x80DA,0xE3F3},{0x80DB,0xE3EE},{0x80DD,0xE3F1},{0x80DE,0x9645}, +{0x80E1,0x8CD3},{0x80E4,0x88FB},{0x80E5,0xE3EF},{0x80EF,0xE3F6}, +{0x80F1,0xE3F7},{0x80F4,0x93B7},{0x80F8,0x8BB9},{0x80FC,0xE445}, +{0x80FD,0x945C},{0x8102,0x8E89},{0x8105,0x8BBA},{0x8106,0x90C6}, +{0x8107,0x9865},{0x8108,0x96AC},{0x8109,0xE3F5},{0x810A,0x90D2}, +{0x811A,0x8B72},{0x811B,0xE3F8},{0x8123,0xE3FA},{0x8129,0xE3F9}, +{0x812F,0xE3FB},{0x8131,0x9245},{0x8133,0x945D},{0x8139,0x92AF}, +{0x813E,0xE442},{0x8146,0xE441},{0x814B,0xE3FC},{0x814E,0x9074}, +{0x8150,0x9585},{0x8151,0xE444},{0x8153,0xE443},{0x8154,0x8D6F}, +{0x8155,0x9872},{0x815F,0xE454},{0x8165,0xE448},{0x8166,0xE449}, +{0x816B,0x8EEE},{0x816E,0xE447},{0x8170,0x8D98},{0x8171,0xE446}, +{0x8174,0xE44A},{0x8178,0x92B0},{0x8179,0x95A0},{0x817A,0x9142}, +{0x817F,0x91DA},{0x8180,0xE44E},{0x8182,0xE44F},{0x8183,0xE44B}, +{0x8188,0xE44C},{0x818A,0xE44D},{0x818F,0x8D70},{0x8193,0xE455}, +{0x8195,0xE451},{0x819A,0x9586},{0x819C,0x968C},{0x819D,0x9547}, +{0x81A0,0xE450},{0x81A3,0xE453},{0x81A4,0xE452},{0x81A8,0x9663}, +{0x81A9,0xE456},{0x81B0,0xE457},{0x81B3,0x9156},{0x81B5,0xE458}, +{0x81B8,0xE45A},{0x81BA,0xE45E},{0x81BD,0xE45B},{0x81BE,0xE459}, +{0x81BF,0x945E},{0x81C0,0xE45C},{0x81C2,0xE45D},{0x81C6,0x89B0}, +{0x81C8,0xE464},{0x81C9,0xE45F},{0x81CD,0xE460},{0x81D1,0xE461}, +{0x81D3,0x919F},{0x81D8,0xE463},{0x81D9,0xE462},{0x81DA,0xE465}, +{0x81DF,0xE466},{0x81E0,0xE467},{0x81E3,0x9062},{0x81E5,0x89E7}, +{0x81E7,0xE468},{0x81E8,0x97D5},{0x81EA,0x8EA9},{0x81ED,0x8F4C}, +{0x81F3,0x8E8A},{0x81F4,0x9276},{0x81FA,0xE469},{0x81FB,0xE46A}, +{0x81FC,0x8950},{0x81FE,0xE46B},{0x8201,0xE46C},{0x8202,0xE46D}, +{0x8205,0xE46E},{0x8207,0xE46F},{0x8208,0x8BBB},{0x8209,0x9DA8}, +{0x820A,0xE470},{0x820C,0x90E3},{0x820D,0xE471},{0x820E,0x8EC9}, +{0x8210,0xE472},{0x8212,0x98AE},{0x8216,0xE473},{0x8217,0x95DC}, +{0x8218,0x8ADA},{0x821B,0x9143},{0x821C,0x8F77},{0x821E,0x9591}, +{0x821F,0x8F4D},{0x8229,0xE474},{0x822A,0x8D71},{0x822B,0xE475}, +{0x822C,0x94CA},{0x822E,0xE484},{0x8233,0xE477},{0x8235,0x91C7}, +{0x8236,0x9495},{0x8237,0x8CBD},{0x8238,0xE476},{0x8239,0x9144}, +{0x8240,0xE478},{0x8247,0x92F8},{0x8258,0xE47A},{0x8259,0xE479}, +{0x825A,0xE47C},{0x825D,0xE47B},{0x825F,0xE47D},{0x8262,0xE480}, +{0x8264,0xE47E},{0x8266,0x8ACD},{0x8268,0xE481},{0x826A,0xE482}, +{0x826B,0xE483},{0x826E,0x8DAF},{0x826F,0x97C7},{0x8271,0xE485}, +{0x8272,0x9046},{0x8276,0x8990},{0x8277,0xE486},{0x8278,0xE487}, +{0x827E,0xE488},{0x828B,0x88F0},{0x828D,0xE489},{0x8292,0xE48A}, +{0x8299,0x9587},{0x829D,0x8EC5},{0x829F,0xE48C},{0x82A5,0x8A48}, +{0x82A6,0x88B0},{0x82AB,0xE48B},{0x82AC,0xE48E},{0x82AD,0x946D}, +{0x82AF,0x9063},{0x82B1,0x89D4},{0x82B3,0x9646},{0x82B8,0x8C7C}, +{0x82B9,0x8BDA},{0x82BB,0xE48D},{0x82BD,0x89E8},{0x82C5,0x8AA1}, +{0x82D1,0x8991},{0x82D2,0xE492},{0x82D3,0x97E8},{0x82D4,0x91DB}, +{0x82D7,0x9563},{0x82D9,0xE49E},{0x82DB,0x89D5},{0x82DC,0xE49C}, +{0x82DE,0xE49A},{0x82DF,0xE491},{0x82E1,0xE48F},{0x82E3,0xE490}, +{0x82E5,0x8EE1},{0x82E6,0x8BEA},{0x82E7,0x9297},{0x82EB,0x93CF}, +{0x82F1,0x8970},{0x82F3,0xE494},{0x82F4,0xE493},{0x82F9,0xE499}, +{0x82FA,0xE495},{0x82FB,0xE498},{0x8301,0xFB93},{0x8302,0x96CE}, +{0x8303,0xE497},{0x8304,0x89D6},{0x8305,0x8A9D},{0x8306,0xE49B}, +{0x8309,0xE49D},{0x830E,0x8C73},{0x8316,0xE4A1},{0x8317,0xE4AA}, +{0x8318,0xE4AB},{0x831C,0x88A9},{0x8323,0xE4B2},{0x8328,0x88EF}, +{0x832B,0xE4A9},{0x832F,0xE4A8},{0x8331,0xE4A3},{0x8332,0xE4A2}, +{0x8334,0xE4A0},{0x8335,0xE49F},{0x8336,0x9283},{0x8338,0x91F9}, +{0x8339,0xE4A5},{0x8340,0xE4A4},{0x8345,0xE4A7},{0x8349,0x9190}, +{0x834A,0x8C74},{0x834F,0x8960},{0x8350,0xE4A6},{0x8352,0x8D72}, +{0x8358,0x9191},{0x8362,0xFB94},{0x8373,0xE4B8},{0x8375,0xE4B9}, +{0x8377,0x89D7},{0x837B,0x89AC},{0x837C,0xE4B6},{0x837F,0xFB95}, +{0x8385,0xE4AC},{0x8387,0xE4B4},{0x8389,0xE4BB},{0x838A,0xE4B5}, +{0x838E,0xE4B3},{0x8393,0xE496},{0x8396,0xE4B1},{0x839A,0xE4AD}, +{0x839E,0x8ACE},{0x839F,0xE4AF},{0x83A0,0xE4BA},{0x83A2,0xE4B0}, +{0x83A8,0xE4BC},{0x83AA,0xE4AE},{0x83AB,0x949C},{0x83B1,0x9789}, +{0x83B5,0xE4B7},{0x83BD,0xE4CD},{0x83C1,0xE4C5},{0x83C5,0x909B}, +{0x83C7,0xFB96},{0x83CA,0x8B65},{0x83CC,0x8BDB},{0x83CE,0xE4C0}, +{0x83D3,0x89D9},{0x83D6,0x8FD2},{0x83D8,0xE4C3},{0x83DC,0x8DD8}, +{0x83DF,0x9370},{0x83E0,0xE4C8},{0x83E9,0x95EC},{0x83EB,0xE4BF}, +{0x83EF,0x89D8},{0x83F0,0x8CD4},{0x83F1,0x9548},{0x83F2,0xE4C9}, +{0x83F4,0xE4BD},{0x83F6,0xFB97},{0x83F7,0xE4C6},{0x83FB,0xE4D0}, +{0x83FD,0xE4C1},{0x8403,0xE4C2},{0x8404,0x93B8},{0x8407,0xE4C7}, +{0x840B,0xE4C4},{0x840C,0x9647},{0x840D,0xE4CA},{0x840E,0x88DE}, +{0x8413,0xE4BE},{0x8420,0xE4CC},{0x8422,0xE4CB},{0x8429,0x948B}, +{0x842A,0xE4D2},{0x842C,0xE4DD},{0x8431,0x8A9E},{0x8435,0xE4E0}, +{0x8438,0xE4CE},{0x843C,0xE4D3},{0x843D,0x978E},{0x8446,0xE4DC}, +{0x8448,0xFB98},{0x8449,0x9774},{0x844E,0x97A8},{0x8457,0x9298}, +{0x845B,0x8A8B},{0x8461,0x9592},{0x8462,0xE4E2},{0x8463,0x939F}, +{0x8466,0x88AF},{0x8469,0xE4DB},{0x846B,0xE4D7},{0x846C,0x9192}, +{0x846D,0xE4D1},{0x846E,0xE4D9},{0x846F,0xE4DE},{0x8471,0x944B}, +{0x8475,0x88A8},{0x8477,0xE4D6},{0x8479,0xE4DF},{0x847A,0x9598}, +{0x8482,0xE4DA},{0x8484,0xE4D5},{0x848B,0x8FD3},{0x8490,0x8F4E}, +{0x8494,0x8EAA},{0x8499,0x96D6},{0x849C,0x9566},{0x849F,0xE4E5}, +{0x84A1,0xE4EE},{0x84AD,0xE4D8},{0x84B2,0x8A97},{0x84B4,0xFB99}, +{0x84B8,0x8FF6},{0x84B9,0xE4E3},{0x84BB,0xE4E8},{0x84BC,0x9193}, +{0x84BF,0xE4E4},{0x84C1,0xE4EB},{0x84C4,0x927E},{0x84C6,0xE4EC}, +{0x84C9,0x9775},{0x84CA,0xE4E1},{0x84CB,0x8A57},{0x84CD,0xE4E7}, +{0x84D0,0xE4EA},{0x84D1,0x96AA},{0x84D6,0xE4ED},{0x84D9,0xE4E6}, +{0x84DA,0xE4E9},{0x84DC,0xFA60},{0x84EC,0x9648},{0x84EE,0x9840}, +{0x84F4,0xE4F1},{0x84FC,0xE4F8},{0x84FF,0xE4F0},{0x8500,0x8EC1}, +{0x8506,0xE4CF},{0x8511,0x95CC},{0x8513,0x96A0},{0x8514,0xE4F7}, +{0x8515,0xE4F6},{0x8517,0xE4F2},{0x8518,0xE4F3},{0x851A,0x8955}, +{0x851F,0xE4F5},{0x8521,0xE4EF},{0x8526,0x92D3},{0x852C,0xE4F4}, +{0x852D,0x88FC},{0x8535,0x91A0},{0x853D,0x95C1},{0x8540,0xE4F9}, +{0x8541,0xE540},{0x8543,0x94D7},{0x8548,0xE4FC},{0x8549,0x8FD4}, +{0x854A,0x8EC7},{0x854B,0xE542},{0x854E,0x8BBC},{0x8553,0xFB9A}, +{0x8555,0xE543},{0x8557,0x9599},{0x8558,0xE4FB},{0x8559,0xFB9B}, +{0x855A,0xE4D4},{0x8563,0xE4FA},{0x8568,0x986E},{0x8569,0x93A0}, +{0x856A,0x9593},{0x856B,0xFB9C},{0x856D,0xE54A},{0x8577,0xE550}, +{0x857E,0xE551},{0x8580,0xE544},{0x8584,0x9496},{0x8587,0xE54E}, +{0x8588,0xE546},{0x858A,0xE548},{0x8590,0xE552},{0x8591,0xE547}, +{0x8594,0xE54B},{0x8597,0x8992},{0x8599,0x93E3},{0x859B,0xE54C}, +{0x859C,0xE54F},{0x85A4,0xE545},{0x85A6,0x9145},{0x85A8,0xE549}, +{0x85A9,0x8E46},{0x85AA,0x9064},{0x85AB,0x8C4F},{0x85AC,0x96F2}, +{0x85AE,0x96F7},{0x85AF,0x8F92},{0x85B0,0xFB9E},{0x85B9,0xE556}, +{0x85BA,0xE554},{0x85C1,0x986D},{0x85C9,0xE553},{0x85CD,0x9795}, +{0x85CF,0xE555},{0x85D0,0xE557},{0x85D5,0xE558},{0x85DC,0xE55B}, +{0x85DD,0xE559},{0x85E4,0x93A1},{0x85E5,0xE55A},{0x85E9,0x94CB}, +{0x85EA,0xE54D},{0x85F7,0x8F93},{0x85F9,0xE55C},{0x85FA,0xE561}, +{0x85FB,0x9194},{0x85FE,0xE560},{0x8602,0xE541},{0x8606,0xE562}, +{0x8607,0x9168},{0x860A,0xE55D},{0x860B,0xE55F},{0x8613,0xE55E}, +{0x8616,0x9F50},{0x8617,0x9F41},{0x861A,0xE564},{0x8622,0xE563}, +{0x862D,0x9796},{0x862F,0xE1BA},{0x8630,0xE565},{0x863F,0xE566}, +{0x864D,0xE567},{0x864E,0x8CD5},{0x8650,0x8B73},{0x8654,0xE569}, +{0x8655,0x997C},{0x865A,0x8B95},{0x865C,0x97B8},{0x865E,0x8BF1}, +{0x865F,0xE56A},{0x8667,0xE56B},{0x866B,0x928E},{0x8671,0xE56C}, +{0x8679,0x93F8},{0x867B,0x88B8},{0x868A,0x89E1},{0x868B,0xE571}, +{0x868C,0xE572},{0x8693,0xE56D},{0x8695,0x8E5C},{0x86A3,0xE56E}, +{0x86A4,0x9461},{0x86A9,0xE56F},{0x86AA,0xE570},{0x86AB,0xE57A}, +{0x86AF,0xE574},{0x86B0,0xE577},{0x86B6,0xE573},{0x86C4,0xE575}, +{0x86C6,0xE576},{0x86C7,0x8ED6},{0x86C9,0xE578},{0x86CB,0x9260}, +{0x86CD,0x8C75},{0x86CE,0x8A61},{0x86D4,0xE57B},{0x86D9,0x8A5E}, +{0x86DB,0xE581},{0x86DE,0xE57C},{0x86DF,0xE580},{0x86E4,0x94B8}, +{0x86E9,0xE57D},{0x86EC,0xE57E},{0x86ED,0x9567},{0x86EE,0x94D8}, +{0x86EF,0xE582},{0x86F8,0x91FB},{0x86F9,0xE58C},{0x86FB,0xE588}, +{0x86FE,0x89E9},{0x8700,0xE586},{0x8702,0x9649},{0x8703,0xE587}, +{0x8706,0xE584},{0x8708,0xE585},{0x8709,0xE58A},{0x870A,0xE58D}, +{0x870D,0xE58B},{0x8711,0xE589},{0x8712,0xE583},{0x8718,0x9277}, +{0x871A,0xE594},{0x871C,0x96A8},{0x8725,0xE592},{0x8729,0xE593}, +{0x8734,0xE58E},{0x8737,0xE590},{0x873B,0xE591},{0x873F,0xE58F}, +{0x8749,0x90E4},{0x874B,0x9858},{0x874C,0xE598},{0x874E,0xE599}, +{0x8753,0xE59F},{0x8755,0x9049},{0x8757,0xE59B},{0x8759,0xE59E}, +{0x875F,0xE596},{0x8760,0xE595},{0x8763,0xE5A0},{0x8766,0x89DA}, +{0x8768,0xE59C},{0x876A,0xE5A1},{0x876E,0xE59D},{0x8774,0xE59A}, +{0x8776,0x92B1},{0x8778,0xE597},{0x877F,0x9488},{0x8782,0xE5A5}, +{0x878D,0x975A},{0x879F,0xE5A4},{0x87A2,0xE5A3},{0x87AB,0xE5AC}, +{0x87AF,0xE5A6},{0x87B3,0xE5AE},{0x87BA,0x9786},{0x87BB,0xE5B1}, +{0x87BD,0xE5A8},{0x87C0,0xE5A9},{0x87C4,0xE5AD},{0x87C6,0xE5B0}, +{0x87C7,0xE5AF},{0x87CB,0xE5A7},{0x87D0,0xE5AA},{0x87D2,0xE5BB}, +{0x87E0,0xE5B4},{0x87EF,0xE5B2},{0x87F2,0xE5B3},{0x87F6,0xE5B8}, +{0x87F7,0xE5B9},{0x87F9,0x8A49},{0x87FB,0x8B61},{0x87FE,0xE5B7}, +{0x8805,0xE5A2},{0x8807,0xFBA1},{0x880D,0xE5B6},{0x880E,0xE5BA}, +{0x880F,0xE5B5},{0x8811,0xE5BC},{0x8815,0xE5BE},{0x8816,0xE5BD}, +{0x8821,0xE5C0},{0x8822,0xE5BF},{0x8823,0xE579},{0x8827,0xE5C4}, +{0x8831,0xE5C1},{0x8836,0xE5C2},{0x8839,0xE5C3},{0x883B,0xE5C5}, +{0x8840,0x8C8C},{0x8842,0xE5C7},{0x8844,0xE5C6},{0x8846,0x8F4F}, +{0x884C,0x8D73},{0x884D,0x9FA5},{0x8852,0xE5C8},{0x8853,0x8F70}, +{0x8857,0x8A58},{0x8859,0xE5C9},{0x885B,0x8971},{0x885D,0x8FD5}, +{0x885E,0xE5CA},{0x8861,0x8D74},{0x8862,0xE5CB},{0x8863,0x88DF}, +{0x8868,0x955C},{0x886B,0xE5CC},{0x8870,0x908A},{0x8872,0xE5D3}, +{0x8875,0xE5D0},{0x8877,0x928F},{0x887D,0xE5D1},{0x887E,0xE5CE}, +{0x887F,0x8BDC},{0x8881,0xE5CD},{0x8882,0xE5D4},{0x8888,0x8C55}, +{0x888B,0x91DC},{0x888D,0xE5DA},{0x8892,0xE5D6},{0x8896,0x91B3}, +{0x8897,0xE5D5},{0x8899,0xE5D8},{0x889E,0xE5CF},{0x88A2,0xE5D9}, +{0x88A4,0xE5DB},{0x88AB,0x94ED},{0x88AE,0xE5D7},{0x88B0,0xE5DC}, +{0x88B1,0xE5DE},{0x88B4,0x8CD1},{0x88B5,0xE5D2},{0x88B7,0x88BF}, +{0x88BF,0xE5DD},{0x88C1,0x8DD9},{0x88C2,0x97F4},{0x88C3,0xE5DF}, +{0x88C4,0xE5E0},{0x88C5,0x9195},{0x88CF,0x97A0},{0x88D4,0xE5E1}, +{0x88D5,0x9754},{0x88D8,0xE5E2},{0x88D9,0xE5E3},{0x88DC,0x95E2}, +{0x88DD,0xE5E4},{0x88DF,0x8DBE},{0x88E1,0x97A1},{0x88E8,0xE5E9}, +{0x88F2,0xE5EA},{0x88F3,0x8FD6},{0x88F4,0xE5E8},{0x88F5,0xFBA2}, +{0x88F8,0x9787},{0x88F9,0xE5E5},{0x88FC,0xE5E7},{0x88FD,0x90BB}, +{0x88FE,0x909E},{0x8902,0xE5E6},{0x8904,0xE5EB},{0x8907,0x95A1}, +{0x890A,0xE5ED},{0x890C,0xE5EC},{0x8910,0x8A8C},{0x8912,0x964A}, +{0x8913,0xE5EE},{0x891C,0xFA5D},{0x891D,0xE5FA},{0x891E,0xE5F0}, +{0x8925,0xE5F1},{0x892A,0xE5F2},{0x892B,0xE5F3},{0x8936,0xE5F7}, +{0x8938,0xE5F8},{0x893B,0xE5F6},{0x8941,0xE5F4},{0x8943,0xE5EF}, +{0x8944,0xE5F5},{0x894C,0xE5F9},{0x894D,0xE8B5},{0x8956,0x89A6}, +{0x895E,0xE5FC},{0x895F,0x8BDD},{0x8960,0xE5FB},{0x8964,0xE641}, +{0x8966,0xE640},{0x896A,0xE643},{0x896D,0xE642},{0x896F,0xE644}, +{0x8972,0x8F50},{0x8974,0xE645},{0x8977,0xE646},{0x897E,0xE647}, +{0x897F,0x90BC},{0x8981,0x9776},{0x8983,0xE648},{0x8986,0x95A2}, +{0x8987,0x9465},{0x8988,0xE649},{0x898A,0xE64A},{0x898B,0x8CA9}, +{0x898F,0x8B4B},{0x8993,0xE64B},{0x8996,0x8E8B},{0x8997,0x9460}, +{0x8998,0xE64C},{0x899A,0x8A6F},{0x89A1,0xE64D},{0x89A6,0xE64F}, +{0x89A7,0x9797},{0x89A9,0xE64E},{0x89AA,0x9065},{0x89AC,0xE650}, +{0x89AF,0xE651},{0x89B2,0xE652},{0x89B3,0x8ACF},{0x89BA,0xE653}, +{0x89BD,0xE654},{0x89BF,0xE655},{0x89C0,0xE656},{0x89D2,0x8A70}, +{0x89DA,0xE657},{0x89DC,0xE658},{0x89DD,0xE659},{0x89E3,0x89F0}, +{0x89E6,0x9047},{0x89E7,0xE65A},{0x89F4,0xE65B},{0x89F8,0xE65C}, +{0x8A00,0x8CBE},{0x8A02,0x92F9},{0x8A03,0xE65D},{0x8A08,0x8C76}, +{0x8A0A,0x9075},{0x8A0C,0xE660},{0x8A0E,0x93A2},{0x8A10,0xE65F}, +{0x8A12,0xFBA3},{0x8A13,0x8C50},{0x8A16,0xE65E},{0x8A17,0x91F5}, +{0x8A18,0x8B4C},{0x8A1B,0xE661},{0x8A1D,0xE662},{0x8A1F,0x8FD7}, +{0x8A23,0x8C8D},{0x8A25,0xE663},{0x8A2A,0x964B},{0x8A2D,0x90DD}, +{0x8A31,0x8B96},{0x8A33,0x96F3},{0x8A34,0x9169},{0x8A36,0xE664}, +{0x8A37,0xFBA4},{0x8A3A,0x9066},{0x8A3B,0x9290},{0x8A3C,0x8FD8}, +{0x8A41,0xE665},{0x8A46,0xE668},{0x8A48,0xE669},{0x8A50,0x8DBC}, +{0x8A51,0x91C0},{0x8A52,0xE667},{0x8A54,0x8FD9},{0x8A55,0x955D}, +{0x8A5B,0xE666},{0x8A5E,0x8E8C},{0x8A60,0x8972},{0x8A62,0xE66D}, +{0x8A63,0x8C77},{0x8A66,0x8E8E},{0x8A69,0x8E8D},{0x8A6B,0x986C}, +{0x8A6C,0xE66C},{0x8A6D,0xE66B},{0x8A6E,0x9146},{0x8A70,0x8B6C}, +{0x8A71,0x9862},{0x8A72,0x8A59},{0x8A73,0x8FDA},{0x8A79,0xFBA5}, +{0x8A7C,0xE66A},{0x8A82,0xE66F},{0x8A84,0xE670},{0x8A85,0xE66E}, +{0x8A87,0x8CD6},{0x8A89,0x975F},{0x8A8C,0x8E8F},{0x8A8D,0x9446}, +{0x8A91,0xE673},{0x8A93,0x90BE},{0x8A95,0x9261},{0x8A98,0x9755}, +{0x8A9A,0xE676},{0x8A9E,0x8CEA},{0x8AA0,0x90BD},{0x8AA1,0xE672}, +{0x8AA3,0xE677},{0x8AA4,0x8CEB},{0x8AA5,0xE674},{0x8AA6,0xE675}, +{0x8AA7,0xFBA6},{0x8AA8,0xE671},{0x8AAC,0x90E0},{0x8AAD,0x93C7}, +{0x8AB0,0x924E},{0x8AB2,0x89DB},{0x8AB9,0x94EE},{0x8ABC,0x8B62}, +{0x8ABE,0xFBA7},{0x8ABF,0x92B2},{0x8AC2,0xE67A},{0x8AC4,0xE678}, +{0x8AC7,0x926B},{0x8ACB,0x90BF},{0x8ACC,0x8AD0},{0x8ACD,0xE679}, +{0x8ACF,0x907A},{0x8AD2,0x97C8},{0x8AD6,0x985F},{0x8ADA,0xE67B}, +{0x8ADB,0xE687},{0x8ADC,0x92B3},{0x8ADE,0xE686},{0x8ADF,0xFBA8}, +{0x8AE0,0xE683},{0x8AE1,0xE68B},{0x8AE2,0xE684},{0x8AE4,0xE680}, +{0x8AE6,0x92FA},{0x8AE7,0xE67E},{0x8AEB,0xE67C},{0x8AED,0x9740}, +{0x8AEE,0x8E90},{0x8AF1,0xE681},{0x8AF3,0xE67D},{0x8AF6,0xFBAA}, +{0x8AF7,0xE685},{0x8AF8,0x8F94},{0x8AFA,0x8CBF},{0x8AFE,0x91F8}, +{0x8B00,0x9664},{0x8B01,0x8979},{0x8B02,0x88E0},{0x8B04,0x93A3}, +{0x8B07,0xE689},{0x8B0C,0xE688},{0x8B0E,0x93E4},{0x8B10,0xE68D}, +{0x8B14,0xE682},{0x8B16,0xE68C},{0x8B17,0xE68E},{0x8B19,0x8CAA}, +{0x8B1A,0xE68A},{0x8B1B,0x8D75},{0x8B1D,0x8ED3},{0x8B20,0xE68F}, +{0x8B21,0x9777},{0x8B26,0xE692},{0x8B28,0xE695},{0x8B2B,0xE693}, +{0x8B2C,0x9554},{0x8B33,0xE690},{0x8B39,0x8BDE},{0x8B3E,0xE694}, +{0x8B41,0xE696},{0x8B49,0xE69A},{0x8B4C,0xE697},{0x8B4E,0xE699}, +{0x8B4F,0xE698},{0x8B53,0xFBAB},{0x8B56,0xE69B},{0x8B58,0x8EAF}, +{0x8B5A,0xE69D},{0x8B5B,0xE69C},{0x8B5C,0x9588},{0x8B5F,0xE69F}, +{0x8B66,0x8C78},{0x8B6B,0xE69E},{0x8B6C,0xE6A0},{0x8B6F,0xE6A1}, +{0x8B70,0x8B63},{0x8B71,0xE3BF},{0x8B72,0x8FF7},{0x8B74,0xE6A2}, +{0x8B77,0x8CEC},{0x8B7D,0xE6A3},{0x8B7F,0xFBAC},{0x8B80,0xE6A4}, +{0x8B83,0x8E5D},{0x8B8A,0x9DCC},{0x8B8C,0xE6A5},{0x8B8E,0xE6A6}, +{0x8B90,0x8F51},{0x8B92,0xE6A7},{0x8B93,0xE6A8},{0x8B96,0xE6A9}, +{0x8B99,0xE6AA},{0x8B9A,0xE6AB},{0x8C37,0x924A},{0x8C3A,0xE6AC}, +{0x8C3F,0xE6AE},{0x8C41,0xE6AD},{0x8C46,0x93A4},{0x8C48,0xE6AF}, +{0x8C4A,0x964C},{0x8C4C,0xE6B0},{0x8C4E,0xE6B1},{0x8C50,0xE6B2}, +{0x8C55,0xE6B3},{0x8C5A,0x93D8},{0x8C61,0x8FDB},{0x8C62,0xE6B4}, +{0x8C6A,0x8D8B},{0x8C6B,0x98AC},{0x8C6C,0xE6B5},{0x8C78,0xE6B6}, +{0x8C79,0x955E},{0x8C7A,0xE6B7},{0x8C7C,0xE6BF},{0x8C82,0xE6B8}, +{0x8C85,0xE6BA},{0x8C89,0xE6B9},{0x8C8A,0xE6BB},{0x8C8C,0x9665}, +{0x8C8D,0xE6BC},{0x8C8E,0xE6BD},{0x8C94,0xE6BE},{0x8C98,0xE6C0}, +{0x8C9D,0x8A4C},{0x8C9E,0x92E5},{0x8CA0,0x9589},{0x8CA1,0x8DE0}, +{0x8CA2,0x8D76},{0x8CA7,0x956E},{0x8CA8,0x89DD},{0x8CA9,0x94CC}, +{0x8CAA,0xE6C3},{0x8CAB,0x8AD1},{0x8CAC,0x90D3},{0x8CAD,0xE6C2}, +{0x8CAE,0xE6C7},{0x8CAF,0x9299},{0x8CB0,0x96E1},{0x8CB2,0xE6C5}, +{0x8CB3,0xE6C6},{0x8CB4,0x8B4D},{0x8CB6,0xE6C8},{0x8CB7,0x9483}, +{0x8CB8,0x91DD},{0x8CBB,0x94EF},{0x8CBC,0x935C},{0x8CBD,0xE6C4}, +{0x8CBF,0x9666},{0x8CC0,0x89EA},{0x8CC1,0xE6CA},{0x8CC2,0x9847}, +{0x8CC3,0x92C0},{0x8CC4,0x9864},{0x8CC7,0x8E91},{0x8CC8,0xE6C9}, +{0x8CCA,0x91AF},{0x8CCD,0xE6DA},{0x8CCE,0x9147},{0x8CD1,0x93F6}, +{0x8CD3,0x956F},{0x8CDA,0xE6CD},{0x8CDB,0x8E5E},{0x8CDC,0x8E92}, +{0x8CDE,0x8FDC},{0x8CE0,0x9485},{0x8CE2,0x8CAB},{0x8CE3,0xE6CC}, +{0x8CE4,0xE6CB},{0x8CE6,0x958A},{0x8CEA,0x8EBF},{0x8CED,0x9371}, +{0x8CF0,0xFBAD},{0x8CF4,0xFBAE},{0x8CFA,0xE6CF},{0x8CFB,0xE6D0}, +{0x8CFC,0x8D77},{0x8CFD,0xE6CE},{0x8D04,0xE6D1},{0x8D05,0xE6D2}, +{0x8D07,0xE6D4},{0x8D08,0x91A1},{0x8D0A,0xE6D3},{0x8D0B,0x8AE4}, +{0x8D0D,0xE6D6},{0x8D0F,0xE6D5},{0x8D10,0xE6D7},{0x8D12,0xFBAF}, +{0x8D13,0xE6D9},{0x8D14,0xE6DB},{0x8D16,0xE6DC},{0x8D64,0x90D4}, +{0x8D66,0x8ECD},{0x8D67,0xE6DD},{0x8D6B,0x8A71},{0x8D6D,0xE6DE}, +{0x8D70,0x9196},{0x8D71,0xE6DF},{0x8D73,0xE6E0},{0x8D74,0x958B}, +{0x8D76,0xFBB0},{0x8D77,0x8B4E},{0x8D81,0xE6E1},{0x8D85,0x92B4}, +{0x8D8A,0x897A},{0x8D99,0xE6E2},{0x8DA3,0x8EEF},{0x8DA8,0x9096}, +{0x8DB3,0x91AB},{0x8DBA,0xE6E5},{0x8DBE,0xE6E4},{0x8DC2,0xE6E3}, +{0x8DCB,0xE6EB},{0x8DCC,0xE6E9},{0x8DCF,0xE6E6},{0x8DD6,0xE6E8}, +{0x8DDA,0xE6E7},{0x8DDB,0xE6EA},{0x8DDD,0x8B97},{0x8DDF,0xE6EE}, +{0x8DE1,0x90D5},{0x8DE3,0xE6EF},{0x8DE8,0x8CD7},{0x8DEA,0xE6EC}, +{0x8DEB,0xE6ED},{0x8DEF,0x9848},{0x8DF3,0x92B5},{0x8DF5,0x9148}, +{0x8DFC,0xE6F0},{0x8DFF,0xE6F3},{0x8E08,0xE6F1},{0x8E09,0xE6F2}, +{0x8E0A,0x9778},{0x8E0F,0x93A5},{0x8E10,0xE6F6},{0x8E1D,0xE6F4}, +{0x8E1E,0xE6F5},{0x8E1F,0xE6F7},{0x8E2A,0xE748},{0x8E30,0xE6FA}, +{0x8E34,0xE6FB},{0x8E35,0xE6F9},{0x8E42,0xE6F8},{0x8E44,0x92FB}, +{0x8E47,0xE740},{0x8E48,0xE744},{0x8E49,0xE741},{0x8E4A,0xE6FC}, +{0x8E4C,0xE742},{0x8E50,0xE743},{0x8E55,0xE74A},{0x8E59,0xE745}, +{0x8E5F,0x90D6},{0x8E60,0xE747},{0x8E63,0xE749},{0x8E64,0xE746}, +{0x8E72,0xE74C},{0x8E74,0x8F52},{0x8E76,0xE74B},{0x8E7C,0xE74D}, +{0x8E81,0xE74E},{0x8E84,0xE751},{0x8E85,0xE750},{0x8E87,0xE74F}, +{0x8E8A,0xE753},{0x8E8B,0xE752},{0x8E8D,0x96F4},{0x8E91,0xE755}, +{0x8E93,0xE754},{0x8E94,0xE756},{0x8E99,0xE757},{0x8EA1,0xE759}, +{0x8EAA,0xE758},{0x8EAB,0x9067},{0x8EAC,0xE75A},{0x8EAF,0x8BEB}, +{0x8EB0,0xE75B},{0x8EB1,0xE75D},{0x8EBE,0xE75E},{0x8EC5,0xE75F}, +{0x8EC6,0xE75C},{0x8EC8,0xE760},{0x8ECA,0x8ED4},{0x8ECB,0xE761}, +{0x8ECC,0x8B4F},{0x8ECD,0x8C52},{0x8ECF,0xFBB2},{0x8ED2,0x8CAC}, +{0x8EDB,0xE762},{0x8EDF,0x93EE},{0x8EE2,0x935D},{0x8EE3,0xE763}, +{0x8EEB,0xE766},{0x8EF8,0x8EB2},{0x8EFB,0xE765},{0x8EFC,0xE764}, +{0x8EFD,0x8C79},{0x8EFE,0xE767},{0x8F03,0x8A72},{0x8F05,0xE769}, +{0x8F09,0x8DDA},{0x8F0A,0xE768},{0x8F0C,0xE771},{0x8F12,0xE76B}, +{0x8F13,0xE76D},{0x8F14,0x95E3},{0x8F15,0xE76A},{0x8F19,0xE76C}, +{0x8F1B,0xE770},{0x8F1C,0xE76E},{0x8F1D,0x8B50},{0x8F1F,0xE76F}, +{0x8F26,0xE772},{0x8F29,0x9479},{0x8F2A,0x97D6},{0x8F2F,0x8F53}, +{0x8F33,0xE773},{0x8F38,0x9741},{0x8F39,0xE775},{0x8F3B,0xE774}, +{0x8F3E,0xE778},{0x8F3F,0x9760},{0x8F42,0xE777},{0x8F44,0x8A8D}, +{0x8F45,0xE776},{0x8F46,0xE77B},{0x8F49,0xE77A},{0x8F4C,0xE779}, +{0x8F4D,0x9351},{0x8F4E,0xE77C},{0x8F57,0xE77D},{0x8F5C,0xE77E}, +{0x8F5F,0x8D8C},{0x8F61,0x8C44},{0x8F62,0xE780},{0x8F63,0xE781}, +{0x8F64,0xE782},{0x8F9B,0x9068},{0x8F9C,0xE783},{0x8F9E,0x8EAB}, +{0x8F9F,0xE784},{0x8FA3,0xE785},{0x8FA7,0x999F},{0x8FA8,0x999E}, +{0x8FAD,0xE786},{0x8FAE,0xE390},{0x8FAF,0xE787},{0x8FB0,0x9243}, +{0x8FB1,0x904A},{0x8FB2,0x945F},{0x8FB7,0xE788},{0x8FBA,0x95D3}, +{0x8FBB,0x92D2},{0x8FBC,0x8D9E},{0x8FBF,0x9248},{0x8FC2,0x8949}, +{0x8FC4,0x9698},{0x8FC5,0x9076},{0x8FCE,0x8C7D},{0x8FD1,0x8BDF}, +{0x8FD4,0x95D4},{0x8FDA,0xE789},{0x8FE2,0xE78B},{0x8FE5,0xE78A}, +{0x8FE6,0x89DE},{0x8FE9,0x93F4},{0x8FEA,0xE78C},{0x8FEB,0x9497}, +{0x8FED,0x9352},{0x8FEF,0xE78D},{0x8FF0,0x8F71},{0x8FF4,0xE78F}, +{0x8FF7,0x96C0},{0x8FF8,0xE79E},{0x8FF9,0xE791},{0x8FFA,0xE792}, +{0x8FFD,0x92C7},{0x9000,0x91DE},{0x9001,0x9197},{0x9003,0x93A6}, +{0x9005,0xE790},{0x9006,0x8B74},{0x900B,0xE799},{0x900D,0xE796}, +{0x900E,0xE7A3},{0x900F,0x93A7},{0x9010,0x9280},{0x9011,0xE793}, +{0x9013,0x92FC},{0x9014,0x9372},{0x9015,0xE794},{0x9016,0xE798}, +{0x9017,0x9080},{0x9019,0x9487},{0x901A,0x92CA},{0x901D,0x90C0}, +{0x901E,0xE797},{0x901F,0x91AC},{0x9020,0x91A2},{0x9021,0xE795}, +{0x9022,0x88A7},{0x9023,0x9841},{0x9027,0xE79A},{0x902E,0x91DF}, +{0x9031,0x8F54},{0x9032,0x9069},{0x9035,0xE79C},{0x9036,0xE79B}, +{0x9038,0x88ED},{0x9039,0xE79D},{0x903C,0x954E},{0x903E,0xE7A5}, +{0x9041,0x93D9},{0x9042,0x908B},{0x9045,0x9278},{0x9047,0x8BF6}, +{0x9049,0xE7A4},{0x904A,0x9756},{0x904B,0x895E},{0x904D,0x95D5}, +{0x904E,0x89DF},{0x904F,0xE79F},{0x9050,0xE7A0},{0x9051,0xE7A1}, +{0x9052,0xE7A2},{0x9053,0x93B9},{0x9054,0x9242},{0x9055,0x88E1}, +{0x9056,0xE7A6},{0x9058,0xE7A7},{0x9059,0xEAA1},{0x905C,0x91BB}, +{0x905E,0xE7A8},{0x9060,0x8993},{0x9061,0x916B},{0x9063,0x8CAD}, +{0x9065,0x9779},{0x9067,0xFBB5},{0x9068,0xE7A9},{0x9069,0x934B}, +{0x906D,0x9198},{0x906E,0x8ED5},{0x906F,0xE7AA},{0x9072,0xE7AD}, +{0x9075,0x8F85},{0x9076,0xE7AB},{0x9077,0x914A},{0x9078,0x9149}, +{0x907A,0x88E2},{0x907C,0x97C9},{0x907D,0xE7AF},{0x907F,0x94F0}, +{0x9080,0xE7B1},{0x9081,0xE7B0},{0x9082,0xE7AE},{0x9083,0xE284}, +{0x9084,0x8AD2},{0x9087,0xE78E},{0x9089,0xE7B3},{0x908A,0xE7B2}, +{0x908F,0xE7B4},{0x9091,0x9757},{0x90A3,0x93DF},{0x90A6,0x964D}, +{0x90A8,0xE7B5},{0x90AA,0x8ED7},{0x90AF,0xE7B6},{0x90B1,0xE7B7}, +{0x90B5,0xE7B8},{0x90B8,0x9340},{0x90C1,0x88E8},{0x90CA,0x8D78}, +{0x90CE,0x9859},{0x90DB,0xE7BC},{0x90DE,0xFBB6},{0x90E1,0x8C53}, +{0x90E2,0xE7B9},{0x90E4,0xE7BA},{0x90E8,0x9594},{0x90ED,0x8A73}, +{0x90F5,0x9758},{0x90F7,0x8BBD},{0x90FD,0x9373},{0x9102,0xE7BD}, +{0x9112,0xE7BE},{0x9115,0xFBB8},{0x9119,0xE7BF},{0x9127,0xFBB9}, +{0x912D,0x9341},{0x9130,0xE7C1},{0x9132,0xE7C0},{0x9149,0x93D1}, +{0x914A,0xE7C2},{0x914B,0x8F55},{0x914C,0x8EDE},{0x914D,0x947A}, +{0x914E,0x9291},{0x9152,0x8EF0},{0x9154,0x908C},{0x9156,0xE7C3}, +{0x9158,0xE7C4},{0x9162,0x907C},{0x9163,0xE7C5},{0x9165,0xE7C6}, +{0x9169,0xE7C7},{0x916A,0x978F},{0x916C,0x8F56},{0x9172,0xE7C9}, +{0x9173,0xE7C8},{0x9175,0x8D79},{0x9177,0x8D93},{0x9178,0x8E5F}, +{0x9182,0xE7CC},{0x9187,0x8F86},{0x9189,0xE7CB},{0x918B,0xE7CA}, +{0x918D,0x91E7},{0x9190,0x8CED},{0x9192,0x90C1},{0x9197,0x94AE}, +{0x919C,0x8F58},{0x91A2,0xE7CD},{0x91A4,0x8FDD},{0x91AA,0xE7D0}, +{0x91AB,0xE7CE},{0x91AF,0xE7CF},{0x91B4,0xE7D2},{0x91B5,0xE7D1}, +{0x91B8,0x8FF8},{0x91BA,0xE7D3},{0x91C0,0xE7D4},{0x91C1,0xE7D5}, +{0x91C6,0x94CE},{0x91C7,0x8DD1},{0x91C8,0x8EDF},{0x91C9,0xE7D6}, +{0x91CB,0xE7D7},{0x91CC,0x97A2},{0x91CD,0x8F64},{0x91CE,0x96EC}, +{0x91CF,0x97CA},{0x91D0,0xE7D8},{0x91D1,0x8BE0},{0x91D6,0xE7D9}, +{0x91D7,0xFBBB},{0x91D8,0x9342},{0x91DA,0xFBBA},{0x91DB,0xE7DC}, +{0x91DC,0x8A98},{0x91DD,0x906A},{0x91DE,0xFBBC},{0x91DF,0xE7DA}, +{0x91E1,0xE7DB},{0x91E3,0x92DE},{0x91E4,0xFBBF},{0x91E5,0xFBC0}, +{0x91E6,0x9674},{0x91E7,0x8BFA},{0x91ED,0xFBBD},{0x91EE,0xFBBE}, +{0x91F5,0xE7DE},{0x91F6,0xE7DF},{0x91FC,0xE7DD},{0x91FF,0xE7E1}, +{0x9206,0xFBC1},{0x920A,0xFBC3},{0x920D,0x93DD},{0x920E,0x8A62}, +{0x9210,0xFBC2},{0x9211,0xE7E5},{0x9214,0xE7E2},{0x9215,0xE7E4}, +{0x921E,0xE7E0},{0x9229,0xE86E},{0x922C,0xE7E3},{0x9234,0x97E9}, +{0x9237,0x8CD8},{0x9239,0xFBCA},{0x923A,0xFBC4},{0x923C,0xFBC6}, +{0x923F,0xE7ED},{0x9240,0xFBC5},{0x9244,0x9353},{0x9245,0xE7E8}, +{0x9248,0xE7EB},{0x9249,0xE7E9},{0x924B,0xE7EE},{0x924E,0xFBC7}, +{0x9250,0xE7EF},{0x9251,0xFBC9},{0x9257,0xE7E7},{0x9259,0xFBC8}, +{0x925A,0xE7F4},{0x925B,0x8994},{0x925E,0xE7E6},{0x9262,0x94AB}, +{0x9264,0xE7EA},{0x9266,0x8FDE},{0x9267,0xFBCB},{0x9271,0x8D7A}, +{0x9277,0xFBCD},{0x9278,0xFBCE},{0x927E,0x9667},{0x9280,0x8BE2}, +{0x9283,0x8F65},{0x9285,0x93BA},{0x9288,0xFA5F},{0x9291,0x914C}, +{0x9293,0xE7F2},{0x9295,0xE7EC},{0x9296,0xE7F1},{0x9298,0x96C1}, +{0x929A,0x92B6},{0x929B,0xE7F3},{0x929C,0xE7F0},{0x92A7,0xFBCC}, +{0x92AD,0x914B},{0x92B7,0xE7F7},{0x92B9,0xE7F6},{0x92CF,0xE7F5}, +{0x92D0,0xFBD2},{0x92D2,0x964E},{0x92D3,0xFBD6},{0x92D5,0xFBD4}, +{0x92D7,0xFBD0},{0x92D9,0xFBD1},{0x92E0,0xFBD5},{0x92E4,0x8F9B}, +{0x92E7,0xFBCF},{0x92E9,0xE7F8},{0x92EA,0x95DD},{0x92ED,0x8973}, +{0x92F2,0x9565},{0x92F3,0x9292},{0x92F8,0x8B98},{0x92F9,0xFA65}, +{0x92FA,0xE7FA},{0x92FB,0xFBD9},{0x92FC,0x8D7C},{0x92FF,0xFBDC}, +{0x9302,0xFBDE},{0x9306,0x8E4B},{0x930F,0xE7F9},{0x9310,0x908D}, +{0x9318,0x908E},{0x9319,0xE840},{0x931A,0xE842},{0x931D,0xFBDD}, +{0x931E,0xFBDB},{0x9320,0x8FF9},{0x9321,0xFBD8},{0x9322,0xE841}, +{0x9323,0xE843},{0x9325,0xFBD7},{0x9326,0x8BD1},{0x9328,0x9564}, +{0x932B,0x8EE0},{0x932C,0x9842},{0x932E,0xE7FC},{0x932F,0x8DF6}, +{0x9332,0x985E},{0x9335,0xE845},{0x933A,0xE844},{0x933B,0xE846}, +{0x9344,0xE7FB},{0x9348,0xFA5E},{0x934B,0x93E7},{0x934D,0x9374}, +{0x9354,0x92D5},{0x9356,0xE84B},{0x9357,0xFBE0},{0x935B,0x9262}, +{0x935C,0xE847},{0x9360,0xE848},{0x936C,0x8C4C},{0x936E,0xE84A}, +{0x9370,0xFBDF},{0x9375,0x8CAE},{0x937C,0xE849},{0x937E,0x8FDF}, +{0x938C,0x8A99},{0x9394,0xE84F},{0x9396,0x8DBD},{0x9397,0x9199}, +{0x939A,0x92C8},{0x93A4,0xFBE1},{0x93A7,0x8A5A},{0x93AC,0xE84D}, +{0x93AD,0xE84E},{0x93AE,0x92C1},{0x93B0,0xE84C},{0x93B9,0xE850}, +{0x93C3,0xE856},{0x93C6,0xFBE2},{0x93C8,0xE859},{0x93D0,0xE858}, +{0x93D1,0x934C},{0x93D6,0xE851},{0x93D7,0xE852},{0x93D8,0xE855}, +{0x93DD,0xE857},{0x93DE,0xFBE3},{0x93E1,0x8BBE},{0x93E4,0xE85A}, +{0x93E5,0xE854},{0x93E8,0xE853},{0x93F8,0xFBE4},{0x9403,0xE85E}, +{0x9407,0xE85F},{0x9410,0xE860},{0x9413,0xE85D},{0x9414,0xE85C}, +{0x9418,0x8FE0},{0x9419,0x93A8},{0x941A,0xE85B},{0x9421,0xE864}, +{0x942B,0xE862},{0x9431,0xFBE5},{0x9435,0xE863},{0x9436,0xE861}, +{0x9438,0x91F6},{0x943A,0xE865},{0x9441,0xE866},{0x9444,0xE868}, +{0x9445,0xFBE6},{0x9448,0xFBE7},{0x9451,0x8AD3},{0x9452,0xE867}, +{0x9453,0x96F8},{0x945A,0xE873},{0x945B,0xE869},{0x945E,0xE86C}, +{0x9460,0xE86A},{0x9462,0xE86B},{0x946A,0xE86D},{0x9470,0xE86F}, +{0x9475,0xE870},{0x9477,0xE871},{0x947C,0xE874},{0x947D,0xE872}, +{0x947E,0xE875},{0x947F,0xE877},{0x9481,0xE876},{0x9577,0x92B7}, +{0x9580,0x96E5},{0x9582,0xE878},{0x9583,0x914D},{0x9587,0xE879}, +{0x9589,0x95C2},{0x958A,0xE87A},{0x958B,0x8A4A},{0x958F,0x895B}, +{0x9591,0x8AD5},{0x9592,0xFBE8},{0x9593,0x8AD4},{0x9594,0xE87B}, +{0x9596,0xE87C},{0x9598,0xE87D},{0x9599,0xE87E},{0x95A0,0xE880}, +{0x95A2,0x8AD6},{0x95A3,0x8A74},{0x95A4,0x8D7D},{0x95A5,0x94B4}, +{0x95A7,0xE882},{0x95A8,0xE881},{0x95AD,0xE883},{0x95B2,0x897B}, +{0x95B9,0xE886},{0x95BB,0xE885},{0x95BC,0xE884},{0x95BE,0xE887}, +{0x95C3,0xE88A},{0x95C7,0x88C5},{0x95CA,0xE888},{0x95CC,0xE88C}, +{0x95CD,0xE88B},{0x95D4,0xE88E},{0x95D5,0xE88D},{0x95D6,0xE88F}, +{0x95D8,0x93AC},{0x95DC,0xE890},{0x95E1,0xE891},{0x95E2,0xE893}, +{0x95E5,0xE892},{0x961C,0x958C},{0x9621,0xE894},{0x9628,0xE895}, +{0x962A,0x8DE3},{0x962E,0xE896},{0x962F,0xE897},{0x9632,0x9668}, +{0x963B,0x916A},{0x963F,0x88A2},{0x9640,0x91C9},{0x9642,0xE898}, +{0x9644,0x958D},{0x964B,0xE89B},{0x964C,0xE899},{0x964D,0x8D7E}, +{0x964F,0xE89A},{0x9650,0x8CC0},{0x965B,0x95C3},{0x965C,0xE89D}, +{0x965D,0xE89F},{0x965E,0xE89E},{0x965F,0xE8A0},{0x9662,0x8940}, +{0x9663,0x9077},{0x9664,0x8F9C},{0x9665,0x8AD7},{0x9666,0xE8A1}, +{0x966A,0x9486},{0x966C,0xE8A3},{0x9670,0x8941},{0x9672,0xE8A2}, +{0x9673,0x92C2},{0x9675,0x97CB},{0x9676,0x93A9},{0x9677,0xE89C}, +{0x9678,0x97A4},{0x967A,0x8CAF},{0x967D,0x977A},{0x9685,0x8BF7}, +{0x9686,0x97B2},{0x9688,0x8C47},{0x968A,0x91E0},{0x968B,0xE440}, +{0x968D,0xE8A4},{0x968E,0x8A4B},{0x968F,0x908F},{0x9694,0x8A75}, +{0x9695,0xE8A6},{0x9697,0xE8A7},{0x9698,0xE8A5},{0x9699,0x8C84}, +{0x969B,0x8DDB},{0x969C,0x8FE1},{0x969D,0xFBEB},{0x96A0,0x8942}, +{0x96A3,0x97D7},{0x96A7,0xE8A9},{0x96A8,0xE7AC},{0x96AA,0xE8A8}, +{0x96AF,0xFBEC},{0x96B0,0xE8AC},{0x96B1,0xE8AA},{0x96B2,0xE8AB}, +{0x96B4,0xE8AD},{0x96B6,0xE8AE},{0x96B7,0x97EA},{0x96B8,0xE8AF}, +{0x96B9,0xE8B0},{0x96BB,0x90C7},{0x96BC,0x94B9},{0x96C0,0x909D}, +{0x96C1,0x8AE5},{0x96C4,0x9759},{0x96C5,0x89EB},{0x96C6,0x8F57}, +{0x96C7,0x8CD9},{0x96C9,0xE8B3},{0x96CB,0xE8B2},{0x96CC,0x8E93}, +{0x96CD,0xE8B4},{0x96CE,0xE8B1},{0x96D1,0x8E47},{0x96D5,0xE8B8}, +{0x96D6,0xE5AB},{0x96D9,0x99D4},{0x96DB,0x9097},{0x96DC,0xE8B6}, +{0x96E2,0x97A3},{0x96E3,0x93EF},{0x96E8,0x894A},{0x96EA,0x90E1}, +{0x96EB,0x8EB4},{0x96F0,0x95B5},{0x96F2,0x895F},{0x96F6,0x97EB}, +{0x96F7,0x978B},{0x96F9,0xE8B9},{0x96FB,0x9364},{0x9700,0x8EF9}, +{0x9704,0xE8BA},{0x9706,0xE8BB},{0x9707,0x906B},{0x9708,0xE8BC}, +{0x970A,0x97EC},{0x970D,0xE8B7},{0x970E,0xE8BE},{0x970F,0xE8C0}, +{0x9711,0xE8BF},{0x9713,0xE8BD},{0x9716,0xE8C1},{0x9719,0xE8C2}, +{0x971C,0x919A},{0x971E,0x89E0},{0x9724,0xE8C3},{0x9727,0x96B6}, +{0x972A,0xE8C4},{0x9730,0xE8C5},{0x9732,0x9849},{0x9733,0xFBED}, +{0x9738,0x9E50},{0x9739,0xE8C6},{0x973B,0xFBEE},{0x973D,0xE8C7}, +{0x973E,0xE8C8},{0x9742,0xE8CC},{0x9743,0xFBEF},{0x9744,0xE8C9}, +{0x9746,0xE8CA},{0x9748,0xE8CB},{0x9749,0xE8CD},{0x974D,0xFBF0}, +{0x974F,0xFBF1},{0x9751,0xFBF2},{0x9752,0x90C2},{0x9755,0xFBF3}, +{0x9756,0x96F5},{0x9759,0x90C3},{0x975C,0xE8CE},{0x975E,0x94F1}, +{0x9760,0xE8CF},{0x9761,0xEA72},{0x9762,0x96CA},{0x9764,0xE8D0}, +{0x9766,0xE8D1},{0x9768,0xE8D2},{0x9769,0x8A76},{0x976B,0xE8D4}, +{0x976D,0x9078},{0x9771,0xE8D5},{0x9774,0x8C43},{0x9779,0xE8D6}, +{0x977A,0xE8DA},{0x977C,0xE8D8},{0x9781,0xE8D9},{0x9784,0x8A93}, +{0x9785,0xE8D7},{0x9786,0xE8DB},{0x978B,0xE8DC},{0x978D,0x88C6}, +{0x978F,0xE8DD},{0x9790,0xE8DE},{0x9798,0x8FE2},{0x979C,0xE8DF}, +{0x97A0,0x8B66},{0x97A3,0xE8E2},{0x97A6,0xE8E1},{0x97A8,0xE8E0}, +{0x97AB,0xE691},{0x97AD,0x95DA},{0x97B3,0xE8E3},{0x97B4,0xE8E4}, +{0x97C3,0xE8E5},{0x97C6,0xE8E6},{0x97C8,0xE8E7},{0x97CB,0xE8E8}, +{0x97D3,0x8AD8},{0x97DC,0xE8E9},{0x97ED,0xE8EA},{0x97EE,0x9442}, +{0x97F2,0xE8EC},{0x97F3,0x89B9},{0x97F5,0xE8EF},{0x97F6,0xE8EE}, +{0x97FB,0x8943},{0x97FF,0x8BBF},{0x9801,0x95C5},{0x9802,0x92B8}, +{0x9803,0x8DA0},{0x9805,0x8D80},{0x9806,0x8F87},{0x9808,0x907B}, +{0x980C,0xE8F1},{0x980F,0xE8F0},{0x9810,0x9761},{0x9811,0x8AE6}, +{0x9812,0x94D0},{0x9813,0x93DA},{0x9817,0x909C},{0x9818,0x97CC}, +{0x981A,0x8C7A},{0x9821,0xE8F4},{0x9824,0xE8F3},{0x982C,0x966A}, +{0x982D,0x93AA},{0x9834,0x896F},{0x9837,0xE8F5},{0x9838,0xE8F2}, +{0x983B,0x9570},{0x983C,0x978A},{0x983D,0xE8F6},{0x9846,0xE8F7}, +{0x984B,0xE8F9},{0x984C,0x91E8},{0x984D,0x8A7A},{0x984E,0x8A7B}, +{0x984F,0xE8F8},{0x9854,0x8AE7},{0x9855,0x8CB0},{0x9857,0xFBF4}, +{0x9858,0x8AE8},{0x985B,0x935E},{0x985E,0x97DE},{0x9865,0xFBF5}, +{0x9867,0x8CDA},{0x986B,0xE8FA},{0x986F,0xE8FB},{0x9870,0xE8FC}, +{0x9871,0xE940},{0x9873,0xE942},{0x9874,0xE941},{0x98A8,0x9597}, +{0x98AA,0xE943},{0x98AF,0xE944},{0x98B1,0xE945},{0x98B6,0xE946}, +{0x98C3,0xE948},{0x98C4,0xE947},{0x98C6,0xE949},{0x98DB,0x94F2}, +{0x98DC,0xE3CA},{0x98DF,0x9048},{0x98E2,0x8B51},{0x98E9,0xE94A}, +{0x98EB,0xE94B},{0x98ED,0x99AA},{0x98EE,0x9F5A},{0x98EF,0x94D1}, +{0x98F2,0x88F9},{0x98F4,0x88B9},{0x98FC,0x8E94},{0x98FD,0x964F}, +{0x98FE,0x8FFC},{0x9903,0xE94C},{0x9905,0x96DD},{0x9909,0xE94D}, +{0x990A,0x977B},{0x990C,0x8961},{0x9910,0x8E60},{0x9912,0xE94E}, +{0x9913,0x89EC},{0x9914,0xE94F},{0x9918,0xE950},{0x991D,0xE952}, +{0x991E,0xE953},{0x9920,0xE955},{0x9921,0xE951},{0x9924,0xE954}, +{0x9927,0xFBF8},{0x9928,0x8AD9},{0x992C,0xE956},{0x992E,0xE957}, +{0x993D,0xE958},{0x993E,0xE959},{0x9942,0xE95A},{0x9945,0xE95C}, +{0x9949,0xE95B},{0x994B,0xE95E},{0x994C,0xE961},{0x9950,0xE95D}, +{0x9951,0xE95F},{0x9952,0xE960},{0x9955,0xE962},{0x9957,0x8BC0}, +{0x9996,0x8EF1},{0x9997,0xE963},{0x9998,0xE964},{0x9999,0x8D81}, +{0x999E,0xFBFA},{0x99A5,0xE965},{0x99A8,0x8A5D},{0x99AC,0x946E}, +{0x99AD,0xE966},{0x99AE,0xE967},{0x99B3,0x9279},{0x99B4,0x93E9}, +{0x99BC,0xE968},{0x99C1,0x949D},{0x99C4,0x91CA},{0x99C5,0x8977}, +{0x99C6,0x8BEC},{0x99C8,0x8BED},{0x99D0,0x9293},{0x99D1,0xE96D}, +{0x99D2,0x8BEE},{0x99D5,0x89ED},{0x99D8,0xE96C},{0x99DB,0xE96A}, +{0x99DD,0xE96B},{0x99DF,0xE969},{0x99E2,0xE977},{0x99ED,0xE96E}, +{0x99EE,0xE96F},{0x99F1,0xE970},{0x99F2,0xE971},{0x99F8,0xE973}, +{0x99FB,0xE972},{0x99FF,0x8F78},{0x9A01,0xE974},{0x9A05,0xE976}, +{0x9A0E,0x8B52},{0x9A0F,0xE975},{0x9A12,0x919B},{0x9A13,0x8CB1}, +{0x9A19,0xE978},{0x9A28,0x91CB},{0x9A2B,0xE979},{0x9A30,0x93AB}, +{0x9A37,0xE97A},{0x9A3E,0xE980},{0x9A40,0xE97D},{0x9A42,0xE97C}, +{0x9A43,0xE97E},{0x9A45,0xE97B},{0x9A4D,0xE982},{0x9A4E,0xFBFB}, +{0x9A55,0xE981},{0x9A57,0xE984},{0x9A5A,0x8BC1},{0x9A5B,0xE983}, +{0x9A5F,0xE985},{0x9A62,0xE986},{0x9A64,0xE988},{0x9A65,0xE987}, +{0x9A69,0xE989},{0x9A6A,0xE98B},{0x9A6B,0xE98A},{0x9AA8,0x8D9C}, +{0x9AAD,0xE98C},{0x9AB0,0xE98D},{0x9AB8,0x8A5B},{0x9ABC,0xE98E}, +{0x9AC0,0xE98F},{0x9AC4,0x9091},{0x9ACF,0xE990},{0x9AD1,0xE991}, +{0x9AD3,0xE992},{0x9AD4,0xE993},{0x9AD8,0x8D82},{0x9AD9,0xFBFC}, +{0x9ADC,0xFC40},{0x9ADE,0xE994},{0x9ADF,0xE995},{0x9AE2,0xE996}, +{0x9AE3,0xE997},{0x9AE6,0xE998},{0x9AEA,0x94AF},{0x9AEB,0xE99A}, +{0x9AED,0x9545},{0x9AEE,0xE99B},{0x9AEF,0xE999},{0x9AF1,0xE99D}, +{0x9AF4,0xE99C},{0x9AF7,0xE99E},{0x9AFB,0xE99F},{0x9B06,0xE9A0}, +{0x9B18,0xE9A1},{0x9B1A,0xE9A2},{0x9B1F,0xE9A3},{0x9B22,0xE9A4}, +{0x9B23,0xE9A5},{0x9B25,0xE9A6},{0x9B27,0xE9A7},{0x9B28,0xE9A8}, +{0x9B29,0xE9A9},{0x9B2A,0xE9AA},{0x9B2E,0xE9AB},{0x9B2F,0xE9AC}, +{0x9B31,0x9F54},{0x9B32,0xE9AD},{0x9B3B,0xE2F6},{0x9B3C,0x8B53}, +{0x9B41,0x8A40},{0x9B42,0x8DB0},{0x9B43,0xE9AF},{0x9B44,0xE9AE}, +{0x9B45,0x96A3},{0x9B4D,0xE9B1},{0x9B4E,0xE9B2},{0x9B4F,0xE9B0}, +{0x9B51,0xE9B3},{0x9B54,0x9682},{0x9B58,0xE9B4},{0x9B5A,0x8B9B}, +{0x9B6F,0x9844},{0x9B72,0xFC42},{0x9B74,0xE9B5},{0x9B75,0xFC41}, +{0x9B83,0xE9B7},{0x9B8E,0x88BC},{0x9B8F,0xFC43},{0x9B91,0xE9B8}, +{0x9B92,0x95A9},{0x9B93,0xE9B6},{0x9B96,0xE9B9},{0x9B97,0xE9BA}, +{0x9B9F,0xE9BB},{0x9BA0,0xE9BC},{0x9BA8,0xE9BD},{0x9BAA,0x968E}, +{0x9BAB,0x8E4C},{0x9BAD,0x8DF8},{0x9BAE,0x914E},{0x9BB1,0xFC44}, +{0x9BB4,0xE9BE},{0x9BB9,0xE9C1},{0x9BBB,0xFC45},{0x9BC0,0xE9BF}, +{0x9BC6,0xE9C2},{0x9BC9,0x8CEF},{0x9BCA,0xE9C0},{0x9BCF,0xE9C3}, +{0x9BD1,0xE9C4},{0x9BD2,0xE9C5},{0x9BD4,0xE9C9},{0x9BD6,0x8E49}, +{0x9BDB,0x91E2},{0x9BE1,0xE9CA},{0x9BE2,0xE9C7},{0x9BE3,0xE9C6}, +{0x9BE4,0xE9C8},{0x9BE8,0x8C7E},{0x9BF0,0xE9CE},{0x9BF1,0xE9CD}, +{0x9BF2,0xE9CC},{0x9BF5,0x88B1},{0x9C00,0xFC46},{0x9C04,0xE9D8}, +{0x9C06,0xE9D4},{0x9C08,0xE9D5},{0x9C09,0xE9D1},{0x9C0A,0xE9D7}, +{0x9C0C,0xE9D3},{0x9C0D,0x8A82},{0x9C10,0x986B},{0x9C12,0xE9D6}, +{0x9C13,0xE9D2},{0x9C14,0xE9D0},{0x9C15,0xE9CF},{0x9C1B,0xE9DA}, +{0x9C21,0xE9DD},{0x9C24,0xE9DC},{0x9C25,0xE9DB},{0x9C2D,0x9568}, +{0x9C2E,0xE9D9},{0x9C2F,0x88F1},{0x9C30,0xE9DE},{0x9C32,0xE9E0}, +{0x9C39,0x8A8F},{0x9C3A,0xE9CB},{0x9C3B,0x8956},{0x9C3E,0xE9E2}, +{0x9C46,0xE9E1},{0x9C47,0xE9DF},{0x9C48,0x924C},{0x9C52,0x9690}, +{0x9C57,0x97D8},{0x9C5A,0xE9E3},{0x9C60,0xE9E4},{0x9C67,0xE9E5}, +{0x9C76,0xE9E6},{0x9C78,0xE9E7},{0x9CE5,0x92B9},{0x9CE7,0xE9E8}, +{0x9CE9,0x94B5},{0x9CEB,0xE9ED},{0x9CEC,0xE9E9},{0x9CF0,0xE9EA}, +{0x9CF3,0x9650},{0x9CF4,0x96C2},{0x9CF6,0x93CE},{0x9D03,0xE9EE}, +{0x9D06,0xE9EF},{0x9D07,0x93BC},{0x9D08,0xE9EC},{0x9D09,0xE9EB}, +{0x9D0E,0x89A8},{0x9D12,0xE9F7},{0x9D15,0xE9F6},{0x9D1B,0x8995}, +{0x9D1F,0xE9F4},{0x9D23,0xE9F3},{0x9D26,0xE9F1},{0x9D28,0x8A9B}, +{0x9D2A,0xE9F0},{0x9D2B,0x8EB0},{0x9D2C,0x89A7},{0x9D3B,0x8D83}, +{0x9D3E,0xE9FA},{0x9D3F,0xE9F9},{0x9D41,0xE9F8},{0x9D44,0xE9F5}, +{0x9D46,0xE9FB},{0x9D48,0xE9FC},{0x9D50,0xEA44},{0x9D51,0xEA43}, +{0x9D59,0xEA45},{0x9D5C,0x894C},{0x9D5D,0xEA40},{0x9D5E,0xEA41}, +{0x9D60,0x8D94},{0x9D61,0x96B7},{0x9D64,0xEA42},{0x9D6B,0xFC48}, +{0x9D6C,0x9651},{0x9D6F,0xEA4A},{0x9D70,0xFC47},{0x9D72,0xEA46}, +{0x9D7A,0xEA4B},{0x9D87,0xEA48},{0x9D89,0xEA47},{0x9D8F,0x8C7B}, +{0x9D9A,0xEA4C},{0x9DA4,0xEA4D},{0x9DA9,0xEA4E},{0x9DAB,0xEA49}, +{0x9DAF,0xE9F2},{0x9DB2,0xEA4F},{0x9DB4,0x92DF},{0x9DB8,0xEA53}, +{0x9DBA,0xEA54},{0x9DBB,0xEA52},{0x9DC1,0xEA51},{0x9DC2,0xEA57}, +{0x9DC4,0xEA50},{0x9DC6,0xEA55},{0x9DCF,0xEA56},{0x9DD3,0xEA59}, +{0x9DD9,0xEA58},{0x9DE6,0xEA5B},{0x9DED,0xEA5C},{0x9DEF,0xEA5D}, +{0x9DF2,0x9868},{0x9DF8,0xEA5A},{0x9DF9,0x91E9},{0x9DFA,0x8DEB}, +{0x9DFD,0xEA5E},{0x9E19,0xFC4A},{0x9E1A,0xEA5F},{0x9E1B,0xEA60}, +{0x9E1E,0xEA61},{0x9E75,0xEA62},{0x9E78,0x8CB2},{0x9E79,0xEA63}, +{0x9E7D,0xEA64},{0x9E7F,0x8EAD},{0x9E81,0xEA65},{0x9E88,0xEA66}, +{0x9E8B,0xEA67},{0x9E8C,0xEA68},{0x9E91,0xEA6B},{0x9E92,0xEA69}, +{0x9E93,0x985B},{0x9E95,0xEA6A},{0x9E97,0x97ED},{0x9E9D,0xEA6C}, +{0x9E9F,0x97D9},{0x9EA5,0xEA6D},{0x9EA6,0x949E},{0x9EA9,0xEA6E}, +{0x9EAA,0xEA70},{0x9EAD,0xEA71},{0x9EB8,0xEA6F},{0x9EB9,0x8D8D}, +{0x9EBA,0x96CB},{0x9EBB,0x9683},{0x9EBC,0x9BF5},{0x9EBE,0x9F80}, +{0x9EBF,0x969B},{0x9EC4,0x89A9},{0x9ECC,0xEA73},{0x9ECD,0x8B6F}, +{0x9ECE,0xEA74},{0x9ECF,0xEA75},{0x9ED0,0xEA76},{0x9ED1,0xFC4B}, +{0x9ED2,0x8D95},{0x9ED4,0xEA77},{0x9ED8,0xE0D2},{0x9ED9,0x96D9}, +{0x9EDB,0x91E1},{0x9EDC,0xEA78},{0x9EDD,0xEA7A},{0x9EDE,0xEA79}, +{0x9EE0,0xEA7B},{0x9EE5,0xEA7C},{0x9EE8,0xEA7D},{0x9EEF,0xEA7E}, +{0x9EF4,0xEA80},{0x9EF6,0xEA81},{0x9EF7,0xEA82},{0x9EF9,0xEA83}, +{0x9EFB,0xEA84},{0x9EFC,0xEA85},{0x9EFD,0xEA86},{0x9F07,0xEA87}, +{0x9F08,0xEA88},{0x9F0E,0x9343},{0x9F13,0x8CDB},{0x9F15,0xEA8A}, +{0x9F20,0x916C},{0x9F21,0xEA8B},{0x9F2C,0xEA8C},{0x9F3B,0x9540}, +{0x9F3E,0xEA8D},{0x9F4A,0xEA8E},{0x9F4B,0xE256},{0x9F4E,0xE6D8}, +{0x9F4F,0xE8EB},{0x9F52,0xEA8F},{0x9F54,0xEA90},{0x9F5F,0xEA92}, +{0x9F60,0xEA93},{0x9F61,0xEA94},{0x9F62,0x97EE},{0x9F63,0xEA91}, +{0x9F66,0xEA95},{0x9F67,0xEA96},{0x9F6A,0xEA98},{0x9F6C,0xEA97}, +{0x9F72,0xEA9A},{0x9F76,0xEA9B},{0x9F77,0xEA99},{0x9F8D,0x97B4}, +{0x9F95,0xEA9C},{0x9F9C,0xEA9D},{0x9F9D,0xE273},{0x9FA0,0xEA9E}, +{0xF929,0xFAE0},{0xF9DC,0xFBE9},{0xFA0E,0xFA90},{0xFA0F,0xFA9B}, +{0xFA10,0xFA9C},{0xFA11,0xFAB1},{0xFA12,0xFAD8},{0xFA13,0xFAE8}, +{0xFA14,0xFAEA},{0xFA15,0xFB58},{0xFA16,0xFB5E},{0xFA17,0xFB75}, +{0xFA18,0xFB7D},{0xFA19,0xFB7E},{0xFA1A,0xFB80},{0xFA1B,0xFB82}, +{0xFA1C,0xFB86},{0xFA1D,0xFB89},{0xFA1E,0xFB92},{0xFA1F,0xFB9D}, +{0xFA20,0xFB9F},{0xFA21,0xFBA0},{0xFA22,0xFBA9},{0xFA23,0xFBB1}, +{0xFA24,0xFBB3},{0xFA25,0xFBB4},{0xFA26,0xFBB7},{0xFA27,0xFBD3}, +{0xFA28,0xFBDA},{0xFA29,0xFBEA},{0xFA2A,0xFBF6},{0xFA2B,0xFBF7}, +{0xFA2C,0xFBF9},{0xFA2D,0xFC49},{0xFF01,0x8149},{0xFF02,0xFA57}, +{0xFF03,0x8194},{0xFF04,0x8190},{0xFF05,0x8193},{0xFF06,0x8195}, +{0xFF07,0xFA56},{0xFF08,0x8169},{0xFF09,0x816A},{0xFF0A,0x8196}, +{0xFF0B,0x817B},{0xFF0C,0x8143},{0xFF0D,0x817C},{0xFF0E,0x8144}, +{0xFF0F,0x815E},{0xFF10,0x824F},{0xFF11,0x8250},{0xFF12,0x8251}, +{0xFF13,0x8252},{0xFF14,0x8253},{0xFF15,0x8254},{0xFF16,0x8255}, +{0xFF17,0x8256},{0xFF18,0x8257},{0xFF19,0x8258},{0xFF1A,0x8146}, +{0xFF1B,0x8147},{0xFF1C,0x8183},{0xFF1D,0x8181},{0xFF1E,0x8184}, +{0xFF1F,0x8148},{0xFF20,0x8197},{0xFF21,0x8260},{0xFF22,0x8261}, +{0xFF23,0x8262},{0xFF24,0x8263},{0xFF25,0x8264},{0xFF26,0x8265}, +{0xFF27,0x8266},{0xFF28,0x8267},{0xFF29,0x8268},{0xFF2A,0x8269}, +{0xFF2B,0x826A},{0xFF2C,0x826B},{0xFF2D,0x826C},{0xFF2E,0x826D}, +{0xFF2F,0x826E},{0xFF30,0x826F},{0xFF31,0x8270},{0xFF32,0x8271}, +{0xFF33,0x8272},{0xFF34,0x8273},{0xFF35,0x8274},{0xFF36,0x8275}, +{0xFF37,0x8276},{0xFF38,0x8277},{0xFF39,0x8278},{0xFF3A,0x8279}, +{0xFF3B,0x816D},{0xFF3C,0x815F},{0xFF3D,0x816E},{0xFF3E,0x814F}, +{0xFF3F,0x8151},{0xFF40,0x814D},{0xFF41,0x8281},{0xFF42,0x8282}, +{0xFF43,0x8283},{0xFF44,0x8284},{0xFF45,0x8285},{0xFF46,0x8286}, +{0xFF47,0x8287},{0xFF48,0x8288},{0xFF49,0x8289},{0xFF4A,0x828A}, +{0xFF4B,0x828B},{0xFF4C,0x828C},{0xFF4D,0x828D},{0xFF4E,0x828E}, +{0xFF4F,0x828F},{0xFF50,0x8290},{0xFF51,0x8291},{0xFF52,0x8292}, +{0xFF53,0x8293},{0xFF54,0x8294},{0xFF55,0x8295},{0xFF56,0x8296}, +{0xFF57,0x8297},{0xFF58,0x8298},{0xFF59,0x8299},{0xFF5A,0x829A}, +{0xFF5B,0x816F},{0xFF5C,0x8162},{0xFF5D,0x8170},{0xFF5E,0x8160}, +{0xFFE0,0x8191},{0xFFE1,0x8192},{0xFFE2,0x81CA},{0xFFE3,0x8150}, +{0xFFE4,0xFA55},{0xFFE5,0x818F}}; + + + + +/* JIS UCODE */ +KS_CONSTANT word KS_FAR jis2ucode[NUMJISCHARS][2] = { +{0x8140,0x3000},{0x8141,0x3001},{0x8142,0x3002},{0x8143,0xFF0C}, +{0x8144,0xFF0E},{0x8145,0x30FB},{0x8146,0xFF1A},{0x8147,0xFF1B}, +{0x8148,0xFF1F},{0x8149,0xFF01},{0x814A,0x309B},{0x814B,0x309C}, +{0x814C,0x00B4},{0x814D,0xFF40},{0x814E,0x00A8},{0x814F,0xFF3E}, +{0x8150,0xFFE3},{0x8151,0xFF3F},{0x8152,0x30FD},{0x8153,0x30FE}, +{0x8154,0x309D},{0x8155,0x309E},{0x8156,0x3003},{0x8157,0x4EDD}, +{0x8158,0x3005},{0x8159,0x3006},{0x815A,0x3007},{0x815B,0x30FC}, +{0x815C,0x2015},{0x815D,0x2010},{0x815E,0xFF0F},{0x815F,0xFF3C}, +{0x8160,0xFF5E},{0x8161,0x2225},{0x8162,0xFF5C},{0x8163,0x2026}, +{0x8164,0x2025},{0x8165,0x2018},{0x8166,0x2019},{0x8167,0x201C}, +{0x8168,0x201D},{0x8169,0xFF08},{0x816A,0xFF09},{0x816B,0x3014}, +{0x816C,0x3015},{0x816D,0xFF3B},{0x816E,0xFF3D},{0x816F,0xFF5B}, +{0x8170,0xFF5D},{0x8171,0x3008},{0x8172,0x3009},{0x8173,0x300A}, +{0x8174,0x300B},{0x8175,0x300C},{0x8176,0x300D},{0x8177,0x300E}, +{0x8178,0x300F},{0x8179,0x3010},{0x817A,0x3011},{0x817B,0xFF0B}, +{0x817C,0xFF0D},{0x817D,0x00B1},{0x817E,0x00D7},{0x8180,0x00F7}, +{0x8181,0xFF1D},{0x8182,0x2260},{0x8183,0xFF1C},{0x8184,0xFF1E}, +{0x8185,0x2266},{0x8186,0x2267},{0x8187,0x221E},{0x8188,0x2234}, +{0x8189,0x2642},{0x818A,0x2640},{0x818B,0x00B0},{0x818C,0x2032}, +{0x818D,0x2033},{0x818E,0x2103},{0x818F,0xFFE5},{0x8190,0xFF04}, +{0x8191,0xFFE0},{0x8192,0xFFE1},{0x8193,0xFF05},{0x8194,0xFF03}, +{0x8195,0xFF06},{0x8196,0xFF0A},{0x8197,0xFF20},{0x8198,0x00A7}, +{0x8199,0x2606},{0x819A,0x2605},{0x819B,0x25CB},{0x819C,0x25CF}, +{0x819D,0x25CE},{0x819E,0x25C7},{0x819F,0x25C6},{0x81A0,0x25A1}, +{0x81A1,0x25A0},{0x81A2,0x25B3},{0x81A3,0x25B2},{0x81A4,0x25BD}, +{0x81A5,0x25BC},{0x81A6,0x203B},{0x81A7,0x3012},{0x81A8,0x2192}, +{0x81A9,0x2190},{0x81AA,0x2191},{0x81AB,0x2193},{0x81AC,0x3013}, +{0x81B8,0x2208},{0x81B9,0x220B},{0x81BA,0x2286},{0x81BB,0x2287}, +{0x81BC,0x2282},{0x81BD,0x2283},{0x81BE,0x222A},{0x81BF,0x2229}, +{0x81C8,0x2227},{0x81C9,0x2228},{0x81CA,0xFFE2},{0x81CB,0x21D2}, +{0x81CC,0x21D4},{0x81CD,0x2200},{0x81CE,0x2203},{0x81DA,0x2220}, +{0x81DB,0x22A5},{0x81DC,0x2312},{0x81DD,0x2202},{0x81DE,0x2207}, +{0x81DF,0x2261},{0x81E0,0x2252},{0x81E1,0x226A},{0x81E2,0x226B}, +{0x81E3,0x221A},{0x81E4,0x223D},{0x81E5,0x221D},{0x81E6,0x2235}, +{0x81E7,0x222B},{0x81E8,0x222C},{0x81F0,0x212B},{0x81F1,0x2030}, +{0x81F2,0x266F},{0x81F3,0x266D},{0x81F4,0x266A},{0x81F5,0x2020}, +{0x81F6,0x2021},{0x81F7,0x00B6},{0x81FC,0x25EF},{0x824F,0xFF10}, +{0x8250,0xFF11},{0x8251,0xFF12},{0x8252,0xFF13},{0x8253,0xFF14}, +{0x8254,0xFF15},{0x8255,0xFF16},{0x8256,0xFF17},{0x8257,0xFF18}, +{0x8258,0xFF19},{0x8260,0xFF21},{0x8261,0xFF22},{0x8262,0xFF23}, +{0x8263,0xFF24},{0x8264,0xFF25},{0x8265,0xFF26},{0x8266,0xFF27}, +{0x8267,0xFF28},{0x8268,0xFF29},{0x8269,0xFF2A},{0x826A,0xFF2B}, +{0x826B,0xFF2C},{0x826C,0xFF2D},{0x826D,0xFF2E},{0x826E,0xFF2F}, +{0x826F,0xFF30},{0x8270,0xFF31},{0x8271,0xFF32},{0x8272,0xFF33}, +{0x8273,0xFF34},{0x8274,0xFF35},{0x8275,0xFF36},{0x8276,0xFF37}, +{0x8277,0xFF38},{0x8278,0xFF39},{0x8279,0xFF3A},{0x8281,0xFF41}, +{0x8282,0xFF42},{0x8283,0xFF43},{0x8284,0xFF44},{0x8285,0xFF45}, +{0x8286,0xFF46},{0x8287,0xFF47},{0x8288,0xFF48},{0x8289,0xFF49}, +{0x828A,0xFF4A},{0x828B,0xFF4B},{0x828C,0xFF4C},{0x828D,0xFF4D}, +{0x828E,0xFF4E},{0x828F,0xFF4F},{0x8290,0xFF50},{0x8291,0xFF51}, +{0x8292,0xFF52},{0x8293,0xFF53},{0x8294,0xFF54},{0x8295,0xFF55}, +{0x8296,0xFF56},{0x8297,0xFF57},{0x8298,0xFF58},{0x8299,0xFF59}, +{0x829A,0xFF5A},{0x829F,0x3041},{0x82A0,0x3042},{0x82A1,0x3043}, +{0x82A2,0x3044},{0x82A3,0x3045},{0x82A4,0x3046},{0x82A5,0x3047}, +{0x82A6,0x3048},{0x82A7,0x3049},{0x82A8,0x304A},{0x82A9,0x304B}, +{0x82AA,0x304C},{0x82AB,0x304D},{0x82AC,0x304E},{0x82AD,0x304F}, +{0x82AE,0x3050},{0x82AF,0x3051},{0x82B0,0x3052},{0x82B1,0x3053}, +{0x82B2,0x3054},{0x82B3,0x3055},{0x82B4,0x3056},{0x82B5,0x3057}, +{0x82B6,0x3058},{0x82B7,0x3059},{0x82B8,0x305A},{0x82B9,0x305B}, +{0x82BA,0x305C},{0x82BB,0x305D},{0x82BC,0x305E},{0x82BD,0x305F}, +{0x82BE,0x3060},{0x82BF,0x3061},{0x82C0,0x3062},{0x82C1,0x3063}, +{0x82C2,0x3064},{0x82C3,0x3065},{0x82C4,0x3066},{0x82C5,0x3067}, +{0x82C6,0x3068},{0x82C7,0x3069},{0x82C8,0x306A},{0x82C9,0x306B}, +{0x82CA,0x306C},{0x82CB,0x306D},{0x82CC,0x306E},{0x82CD,0x306F}, +{0x82CE,0x3070},{0x82CF,0x3071},{0x82D0,0x3072},{0x82D1,0x3073}, +{0x82D2,0x3074},{0x82D3,0x3075},{0x82D4,0x3076},{0x82D5,0x3077}, +{0x82D6,0x3078},{0x82D7,0x3079},{0x82D8,0x307A},{0x82D9,0x307B}, +{0x82DA,0x307C},{0x82DB,0x307D},{0x82DC,0x307E},{0x82DD,0x307F}, +{0x82DE,0x3080},{0x82DF,0x3081},{0x82E0,0x3082},{0x82E1,0x3083}, +{0x82E2,0x3084},{0x82E3,0x3085},{0x82E4,0x3086},{0x82E5,0x3087}, +{0x82E6,0x3088},{0x82E7,0x3089},{0x82E8,0x308A},{0x82E9,0x308B}, +{0x82EA,0x308C},{0x82EB,0x308D},{0x82EC,0x308E},{0x82ED,0x308F}, +{0x82EE,0x3090},{0x82EF,0x3091},{0x82F0,0x3092},{0x82F1,0x3093}, +{0x8340,0x30A1},{0x8341,0x30A2},{0x8342,0x30A3},{0x8343,0x30A4}, +{0x8344,0x30A5},{0x8345,0x30A6},{0x8346,0x30A7},{0x8347,0x30A8}, +{0x8348,0x30A9},{0x8349,0x30AA},{0x834A,0x30AB},{0x834B,0x30AC}, +{0x834C,0x30AD},{0x834D,0x30AE},{0x834E,0x30AF},{0x834F,0x30B0}, +{0x8350,0x30B1},{0x8351,0x30B2},{0x8352,0x30B3},{0x8353,0x30B4}, +{0x8354,0x30B5},{0x8355,0x30B6},{0x8356,0x30B7},{0x8357,0x30B8}, +{0x8358,0x30B9},{0x8359,0x30BA},{0x835A,0x30BB},{0x835B,0x30BC}, +{0x835C,0x30BD},{0x835D,0x30BE},{0x835E,0x30BF},{0x835F,0x30C0}, +{0x8360,0x30C1},{0x8361,0x30C2},{0x8362,0x30C3},{0x8363,0x30C4}, +{0x8364,0x30C5},{0x8365,0x30C6},{0x8366,0x30C7},{0x8367,0x30C8}, +{0x8368,0x30C9},{0x8369,0x30CA},{0x836A,0x30CB},{0x836B,0x30CC}, +{0x836C,0x30CD},{0x836D,0x30CE},{0x836E,0x30CF},{0x836F,0x30D0}, +{0x8370,0x30D1},{0x8371,0x30D2},{0x8372,0x30D3},{0x8373,0x30D4}, +{0x8374,0x30D5},{0x8375,0x30D6},{0x8376,0x30D7},{0x8377,0x30D8}, +{0x8378,0x30D9},{0x8379,0x30DA},{0x837A,0x30DB},{0x837B,0x30DC}, +{0x837C,0x30DD},{0x837D,0x30DE},{0x837E,0x30DF},{0x8380,0x30E0}, +{0x8381,0x30E1},{0x8382,0x30E2},{0x8383,0x30E3},{0x8384,0x30E4}, +{0x8385,0x30E5},{0x8386,0x30E6},{0x8387,0x30E7},{0x8388,0x30E8}, +{0x8389,0x30E9},{0x838A,0x30EA},{0x838B,0x30EB},{0x838C,0x30EC}, +{0x838D,0x30ED},{0x838E,0x30EE},{0x838F,0x30EF},{0x8390,0x30F0}, +{0x8391,0x30F1},{0x8392,0x30F2},{0x8393,0x30F3},{0x8394,0x30F4}, +{0x8395,0x30F5},{0x8396,0x30F6},{0x839F,0x0391},{0x83A0,0x0392}, +{0x83A1,0x0393},{0x83A2,0x0394},{0x83A3,0x0395},{0x83A4,0x0396}, +{0x83A5,0x0397},{0x83A6,0x0398},{0x83A7,0x0399},{0x83A8,0x039A}, +{0x83A9,0x039B},{0x83AA,0x039C},{0x83AB,0x039D},{0x83AC,0x039E}, +{0x83AD,0x039F},{0x83AE,0x03A0},{0x83AF,0x03A1},{0x83B0,0x03A3}, +{0x83B1,0x03A4},{0x83B2,0x03A5},{0x83B3,0x03A6},{0x83B4,0x03A7}, +{0x83B5,0x03A8},{0x83B6,0x03A9},{0x83BF,0x03B1},{0x83C0,0x03B2}, +{0x83C1,0x03B3},{0x83C2,0x03B4},{0x83C3,0x03B5},{0x83C4,0x03B6}, +{0x83C5,0x03B7},{0x83C6,0x03B8},{0x83C7,0x03B9},{0x83C8,0x03BA}, +{0x83C9,0x03BB},{0x83CA,0x03BC},{0x83CB,0x03BD},{0x83CC,0x03BE}, +{0x83CD,0x03BF},{0x83CE,0x03C0},{0x83CF,0x03C1},{0x83D0,0x03C3}, +{0x83D1,0x03C4},{0x83D2,0x03C5},{0x83D3,0x03C6},{0x83D4,0x03C7}, +{0x83D5,0x03C8},{0x83D6,0x03C9},{0x8440,0x0410},{0x8441,0x0411}, +{0x8442,0x0412},{0x8443,0x0413},{0x8444,0x0414},{0x8445,0x0415}, +{0x8446,0x0401},{0x8447,0x0416},{0x8448,0x0417},{0x8449,0x0418}, +{0x844A,0x0419},{0x844B,0x041A},{0x844C,0x041B},{0x844D,0x041C}, +{0x844E,0x041D},{0x844F,0x041E},{0x8450,0x041F},{0x8451,0x0420}, +{0x8452,0x0421},{0x8453,0x0422},{0x8454,0x0423},{0x8455,0x0424}, +{0x8456,0x0425},{0x8457,0x0426},{0x8458,0x0427},{0x8459,0x0428}, +{0x845A,0x0429},{0x845B,0x042A},{0x845C,0x042B},{0x845D,0x042C}, +{0x845E,0x042D},{0x845F,0x042E},{0x8460,0x042F},{0x8470,0x0430}, +{0x8471,0x0431},{0x8472,0x0432},{0x8473,0x0433},{0x8474,0x0434}, +{0x8475,0x0435},{0x8476,0x0451},{0x8477,0x0436},{0x8478,0x0437}, +{0x8479,0x0438},{0x847A,0x0439},{0x847B,0x043A},{0x847C,0x043B}, +{0x847D,0x043C},{0x847E,0x043D},{0x8480,0x043E},{0x8481,0x043F}, +{0x8482,0x0440},{0x8483,0x0441},{0x8484,0x0442},{0x8485,0x0443}, +{0x8486,0x0444},{0x8487,0x0445},{0x8488,0x0446},{0x8489,0x0447}, +{0x848A,0x0448},{0x848B,0x0449},{0x848C,0x044A},{0x848D,0x044B}, +{0x848E,0x044C},{0x848F,0x044D},{0x8490,0x044E},{0x8491,0x044F}, +{0x849F,0x2500},{0x84A0,0x2502},{0x84A1,0x250C},{0x84A2,0x2510}, +{0x84A3,0x2518},{0x84A4,0x2514},{0x84A5,0x251C},{0x84A6,0x252C}, +{0x84A7,0x2524},{0x84A8,0x2534},{0x84A9,0x253C},{0x84AA,0x2501}, +{0x84AB,0x2503},{0x84AC,0x250F},{0x84AD,0x2513},{0x84AE,0x251B}, +{0x84AF,0x2517},{0x84B0,0x2523},{0x84B1,0x2533},{0x84B2,0x252B}, +{0x84B3,0x253B},{0x84B4,0x254B},{0x84B5,0x2520},{0x84B6,0x252F}, +{0x84B7,0x2528},{0x84B8,0x2537},{0x84B9,0x253F},{0x84BA,0x251D}, +{0x84BB,0x2530},{0x84BC,0x2525},{0x84BD,0x2538},{0x84BE,0x2542}, +{0x8740,0x2460},{0x8741,0x2461},{0x8742,0x2462},{0x8743,0x2463}, +{0x8744,0x2464},{0x8745,0x2465},{0x8746,0x2466},{0x8747,0x2467}, +{0x8748,0x2468},{0x8749,0x2469},{0x874A,0x246A},{0x874B,0x246B}, +{0x874C,0x246C},{0x874D,0x246D},{0x874E,0x246E},{0x874F,0x246F}, +{0x8750,0x2470},{0x8751,0x2471},{0x8752,0x2472},{0x8753,0x2473}, +{0x8754,0x2160},{0x8755,0x2161},{0x8756,0x2162},{0x8757,0x2163}, +{0x8758,0x2164},{0x8759,0x2165},{0x875A,0x2166},{0x875B,0x2167}, +{0x875C,0x2168},{0x875D,0x2169},{0x875F,0x3349},{0x8760,0x3314}, +{0x8761,0x3322},{0x8762,0x334D},{0x8763,0x3318},{0x8764,0x3327}, +{0x8765,0x3303},{0x8766,0x3336},{0x8767,0x3351},{0x8768,0x3357}, +{0x8769,0x330D},{0x876A,0x3326},{0x876B,0x3323},{0x876C,0x332B}, +{0x876D,0x334A},{0x876E,0x333B},{0x876F,0x339C},{0x8770,0x339D}, +{0x8771,0x339E},{0x8772,0x338E},{0x8773,0x338F},{0x8774,0x33C4}, +{0x8775,0x33A1},{0x877E,0x337B},{0x8780,0x301D},{0x8781,0x301F}, +{0x8782,0x2116},{0x8783,0x33CD},{0x8784,0x2121},{0x8785,0x32A4}, +{0x8786,0x32A5},{0x8787,0x32A6},{0x8788,0x32A7},{0x8789,0x32A8}, +{0x878A,0x3231},{0x878B,0x3232},{0x878C,0x3239},{0x878D,0x337E}, +{0x878E,0x337D},{0x878F,0x337C},{0x8790,0x2252},{0x8791,0x2261}, +{0x8792,0x222B},{0x8793,0x222E},{0x8794,0x2211},{0x8795,0x221A}, +{0x8796,0x22A5},{0x8797,0x2220},{0x8798,0x221F},{0x8799,0x22BF}, +{0x879A,0x2235},{0x879B,0x2229},{0x879C,0x222A},{0x889F,0x4E9C}, +{0x88A0,0x5516},{0x88A1,0x5A03},{0x88A2,0x963F},{0x88A3,0x54C0}, +{0x88A4,0x611B},{0x88A5,0x6328},{0x88A6,0x59F6},{0x88A7,0x9022}, +{0x88A8,0x8475},{0x88A9,0x831C},{0x88AA,0x7A50},{0x88AB,0x60AA}, +{0x88AC,0x63E1},{0x88AD,0x6E25},{0x88AE,0x65ED},{0x88AF,0x8466}, +{0x88B0,0x82A6},{0x88B1,0x9BF5},{0x88B2,0x6893},{0x88B3,0x5727}, +{0x88B4,0x65A1},{0x88B5,0x6271},{0x88B6,0x5B9B},{0x88B7,0x59D0}, +{0x88B8,0x867B},{0x88B9,0x98F4},{0x88BA,0x7D62},{0x88BB,0x7DBE}, +{0x88BC,0x9B8E},{0x88BD,0x6216},{0x88BE,0x7C9F},{0x88BF,0x88B7}, +{0x88C0,0x5B89},{0x88C1,0x5EB5},{0x88C2,0x6309},{0x88C3,0x6697}, +{0x88C4,0x6848},{0x88C5,0x95C7},{0x88C6,0x978D},{0x88C7,0x674F}, +{0x88C8,0x4EE5},{0x88C9,0x4F0A},{0x88CA,0x4F4D},{0x88CB,0x4F9D}, +{0x88CC,0x5049},{0x88CD,0x56F2},{0x88CE,0x5937},{0x88CF,0x59D4}, +{0x88D0,0x5A01},{0x88D1,0x5C09},{0x88D2,0x60DF},{0x88D3,0x610F}, +{0x88D4,0x6170},{0x88D5,0x6613},{0x88D6,0x6905},{0x88D7,0x70BA}, +{0x88D8,0x754F},{0x88D9,0x7570},{0x88DA,0x79FB},{0x88DB,0x7DAD}, +{0x88DC,0x7DEF},{0x88DD,0x80C3},{0x88DE,0x840E},{0x88DF,0x8863}, +{0x88E0,0x8B02},{0x88E1,0x9055},{0x88E2,0x907A},{0x88E3,0x533B}, +{0x88E4,0x4E95},{0x88E5,0x4EA5},{0x88E6,0x57DF},{0x88E7,0x80B2}, +{0x88E8,0x90C1},{0x88E9,0x78EF},{0x88EA,0x4E00},{0x88EB,0x58F1}, +{0x88EC,0x6EA2},{0x88ED,0x9038},{0x88EE,0x7A32},{0x88EF,0x8328}, +{0x88F0,0x828B},{0x88F1,0x9C2F},{0x88F2,0x5141},{0x88F3,0x5370}, +{0x88F4,0x54BD},{0x88F5,0x54E1},{0x88F6,0x56E0},{0x88F7,0x59FB}, +{0x88F8,0x5F15},{0x88F9,0x98F2},{0x88FA,0x6DEB},{0x88FB,0x80E4}, +{0x88FC,0x852D},{0x8940,0x9662},{0x8941,0x9670},{0x8942,0x96A0}, +{0x8943,0x97FB},{0x8944,0x540B},{0x8945,0x53F3},{0x8946,0x5B87}, +{0x8947,0x70CF},{0x8948,0x7FBD},{0x8949,0x8FC2},{0x894A,0x96E8}, +{0x894B,0x536F},{0x894C,0x9D5C},{0x894D,0x7ABA},{0x894E,0x4E11}, +{0x894F,0x7893},{0x8950,0x81FC},{0x8951,0x6E26},{0x8952,0x5618}, +{0x8953,0x5504},{0x8954,0x6B1D},{0x8955,0x851A},{0x8956,0x9C3B}, +{0x8957,0x59E5},{0x8958,0x53A9},{0x8959,0x6D66},{0x895A,0x74DC}, +{0x895B,0x958F},{0x895C,0x5642},{0x895D,0x4E91},{0x895E,0x904B}, +{0x895F,0x96F2},{0x8960,0x834F},{0x8961,0x990C},{0x8962,0x53E1}, +{0x8963,0x55B6},{0x8964,0x5B30},{0x8965,0x5F71},{0x8966,0x6620}, +{0x8967,0x66F3},{0x8968,0x6804},{0x8969,0x6C38},{0x896A,0x6CF3}, +{0x896B,0x6D29},{0x896C,0x745B},{0x896D,0x76C8},{0x896E,0x7A4E}, +{0x896F,0x9834},{0x8970,0x82F1},{0x8971,0x885B},{0x8972,0x8A60}, +{0x8973,0x92ED},{0x8974,0x6DB2},{0x8975,0x75AB},{0x8976,0x76CA}, +{0x8977,0x99C5},{0x8978,0x60A6},{0x8979,0x8B01},{0x897A,0x8D8A}, +{0x897B,0x95B2},{0x897C,0x698E},{0x897D,0x53AD},{0x897E,0x5186}, +{0x8980,0x5712},{0x8981,0x5830},{0x8982,0x5944},{0x8983,0x5BB4}, +{0x8984,0x5EF6},{0x8985,0x6028},{0x8986,0x63A9},{0x8987,0x63F4}, +{0x8988,0x6CBF},{0x8989,0x6F14},{0x898A,0x708E},{0x898B,0x7114}, +{0x898C,0x7159},{0x898D,0x71D5},{0x898E,0x733F},{0x898F,0x7E01}, +{0x8990,0x8276},{0x8991,0x82D1},{0x8992,0x8597},{0x8993,0x9060}, +{0x8994,0x925B},{0x8995,0x9D1B},{0x8996,0x5869},{0x8997,0x65BC}, +{0x8998,0x6C5A},{0x8999,0x7525},{0x899A,0x51F9},{0x899B,0x592E}, +{0x899C,0x5965},{0x899D,0x5F80},{0x899E,0x5FDC},{0x899F,0x62BC}, +{0x89A0,0x65FA},{0x89A1,0x6A2A},{0x89A2,0x6B27},{0x89A3,0x6BB4}, +{0x89A4,0x738B},{0x89A5,0x7FC1},{0x89A6,0x8956},{0x89A7,0x9D2C}, +{0x89A8,0x9D0E},{0x89A9,0x9EC4},{0x89AA,0x5CA1},{0x89AB,0x6C96}, +{0x89AC,0x837B},{0x89AD,0x5104},{0x89AE,0x5C4B},{0x89AF,0x61B6}, +{0x89B0,0x81C6},{0x89B1,0x6876},{0x89B2,0x7261},{0x89B3,0x4E59}, +{0x89B4,0x4FFA},{0x89B5,0x5378},{0x89B6,0x6069},{0x89B7,0x6E29}, +{0x89B8,0x7A4F},{0x89B9,0x97F3},{0x89BA,0x4E0B},{0x89BB,0x5316}, +{0x89BC,0x4EEE},{0x89BD,0x4F55},{0x89BE,0x4F3D},{0x89BF,0x4FA1}, +{0x89C0,0x4F73},{0x89C1,0x52A0},{0x89C2,0x53EF},{0x89C3,0x5609}, +{0x89C4,0x590F},{0x89C5,0x5AC1},{0x89C6,0x5BB6},{0x89C7,0x5BE1}, +{0x89C8,0x79D1},{0x89C9,0x6687},{0x89CA,0x679C},{0x89CB,0x67B6}, +{0x89CC,0x6B4C},{0x89CD,0x6CB3},{0x89CE,0x706B},{0x89CF,0x73C2}, +{0x89D0,0x798D},{0x89D1,0x79BE},{0x89D2,0x7A3C},{0x89D3,0x7B87}, +{0x89D4,0x82B1},{0x89D5,0x82DB},{0x89D6,0x8304},{0x89D7,0x8377}, +{0x89D8,0x83EF},{0x89D9,0x83D3},{0x89DA,0x8766},{0x89DB,0x8AB2}, +{0x89DC,0x5629},{0x89DD,0x8CA8},{0x89DE,0x8FE6},{0x89DF,0x904E}, +{0x89E0,0x971E},{0x89E1,0x868A},{0x89E2,0x4FC4},{0x89E3,0x5CE8}, +{0x89E4,0x6211},{0x89E5,0x7259},{0x89E6,0x753B},{0x89E7,0x81E5}, +{0x89E8,0x82BD},{0x89E9,0x86FE},{0x89EA,0x8CC0},{0x89EB,0x96C5}, +{0x89EC,0x9913},{0x89ED,0x99D5},{0x89EE,0x4ECB},{0x89EF,0x4F1A}, +{0x89F0,0x89E3},{0x89F1,0x56DE},{0x89F2,0x584A},{0x89F3,0x58CA}, +{0x89F4,0x5EFB},{0x89F5,0x5FEB},{0x89F6,0x602A},{0x89F7,0x6094}, +{0x89F8,0x6062},{0x89F9,0x61D0},{0x89FA,0x6212},{0x89FB,0x62D0}, +{0x89FC,0x6539},{0x8A40,0x9B41},{0x8A41,0x6666},{0x8A42,0x68B0}, +{0x8A43,0x6D77},{0x8A44,0x7070},{0x8A45,0x754C},{0x8A46,0x7686}, +{0x8A47,0x7D75},{0x8A48,0x82A5},{0x8A49,0x87F9},{0x8A4A,0x958B}, +{0x8A4B,0x968E},{0x8A4C,0x8C9D},{0x8A4D,0x51F1},{0x8A4E,0x52BE}, +{0x8A4F,0x5916},{0x8A50,0x54B3},{0x8A51,0x5BB3},{0x8A52,0x5D16}, +{0x8A53,0x6168},{0x8A54,0x6982},{0x8A55,0x6DAF},{0x8A56,0x788D}, +{0x8A57,0x84CB},{0x8A58,0x8857},{0x8A59,0x8A72},{0x8A5A,0x93A7}, +{0x8A5B,0x9AB8},{0x8A5C,0x6D6C},{0x8A5D,0x99A8},{0x8A5E,0x86D9}, +{0x8A5F,0x57A3},{0x8A60,0x67FF},{0x8A61,0x86CE},{0x8A62,0x920E}, +{0x8A63,0x5283},{0x8A64,0x5687},{0x8A65,0x5404},{0x8A66,0x5ED3}, +{0x8A67,0x62E1},{0x8A68,0x64B9},{0x8A69,0x683C},{0x8A6A,0x6838}, +{0x8A6B,0x6BBB},{0x8A6C,0x7372},{0x8A6D,0x78BA},{0x8A6E,0x7A6B}, +{0x8A6F,0x899A},{0x8A70,0x89D2},{0x8A71,0x8D6B},{0x8A72,0x8F03}, +{0x8A73,0x90ED},{0x8A74,0x95A3},{0x8A75,0x9694},{0x8A76,0x9769}, +{0x8A77,0x5B66},{0x8A78,0x5CB3},{0x8A79,0x697D},{0x8A7A,0x984D}, +{0x8A7B,0x984E},{0x8A7C,0x639B},{0x8A7D,0x7B20},{0x8A7E,0x6A2B}, +{0x8A80,0x6A7F},{0x8A81,0x68B6},{0x8A82,0x9C0D},{0x8A83,0x6F5F}, +{0x8A84,0x5272},{0x8A85,0x559D},{0x8A86,0x6070},{0x8A87,0x62EC}, +{0x8A88,0x6D3B},{0x8A89,0x6E07},{0x8A8A,0x6ED1},{0x8A8B,0x845B}, +{0x8A8C,0x8910},{0x8A8D,0x8F44},{0x8A8E,0x4E14},{0x8A8F,0x9C39}, +{0x8A90,0x53F6},{0x8A91,0x691B},{0x8A92,0x6A3A},{0x8A93,0x9784}, +{0x8A94,0x682A},{0x8A95,0x515C},{0x8A96,0x7AC3},{0x8A97,0x84B2}, +{0x8A98,0x91DC},{0x8A99,0x938C},{0x8A9A,0x565B},{0x8A9B,0x9D28}, +{0x8A9C,0x6822},{0x8A9D,0x8305},{0x8A9E,0x8431},{0x8A9F,0x7CA5}, +{0x8AA0,0x5208},{0x8AA1,0x82C5},{0x8AA2,0x74E6},{0x8AA3,0x4E7E}, +{0x8AA4,0x4F83},{0x8AA5,0x51A0},{0x8AA6,0x5BD2},{0x8AA7,0x520A}, +{0x8AA8,0x52D8},{0x8AA9,0x52E7},{0x8AAA,0x5DFB},{0x8AAB,0x559A}, +{0x8AAC,0x582A},{0x8AAD,0x59E6},{0x8AAE,0x5B8C},{0x8AAF,0x5B98}, +{0x8AB0,0x5BDB},{0x8AB1,0x5E72},{0x8AB2,0x5E79},{0x8AB3,0x60A3}, +{0x8AB4,0x611F},{0x8AB5,0x6163},{0x8AB6,0x61BE},{0x8AB7,0x63DB}, +{0x8AB8,0x6562},{0x8AB9,0x67D1},{0x8ABA,0x6853},{0x8ABB,0x68FA}, +{0x8ABC,0x6B3E},{0x8ABD,0x6B53},{0x8ABE,0x6C57},{0x8ABF,0x6F22}, +{0x8AC0,0x6F97},{0x8AC1,0x6F45},{0x8AC2,0x74B0},{0x8AC3,0x7518}, +{0x8AC4,0x76E3},{0x8AC5,0x770B},{0x8AC6,0x7AFF},{0x8AC7,0x7BA1}, +{0x8AC8,0x7C21},{0x8AC9,0x7DE9},{0x8ACA,0x7F36},{0x8ACB,0x7FF0}, +{0x8ACC,0x809D},{0x8ACD,0x8266},{0x8ACE,0x839E},{0x8ACF,0x89B3}, +{0x8AD0,0x8ACC},{0x8AD1,0x8CAB},{0x8AD2,0x9084},{0x8AD3,0x9451}, +{0x8AD4,0x9593},{0x8AD5,0x9591},{0x8AD6,0x95A2},{0x8AD7,0x9665}, +{0x8AD8,0x97D3},{0x8AD9,0x9928},{0x8ADA,0x8218},{0x8ADB,0x4E38}, +{0x8ADC,0x542B},{0x8ADD,0x5CB8},{0x8ADE,0x5DCC},{0x8ADF,0x73A9}, +{0x8AE0,0x764C},{0x8AE1,0x773C},{0x8AE2,0x5CA9},{0x8AE3,0x7FEB}, +{0x8AE4,0x8D0B},{0x8AE5,0x96C1},{0x8AE6,0x9811},{0x8AE7,0x9854}, +{0x8AE8,0x9858},{0x8AE9,0x4F01},{0x8AEA,0x4F0E},{0x8AEB,0x5371}, +{0x8AEC,0x559C},{0x8AED,0x5668},{0x8AEE,0x57FA},{0x8AEF,0x5947}, +{0x8AF0,0x5B09},{0x8AF1,0x5BC4},{0x8AF2,0x5C90},{0x8AF3,0x5E0C}, +{0x8AF4,0x5E7E},{0x8AF5,0x5FCC},{0x8AF6,0x63EE},{0x8AF7,0x673A}, +{0x8AF8,0x65D7},{0x8AF9,0x65E2},{0x8AFA,0x671F},{0x8AFB,0x68CB}, +{0x8AFC,0x68C4},{0x8B40,0x6A5F},{0x8B41,0x5E30},{0x8B42,0x6BC5}, +{0x8B43,0x6C17},{0x8B44,0x6C7D},{0x8B45,0x757F},{0x8B46,0x7948}, +{0x8B47,0x5B63},{0x8B48,0x7A00},{0x8B49,0x7D00},{0x8B4A,0x5FBD}, +{0x8B4B,0x898F},{0x8B4C,0x8A18},{0x8B4D,0x8CB4},{0x8B4E,0x8D77}, +{0x8B4F,0x8ECC},{0x8B50,0x8F1D},{0x8B51,0x98E2},{0x8B52,0x9A0E}, +{0x8B53,0x9B3C},{0x8B54,0x4E80},{0x8B55,0x507D},{0x8B56,0x5100}, +{0x8B57,0x5993},{0x8B58,0x5B9C},{0x8B59,0x622F},{0x8B5A,0x6280}, +{0x8B5B,0x64EC},{0x8B5C,0x6B3A},{0x8B5D,0x72A0},{0x8B5E,0x7591}, +{0x8B5F,0x7947},{0x8B60,0x7FA9},{0x8B61,0x87FB},{0x8B62,0x8ABC}, +{0x8B63,0x8B70},{0x8B64,0x63AC},{0x8B65,0x83CA},{0x8B66,0x97A0}, +{0x8B67,0x5409},{0x8B68,0x5403},{0x8B69,0x55AB},{0x8B6A,0x6854}, +{0x8B6B,0x6A58},{0x8B6C,0x8A70},{0x8B6D,0x7827},{0x8B6E,0x6775}, +{0x8B6F,0x9ECD},{0x8B70,0x5374},{0x8B71,0x5BA2},{0x8B72,0x811A}, +{0x8B73,0x8650},{0x8B74,0x9006},{0x8B75,0x4E18},{0x8B76,0x4E45}, +{0x8B77,0x4EC7},{0x8B78,0x4F11},{0x8B79,0x53CA},{0x8B7A,0x5438}, +{0x8B7B,0x5BAE},{0x8B7C,0x5F13},{0x8B7D,0x6025},{0x8B7E,0x6551}, +{0x8B80,0x673D},{0x8B81,0x6C42},{0x8B82,0x6C72},{0x8B83,0x6CE3}, +{0x8B84,0x7078},{0x8B85,0x7403},{0x8B86,0x7A76},{0x8B87,0x7AAE}, +{0x8B88,0x7B08},{0x8B89,0x7D1A},{0x8B8A,0x7CFE},{0x8B8B,0x7D66}, +{0x8B8C,0x65E7},{0x8B8D,0x725B},{0x8B8E,0x53BB},{0x8B8F,0x5C45}, +{0x8B90,0x5DE8},{0x8B91,0x62D2},{0x8B92,0x62E0},{0x8B93,0x6319}, +{0x8B94,0x6E20},{0x8B95,0x865A},{0x8B96,0x8A31},{0x8B97,0x8DDD}, +{0x8B98,0x92F8},{0x8B99,0x6F01},{0x8B9A,0x79A6},{0x8B9B,0x9B5A}, +{0x8B9C,0x4EA8},{0x8B9D,0x4EAB},{0x8B9E,0x4EAC},{0x8B9F,0x4F9B}, +{0x8BA0,0x4FA0},{0x8BA1,0x50D1},{0x8BA2,0x5147},{0x8BA3,0x7AF6}, +{0x8BA4,0x5171},{0x8BA5,0x51F6},{0x8BA6,0x5354},{0x8BA7,0x5321}, +{0x8BA8,0x537F},{0x8BA9,0x53EB},{0x8BAA,0x55AC},{0x8BAB,0x5883}, +{0x8BAC,0x5CE1},{0x8BAD,0x5F37},{0x8BAE,0x5F4A},{0x8BAF,0x602F}, +{0x8BB0,0x6050},{0x8BB1,0x606D},{0x8BB2,0x631F},{0x8BB3,0x6559}, +{0x8BB4,0x6A4B},{0x8BB5,0x6CC1},{0x8BB6,0x72C2},{0x8BB7,0x72ED}, +{0x8BB8,0x77EF},{0x8BB9,0x80F8},{0x8BBA,0x8105},{0x8BBB,0x8208}, +{0x8BBC,0x854E},{0x8BBD,0x90F7},{0x8BBE,0x93E1},{0x8BBF,0x97FF}, +{0x8BC0,0x9957},{0x8BC1,0x9A5A},{0x8BC2,0x4EF0},{0x8BC3,0x51DD}, +{0x8BC4,0x5C2D},{0x8BC5,0x6681},{0x8BC6,0x696D},{0x8BC7,0x5C40}, +{0x8BC8,0x66F2},{0x8BC9,0x6975},{0x8BCA,0x7389},{0x8BCB,0x6850}, +{0x8BCC,0x7C81},{0x8BCD,0x50C5},{0x8BCE,0x52E4},{0x8BCF,0x5747}, +{0x8BD0,0x5DFE},{0x8BD1,0x9326},{0x8BD2,0x65A4},{0x8BD3,0x6B23}, +{0x8BD4,0x6B3D},{0x8BD5,0x7434},{0x8BD6,0x7981},{0x8BD7,0x79BD}, +{0x8BD8,0x7B4B},{0x8BD9,0x7DCA},{0x8BDA,0x82B9},{0x8BDB,0x83CC}, +{0x8BDC,0x887F},{0x8BDD,0x895F},{0x8BDE,0x8B39},{0x8BDF,0x8FD1}, +{0x8BE0,0x91D1},{0x8BE1,0x541F},{0x8BE2,0x9280},{0x8BE3,0x4E5D}, +{0x8BE4,0x5036},{0x8BE5,0x53E5},{0x8BE6,0x533A},{0x8BE7,0x72D7}, +{0x8BE8,0x7396},{0x8BE9,0x77E9},{0x8BEA,0x82E6},{0x8BEB,0x8EAF}, +{0x8BEC,0x99C6},{0x8BED,0x99C8},{0x8BEE,0x99D2},{0x8BEF,0x5177}, +{0x8BF0,0x611A},{0x8BF1,0x865E},{0x8BF2,0x55B0},{0x8BF3,0x7A7A}, +{0x8BF4,0x5076},{0x8BF5,0x5BD3},{0x8BF6,0x9047},{0x8BF7,0x9685}, +{0x8BF8,0x4E32},{0x8BF9,0x6ADB},{0x8BFA,0x91E7},{0x8BFB,0x5C51}, +{0x8BFC,0x5C48},{0x8C40,0x6398},{0x8C41,0x7A9F},{0x8C42,0x6C93}, +{0x8C43,0x9774},{0x8C44,0x8F61},{0x8C45,0x7AAA},{0x8C46,0x718A}, +{0x8C47,0x9688},{0x8C48,0x7C82},{0x8C49,0x6817},{0x8C4A,0x7E70}, +{0x8C4B,0x6851},{0x8C4C,0x936C},{0x8C4D,0x52F2},{0x8C4E,0x541B}, +{0x8C4F,0x85AB},{0x8C50,0x8A13},{0x8C51,0x7FA4},{0x8C52,0x8ECD}, +{0x8C53,0x90E1},{0x8C54,0x5366},{0x8C55,0x8888},{0x8C56,0x7941}, +{0x8C57,0x4FC2},{0x8C58,0x50BE},{0x8C59,0x5211},{0x8C5A,0x5144}, +{0x8C5B,0x5553},{0x8C5C,0x572D},{0x8C5D,0x73EA},{0x8C5E,0x578B}, +{0x8C5F,0x5951},{0x8C60,0x5F62},{0x8C61,0x5F84},{0x8C62,0x6075}, +{0x8C63,0x6176},{0x8C64,0x6167},{0x8C65,0x61A9},{0x8C66,0x63B2}, +{0x8C67,0x643A},{0x8C68,0x656C},{0x8C69,0x666F},{0x8C6A,0x6842}, +{0x8C6B,0x6E13},{0x8C6C,0x7566},{0x8C6D,0x7A3D},{0x8C6E,0x7CFB}, +{0x8C6F,0x7D4C},{0x8C70,0x7D99},{0x8C71,0x7E4B},{0x8C72,0x7F6B}, +{0x8C73,0x830E},{0x8C74,0x834A},{0x8C75,0x86CD},{0x8C76,0x8A08}, +{0x8C77,0x8A63},{0x8C78,0x8B66},{0x8C79,0x8EFD},{0x8C7A,0x981A}, +{0x8C7B,0x9D8F},{0x8C7C,0x82B8},{0x8C7D,0x8FCE},{0x8C7E,0x9BE8}, +{0x8C80,0x5287},{0x8C81,0x621F},{0x8C82,0x6483},{0x8C83,0x6FC0}, +{0x8C84,0x9699},{0x8C85,0x6841},{0x8C86,0x5091},{0x8C87,0x6B20}, +{0x8C88,0x6C7A},{0x8C89,0x6F54},{0x8C8A,0x7A74},{0x8C8B,0x7D50}, +{0x8C8C,0x8840},{0x8C8D,0x8A23},{0x8C8E,0x6708},{0x8C8F,0x4EF6}, +{0x8C90,0x5039},{0x8C91,0x5026},{0x8C92,0x5065},{0x8C93,0x517C}, +{0x8C94,0x5238},{0x8C95,0x5263},{0x8C96,0x55A7},{0x8C97,0x570F}, +{0x8C98,0x5805},{0x8C99,0x5ACC},{0x8C9A,0x5EFA},{0x8C9B,0x61B2}, +{0x8C9C,0x61F8},{0x8C9D,0x62F3},{0x8C9E,0x6372},{0x8C9F,0x691C}, +{0x8CA0,0x6A29},{0x8CA1,0x727D},{0x8CA2,0x72AC},{0x8CA3,0x732E}, +{0x8CA4,0x7814},{0x8CA5,0x786F},{0x8CA6,0x7D79},{0x8CA7,0x770C}, +{0x8CA8,0x80A9},{0x8CA9,0x898B},{0x8CAA,0x8B19},{0x8CAB,0x8CE2}, +{0x8CAC,0x8ED2},{0x8CAD,0x9063},{0x8CAE,0x9375},{0x8CAF,0x967A}, +{0x8CB0,0x9855},{0x8CB1,0x9A13},{0x8CB2,0x9E78},{0x8CB3,0x5143}, +{0x8CB4,0x539F},{0x8CB5,0x53B3},{0x8CB6,0x5E7B},{0x8CB7,0x5F26}, +{0x8CB8,0x6E1B},{0x8CB9,0x6E90},{0x8CBA,0x7384},{0x8CBB,0x73FE}, +{0x8CBC,0x7D43},{0x8CBD,0x8237},{0x8CBE,0x8A00},{0x8CBF,0x8AFA}, +{0x8CC0,0x9650},{0x8CC1,0x4E4E},{0x8CC2,0x500B},{0x8CC3,0x53E4}, +{0x8CC4,0x547C},{0x8CC5,0x56FA},{0x8CC6,0x59D1},{0x8CC7,0x5B64}, +{0x8CC8,0x5DF1},{0x8CC9,0x5EAB},{0x8CCA,0x5F27},{0x8CCB,0x6238}, +{0x8CCC,0x6545},{0x8CCD,0x67AF},{0x8CCE,0x6E56},{0x8CCF,0x72D0}, +{0x8CD0,0x7CCA},{0x8CD1,0x88B4},{0x8CD2,0x80A1},{0x8CD3,0x80E1}, +{0x8CD4,0x83F0},{0x8CD5,0x864E},{0x8CD6,0x8A87},{0x8CD7,0x8DE8}, +{0x8CD8,0x9237},{0x8CD9,0x96C7},{0x8CDA,0x9867},{0x8CDB,0x9F13}, +{0x8CDC,0x4E94},{0x8CDD,0x4E92},{0x8CDE,0x4F0D},{0x8CDF,0x5348}, +{0x8CE0,0x5449},{0x8CE1,0x543E},{0x8CE2,0x5A2F},{0x8CE3,0x5F8C}, +{0x8CE4,0x5FA1},{0x8CE5,0x609F},{0x8CE6,0x68A7},{0x8CE7,0x6A8E}, +{0x8CE8,0x745A},{0x8CE9,0x7881},{0x8CEA,0x8A9E},{0x8CEB,0x8AA4}, +{0x8CEC,0x8B77},{0x8CED,0x9190},{0x8CEE,0x4E5E},{0x8CEF,0x9BC9}, +{0x8CF0,0x4EA4},{0x8CF1,0x4F7C},{0x8CF2,0x4FAF},{0x8CF3,0x5019}, +{0x8CF4,0x5016},{0x8CF5,0x5149},{0x8CF6,0x516C},{0x8CF7,0x529F}, +{0x8CF8,0x52B9},{0x8CF9,0x52FE},{0x8CFA,0x539A},{0x8CFB,0x53E3}, +{0x8CFC,0x5411},{0x8D40,0x540E},{0x8D41,0x5589},{0x8D42,0x5751}, +{0x8D43,0x57A2},{0x8D44,0x597D},{0x8D45,0x5B54},{0x8D46,0x5B5D}, +{0x8D47,0x5B8F},{0x8D48,0x5DE5},{0x8D49,0x5DE7},{0x8D4A,0x5DF7}, +{0x8D4B,0x5E78},{0x8D4C,0x5E83},{0x8D4D,0x5E9A},{0x8D4E,0x5EB7}, +{0x8D4F,0x5F18},{0x8D50,0x6052},{0x8D51,0x614C},{0x8D52,0x6297}, +{0x8D53,0x62D8},{0x8D54,0x63A7},{0x8D55,0x653B},{0x8D56,0x6602}, +{0x8D57,0x6643},{0x8D58,0x66F4},{0x8D59,0x676D},{0x8D5A,0x6821}, +{0x8D5B,0x6897},{0x8D5C,0x69CB},{0x8D5D,0x6C5F},{0x8D5E,0x6D2A}, +{0x8D5F,0x6D69},{0x8D60,0x6E2F},{0x8D61,0x6E9D},{0x8D62,0x7532}, +{0x8D63,0x7687},{0x8D64,0x786C},{0x8D65,0x7A3F},{0x8D66,0x7CE0}, +{0x8D67,0x7D05},{0x8D68,0x7D18},{0x8D69,0x7D5E},{0x8D6A,0x7DB1}, +{0x8D6B,0x8015},{0x8D6C,0x8003},{0x8D6D,0x80AF},{0x8D6E,0x80B1}, +{0x8D6F,0x8154},{0x8D70,0x818F},{0x8D71,0x822A},{0x8D72,0x8352}, +{0x8D73,0x884C},{0x8D74,0x8861},{0x8D75,0x8B1B},{0x8D76,0x8CA2}, +{0x8D77,0x8CFC},{0x8D78,0x90CA},{0x8D79,0x9175},{0x8D7A,0x9271}, +{0x8D7B,0x783F},{0x8D7C,0x92FC},{0x8D7D,0x95A4},{0x8D7E,0x964D}, +{0x8D80,0x9805},{0x8D81,0x9999},{0x8D82,0x9AD8},{0x8D83,0x9D3B}, +{0x8D84,0x525B},{0x8D85,0x52AB},{0x8D86,0x53F7},{0x8D87,0x5408}, +{0x8D88,0x58D5},{0x8D89,0x62F7},{0x8D8A,0x6FE0},{0x8D8B,0x8C6A}, +{0x8D8C,0x8F5F},{0x8D8D,0x9EB9},{0x8D8E,0x514B},{0x8D8F,0x523B}, +{0x8D90,0x544A},{0x8D91,0x56FD},{0x8D92,0x7A40},{0x8D93,0x9177}, +{0x8D94,0x9D60},{0x8D95,0x9ED2},{0x8D96,0x7344},{0x8D97,0x6F09}, +{0x8D98,0x8170},{0x8D99,0x7511},{0x8D9A,0x5FFD},{0x8D9B,0x60DA}, +{0x8D9C,0x9AA8},{0x8D9D,0x72DB},{0x8D9E,0x8FBC},{0x8D9F,0x6B64}, +{0x8DA0,0x9803},{0x8DA1,0x4ECA},{0x8DA2,0x56F0},{0x8DA3,0x5764}, +{0x8DA4,0x58BE},{0x8DA5,0x5A5A},{0x8DA6,0x6068},{0x8DA7,0x61C7}, +{0x8DA8,0x660F},{0x8DA9,0x6606},{0x8DAA,0x6839},{0x8DAB,0x68B1}, +{0x8DAC,0x6DF7},{0x8DAD,0x75D5},{0x8DAE,0x7D3A},{0x8DAF,0x826E}, +{0x8DB0,0x9B42},{0x8DB1,0x4E9B},{0x8DB2,0x4F50},{0x8DB3,0x53C9}, +{0x8DB4,0x5506},{0x8DB5,0x5D6F},{0x8DB6,0x5DE6},{0x8DB7,0x5DEE}, +{0x8DB8,0x67FB},{0x8DB9,0x6C99},{0x8DBA,0x7473},{0x8DBB,0x7802}, +{0x8DBC,0x8A50},{0x8DBD,0x9396},{0x8DBE,0x88DF},{0x8DBF,0x5750}, +{0x8DC0,0x5EA7},{0x8DC1,0x632B},{0x8DC2,0x50B5},{0x8DC3,0x50AC}, +{0x8DC4,0x518D},{0x8DC5,0x6700},{0x8DC6,0x54C9},{0x8DC7,0x585E}, +{0x8DC8,0x59BB},{0x8DC9,0x5BB0},{0x8DCA,0x5F69},{0x8DCB,0x624D}, +{0x8DCC,0x63A1},{0x8DCD,0x683D},{0x8DCE,0x6B73},{0x8DCF,0x6E08}, +{0x8DD0,0x707D},{0x8DD1,0x91C7},{0x8DD2,0x7280},{0x8DD3,0x7815}, +{0x8DD4,0x7826},{0x8DD5,0x796D},{0x8DD6,0x658E},{0x8DD7,0x7D30}, +{0x8DD8,0x83DC},{0x8DD9,0x88C1},{0x8DDA,0x8F09},{0x8DDB,0x969B}, +{0x8DDC,0x5264},{0x8DDD,0x5728},{0x8DDE,0x6750},{0x8DDF,0x7F6A}, +{0x8DE0,0x8CA1},{0x8DE1,0x51B4},{0x8DE2,0x5742},{0x8DE3,0x962A}, +{0x8DE4,0x583A},{0x8DE5,0x698A},{0x8DE6,0x80B4},{0x8DE7,0x54B2}, +{0x8DE8,0x5D0E},{0x8DE9,0x57FC},{0x8DEA,0x7895},{0x8DEB,0x9DFA}, +{0x8DEC,0x4F5C},{0x8DED,0x524A},{0x8DEE,0x548B},{0x8DEF,0x643E}, +{0x8DF0,0x6628},{0x8DF1,0x6714},{0x8DF2,0x67F5},{0x8DF3,0x7A84}, +{0x8DF4,0x7B56},{0x8DF5,0x7D22},{0x8DF6,0x932F},{0x8DF7,0x685C}, +{0x8DF8,0x9BAD},{0x8DF9,0x7B39},{0x8DFA,0x5319},{0x8DFB,0x518A}, +{0x8DFC,0x5237},{0x8E40,0x5BDF},{0x8E41,0x62F6},{0x8E42,0x64AE}, +{0x8E43,0x64E6},{0x8E44,0x672D},{0x8E45,0x6BBA},{0x8E46,0x85A9}, +{0x8E47,0x96D1},{0x8E48,0x7690},{0x8E49,0x9BD6},{0x8E4A,0x634C}, +{0x8E4B,0x9306},{0x8E4C,0x9BAB},{0x8E4D,0x76BF},{0x8E4E,0x6652}, +{0x8E4F,0x4E09},{0x8E50,0x5098},{0x8E51,0x53C2},{0x8E52,0x5C71}, +{0x8E53,0x60E8},{0x8E54,0x6492},{0x8E55,0x6563},{0x8E56,0x685F}, +{0x8E57,0x71E6},{0x8E58,0x73CA},{0x8E59,0x7523},{0x8E5A,0x7B97}, +{0x8E5B,0x7E82},{0x8E5C,0x8695},{0x8E5D,0x8B83},{0x8E5E,0x8CDB}, +{0x8E5F,0x9178},{0x8E60,0x9910},{0x8E61,0x65AC},{0x8E62,0x66AB}, +{0x8E63,0x6B8B},{0x8E64,0x4ED5},{0x8E65,0x4ED4},{0x8E66,0x4F3A}, +{0x8E67,0x4F7F},{0x8E68,0x523A},{0x8E69,0x53F8},{0x8E6A,0x53F2}, +{0x8E6B,0x55E3},{0x8E6C,0x56DB},{0x8E6D,0x58EB},{0x8E6E,0x59CB}, +{0x8E6F,0x59C9},{0x8E70,0x59FF},{0x8E71,0x5B50},{0x8E72,0x5C4D}, +{0x8E73,0x5E02},{0x8E74,0x5E2B},{0x8E75,0x5FD7},{0x8E76,0x601D}, +{0x8E77,0x6307},{0x8E78,0x652F},{0x8E79,0x5B5C},{0x8E7A,0x65AF}, +{0x8E7B,0x65BD},{0x8E7C,0x65E8},{0x8E7D,0x679D},{0x8E7E,0x6B62}, +{0x8E80,0x6B7B},{0x8E81,0x6C0F},{0x8E82,0x7345},{0x8E83,0x7949}, +{0x8E84,0x79C1},{0x8E85,0x7CF8},{0x8E86,0x7D19},{0x8E87,0x7D2B}, +{0x8E88,0x80A2},{0x8E89,0x8102},{0x8E8A,0x81F3},{0x8E8B,0x8996}, +{0x8E8C,0x8A5E},{0x8E8D,0x8A69},{0x8E8E,0x8A66},{0x8E8F,0x8A8C}, +{0x8E90,0x8AEE},{0x8E91,0x8CC7},{0x8E92,0x8CDC},{0x8E93,0x96CC}, +{0x8E94,0x98FC},{0x8E95,0x6B6F},{0x8E96,0x4E8B},{0x8E97,0x4F3C}, +{0x8E98,0x4F8D},{0x8E99,0x5150},{0x8E9A,0x5B57},{0x8E9B,0x5BFA}, +{0x8E9C,0x6148},{0x8E9D,0x6301},{0x8E9E,0x6642},{0x8E9F,0x6B21}, +{0x8EA0,0x6ECB},{0x8EA1,0x6CBB},{0x8EA2,0x723E},{0x8EA3,0x74BD}, +{0x8EA4,0x75D4},{0x8EA5,0x78C1},{0x8EA6,0x793A},{0x8EA7,0x800C}, +{0x8EA8,0x8033},{0x8EA9,0x81EA},{0x8EAA,0x8494},{0x8EAB,0x8F9E}, +{0x8EAC,0x6C50},{0x8EAD,0x9E7F},{0x8EAE,0x5F0F},{0x8EAF,0x8B58}, +{0x8EB0,0x9D2B},{0x8EB1,0x7AFA},{0x8EB2,0x8EF8},{0x8EB3,0x5B8D}, +{0x8EB4,0x96EB},{0x8EB5,0x4E03},{0x8EB6,0x53F1},{0x8EB7,0x57F7}, +{0x8EB8,0x5931},{0x8EB9,0x5AC9},{0x8EBA,0x5BA4},{0x8EBB,0x6089}, +{0x8EBC,0x6E7F},{0x8EBD,0x6F06},{0x8EBE,0x75BE},{0x8EBF,0x8CEA}, +{0x8EC0,0x5B9F},{0x8EC1,0x8500},{0x8EC2,0x7BE0},{0x8EC3,0x5072}, +{0x8EC4,0x67F4},{0x8EC5,0x829D},{0x8EC6,0x5C61},{0x8EC7,0x854A}, +{0x8EC8,0x7E1E},{0x8EC9,0x820E},{0x8ECA,0x5199},{0x8ECB,0x5C04}, +{0x8ECC,0x6368},{0x8ECD,0x8D66},{0x8ECE,0x659C},{0x8ECF,0x716E}, +{0x8ED0,0x793E},{0x8ED1,0x7D17},{0x8ED2,0x8005},{0x8ED3,0x8B1D}, +{0x8ED4,0x8ECA},{0x8ED5,0x906E},{0x8ED6,0x86C7},{0x8ED7,0x90AA}, +{0x8ED8,0x501F},{0x8ED9,0x52FA},{0x8EDA,0x5C3A},{0x8EDB,0x6753}, +{0x8EDC,0x707C},{0x8EDD,0x7235},{0x8EDE,0x914C},{0x8EDF,0x91C8}, +{0x8EE0,0x932B},{0x8EE1,0x82E5},{0x8EE2,0x5BC2},{0x8EE3,0x5F31}, +{0x8EE4,0x60F9},{0x8EE5,0x4E3B},{0x8EE6,0x53D6},{0x8EE7,0x5B88}, +{0x8EE8,0x624B},{0x8EE9,0x6731},{0x8EEA,0x6B8A},{0x8EEB,0x72E9}, +{0x8EEC,0x73E0},{0x8EED,0x7A2E},{0x8EEE,0x816B},{0x8EEF,0x8DA3}, +{0x8EF0,0x9152},{0x8EF1,0x9996},{0x8EF2,0x5112},{0x8EF3,0x53D7}, +{0x8EF4,0x546A},{0x8EF5,0x5BFF},{0x8EF6,0x6388},{0x8EF7,0x6A39}, +{0x8EF8,0x7DAC},{0x8EF9,0x9700},{0x8EFA,0x56DA},{0x8EFB,0x53CE}, +{0x8EFC,0x5468},{0x8F40,0x5B97},{0x8F41,0x5C31},{0x8F42,0x5DDE}, +{0x8F43,0x4FEE},{0x8F44,0x6101},{0x8F45,0x62FE},{0x8F46,0x6D32}, +{0x8F47,0x79C0},{0x8F48,0x79CB},{0x8F49,0x7D42},{0x8F4A,0x7E4D}, +{0x8F4B,0x7FD2},{0x8F4C,0x81ED},{0x8F4D,0x821F},{0x8F4E,0x8490}, +{0x8F4F,0x8846},{0x8F50,0x8972},{0x8F51,0x8B90},{0x8F52,0x8E74}, +{0x8F53,0x8F2F},{0x8F54,0x9031},{0x8F55,0x914B},{0x8F56,0x916C}, +{0x8F57,0x96C6},{0x8F58,0x919C},{0x8F59,0x4EC0},{0x8F5A,0x4F4F}, +{0x8F5B,0x5145},{0x8F5C,0x5341},{0x8F5D,0x5F93},{0x8F5E,0x620E}, +{0x8F5F,0x67D4},{0x8F60,0x6C41},{0x8F61,0x6E0B},{0x8F62,0x7363}, +{0x8F63,0x7E26},{0x8F64,0x91CD},{0x8F65,0x9283},{0x8F66,0x53D4}, +{0x8F67,0x5919},{0x8F68,0x5BBF},{0x8F69,0x6DD1},{0x8F6A,0x795D}, +{0x8F6B,0x7E2E},{0x8F6C,0x7C9B},{0x8F6D,0x587E},{0x8F6E,0x719F}, +{0x8F6F,0x51FA},{0x8F70,0x8853},{0x8F71,0x8FF0},{0x8F72,0x4FCA}, +{0x8F73,0x5CFB},{0x8F74,0x6625},{0x8F75,0x77AC},{0x8F76,0x7AE3}, +{0x8F77,0x821C},{0x8F78,0x99FF},{0x8F79,0x51C6},{0x8F7A,0x5FAA}, +{0x8F7B,0x65EC},{0x8F7C,0x696F},{0x8F7D,0x6B89},{0x8F7E,0x6DF3}, +{0x8F80,0x6E96},{0x8F81,0x6F64},{0x8F82,0x76FE},{0x8F83,0x7D14}, +{0x8F84,0x5DE1},{0x8F85,0x9075},{0x8F86,0x9187},{0x8F87,0x9806}, +{0x8F88,0x51E6},{0x8F89,0x521D},{0x8F8A,0x6240},{0x8F8B,0x6691}, +{0x8F8C,0x66D9},{0x8F8D,0x6E1A},{0x8F8E,0x5EB6},{0x8F8F,0x7DD2}, +{0x8F90,0x7F72},{0x8F91,0x66F8},{0x8F92,0x85AF},{0x8F93,0x85F7}, +{0x8F94,0x8AF8},{0x8F95,0x52A9},{0x8F96,0x53D9},{0x8F97,0x5973}, +{0x8F98,0x5E8F},{0x8F99,0x5F90},{0x8F9A,0x6055},{0x8F9B,0x92E4}, +{0x8F9C,0x9664},{0x8F9D,0x50B7},{0x8F9E,0x511F},{0x8F9F,0x52DD}, +{0x8FA0,0x5320},{0x8FA1,0x5347},{0x8FA2,0x53EC},{0x8FA3,0x54E8}, +{0x8FA4,0x5546},{0x8FA5,0x5531},{0x8FA6,0x5617},{0x8FA7,0x5968}, +{0x8FA8,0x59BE},{0x8FA9,0x5A3C},{0x8FAA,0x5BB5},{0x8FAB,0x5C06}, +{0x8FAC,0x5C0F},{0x8FAD,0x5C11},{0x8FAE,0x5C1A},{0x8FAF,0x5E84}, +{0x8FB0,0x5E8A},{0x8FB1,0x5EE0},{0x8FB2,0x5F70},{0x8FB3,0x627F}, +{0x8FB4,0x6284},{0x8FB5,0x62DB},{0x8FB6,0x638C},{0x8FB7,0x6377}, +{0x8FB8,0x6607},{0x8FB9,0x660C},{0x8FBA,0x662D},{0x8FBB,0x6676}, +{0x8FBC,0x677E},{0x8FBD,0x68A2},{0x8FBE,0x6A1F},{0x8FBF,0x6A35}, +{0x8FC0,0x6CBC},{0x8FC1,0x6D88},{0x8FC2,0x6E09},{0x8FC3,0x6E58}, +{0x8FC4,0x713C},{0x8FC5,0x7126},{0x8FC6,0x7167},{0x8FC7,0x75C7}, +{0x8FC8,0x7701},{0x8FC9,0x785D},{0x8FCA,0x7901},{0x8FCB,0x7965}, +{0x8FCC,0x79F0},{0x8FCD,0x7AE0},{0x8FCE,0x7B11},{0x8FCF,0x7CA7}, +{0x8FD0,0x7D39},{0x8FD1,0x8096},{0x8FD2,0x83D6},{0x8FD3,0x848B}, +{0x8FD4,0x8549},{0x8FD5,0x885D},{0x8FD6,0x88F3},{0x8FD7,0x8A1F}, +{0x8FD8,0x8A3C},{0x8FD9,0x8A54},{0x8FDA,0x8A73},{0x8FDB,0x8C61}, +{0x8FDC,0x8CDE},{0x8FDD,0x91A4},{0x8FDE,0x9266},{0x8FDF,0x937E}, +{0x8FE0,0x9418},{0x8FE1,0x969C},{0x8FE2,0x9798},{0x8FE3,0x4E0A}, +{0x8FE4,0x4E08},{0x8FE5,0x4E1E},{0x8FE6,0x4E57},{0x8FE7,0x5197}, +{0x8FE8,0x5270},{0x8FE9,0x57CE},{0x8FEA,0x5834},{0x8FEB,0x58CC}, +{0x8FEC,0x5B22},{0x8FED,0x5E38},{0x8FEE,0x60C5},{0x8FEF,0x64FE}, +{0x8FF0,0x6761},{0x8FF1,0x6756},{0x8FF2,0x6D44},{0x8FF3,0x72B6}, +{0x8FF4,0x7573},{0x8FF5,0x7A63},{0x8FF6,0x84B8},{0x8FF7,0x8B72}, +{0x8FF8,0x91B8},{0x8FF9,0x9320},{0x8FFA,0x5631},{0x8FFB,0x57F4}, +{0x8FFC,0x98FE},{0x9040,0x62ED},{0x9041,0x690D},{0x9042,0x6B96}, +{0x9043,0x71ED},{0x9044,0x7E54},{0x9045,0x8077},{0x9046,0x8272}, +{0x9047,0x89E6},{0x9048,0x98DF},{0x9049,0x8755},{0x904A,0x8FB1}, +{0x904B,0x5C3B},{0x904C,0x4F38},{0x904D,0x4FE1},{0x904E,0x4FB5}, +{0x904F,0x5507},{0x9050,0x5A20},{0x9051,0x5BDD},{0x9052,0x5BE9}, +{0x9053,0x5FC3},{0x9054,0x614E},{0x9055,0x632F},{0x9056,0x65B0}, +{0x9057,0x664B},{0x9058,0x68EE},{0x9059,0x699B},{0x905A,0x6D78}, +{0x905B,0x6DF1},{0x905C,0x7533},{0x905D,0x75B9},{0x905E,0x771F}, +{0x905F,0x795E},{0x9060,0x79E6},{0x9061,0x7D33},{0x9062,0x81E3}, +{0x9063,0x82AF},{0x9064,0x85AA},{0x9065,0x89AA},{0x9066,0x8A3A}, +{0x9067,0x8EAB},{0x9068,0x8F9B},{0x9069,0x9032},{0x906A,0x91DD}, +{0x906B,0x9707},{0x906C,0x4EBA},{0x906D,0x4EC1},{0x906E,0x5203}, +{0x906F,0x5875},{0x9070,0x58EC},{0x9071,0x5C0B},{0x9072,0x751A}, +{0x9073,0x5C3D},{0x9074,0x814E},{0x9075,0x8A0A},{0x9076,0x8FC5}, +{0x9077,0x9663},{0x9078,0x976D},{0x9079,0x7B25},{0x907A,0x8ACF}, +{0x907B,0x9808},{0x907C,0x9162},{0x907D,0x56F3},{0x907E,0x53A8}, +{0x9080,0x9017},{0x9081,0x5439},{0x9082,0x5782},{0x9083,0x5E25}, +{0x9084,0x63A8},{0x9085,0x6C34},{0x9086,0x708A},{0x9087,0x7761}, +{0x9088,0x7C8B},{0x9089,0x7FE0},{0x908A,0x8870},{0x908B,0x9042}, +{0x908C,0x9154},{0x908D,0x9310},{0x908E,0x9318},{0x908F,0x968F}, +{0x9090,0x745E},{0x9091,0x9AC4},{0x9092,0x5D07},{0x9093,0x5D69}, +{0x9094,0x6570},{0x9095,0x67A2},{0x9096,0x8DA8},{0x9097,0x96DB}, +{0x9098,0x636E},{0x9099,0x6749},{0x909A,0x6919},{0x909B,0x83C5}, +{0x909C,0x9817},{0x909D,0x96C0},{0x909E,0x88FE},{0x909F,0x6F84}, +{0x90A0,0x647A},{0x90A1,0x5BF8},{0x90A2,0x4E16},{0x90A3,0x702C}, +{0x90A4,0x755D},{0x90A5,0x662F},{0x90A6,0x51C4},{0x90A7,0x5236}, +{0x90A8,0x52E2},{0x90A9,0x59D3},{0x90AA,0x5F81},{0x90AB,0x6027}, +{0x90AC,0x6210},{0x90AD,0x653F},{0x90AE,0x6574},{0x90AF,0x661F}, +{0x90B0,0x6674},{0x90B1,0x68F2},{0x90B2,0x6816},{0x90B3,0x6B63}, +{0x90B4,0x6E05},{0x90B5,0x7272},{0x90B6,0x751F},{0x90B7,0x76DB}, +{0x90B8,0x7CBE},{0x90B9,0x8056},{0x90BA,0x58F0},{0x90BB,0x88FD}, +{0x90BC,0x897F},{0x90BD,0x8AA0},{0x90BE,0x8A93},{0x90BF,0x8ACB}, +{0x90C0,0x901D},{0x90C1,0x9192},{0x90C2,0x9752},{0x90C3,0x9759}, +{0x90C4,0x6589},{0x90C5,0x7A0E},{0x90C6,0x8106},{0x90C7,0x96BB}, +{0x90C8,0x5E2D},{0x90C9,0x60DC},{0x90CA,0x621A},{0x90CB,0x65A5}, +{0x90CC,0x6614},{0x90CD,0x6790},{0x90CE,0x77F3},{0x90CF,0x7A4D}, +{0x90D0,0x7C4D},{0x90D1,0x7E3E},{0x90D2,0x810A},{0x90D3,0x8CAC}, +{0x90D4,0x8D64},{0x90D5,0x8DE1},{0x90D6,0x8E5F},{0x90D7,0x78A9}, +{0x90D8,0x5207},{0x90D9,0x62D9},{0x90DA,0x63A5},{0x90DB,0x6442}, +{0x90DC,0x6298},{0x90DD,0x8A2D},{0x90DE,0x7A83},{0x90DF,0x7BC0}, +{0x90E0,0x8AAC},{0x90E1,0x96EA},{0x90E2,0x7D76},{0x90E3,0x820C}, +{0x90E4,0x8749},{0x90E5,0x4ED9},{0x90E6,0x5148},{0x90E7,0x5343}, +{0x90E8,0x5360},{0x90E9,0x5BA3},{0x90EA,0x5C02},{0x90EB,0x5C16}, +{0x90EC,0x5DDD},{0x90ED,0x6226},{0x90EE,0x6247},{0x90EF,0x64B0}, +{0x90F0,0x6813},{0x90F1,0x6834},{0x90F2,0x6CC9},{0x90F3,0x6D45}, +{0x90F4,0x6D17},{0x90F5,0x67D3},{0x90F6,0x6F5C},{0x90F7,0x714E}, +{0x90F8,0x717D},{0x90F9,0x65CB},{0x90FA,0x7A7F},{0x90FB,0x7BAD}, +{0x90FC,0x7DDA},{0x9140,0x7E4A},{0x9141,0x7FA8},{0x9142,0x817A}, +{0x9143,0x821B},{0x9144,0x8239},{0x9145,0x85A6},{0x9146,0x8A6E}, +{0x9147,0x8CCE},{0x9148,0x8DF5},{0x9149,0x9078},{0x914A,0x9077}, +{0x914B,0x92AD},{0x914C,0x9291},{0x914D,0x9583},{0x914E,0x9BAE}, +{0x914F,0x524D},{0x9150,0x5584},{0x9151,0x6F38},{0x9152,0x7136}, +{0x9153,0x5168},{0x9154,0x7985},{0x9155,0x7E55},{0x9156,0x81B3}, +{0x9157,0x7CCE},{0x9158,0x564C},{0x9159,0x5851},{0x915A,0x5CA8}, +{0x915B,0x63AA},{0x915C,0x66FE},{0x915D,0x66FD},{0x915E,0x695A}, +{0x915F,0x72D9},{0x9160,0x758F},{0x9161,0x758E},{0x9162,0x790E}, +{0x9163,0x7956},{0x9164,0x79DF},{0x9165,0x7C97},{0x9166,0x7D20}, +{0x9167,0x7D44},{0x9168,0x8607},{0x9169,0x8A34},{0x916A,0x963B}, +{0x916B,0x9061},{0x916C,0x9F20},{0x916D,0x50E7},{0x916E,0x5275}, +{0x916F,0x53CC},{0x9170,0x53E2},{0x9171,0x5009},{0x9172,0x55AA}, +{0x9173,0x58EE},{0x9174,0x594F},{0x9175,0x723D},{0x9176,0x5B8B}, +{0x9177,0x5C64},{0x9178,0x531D},{0x9179,0x60E3},{0x917A,0x60F3}, +{0x917B,0x635C},{0x917C,0x6383},{0x917D,0x633F},{0x917E,0x63BB}, +{0x9180,0x64CD},{0x9181,0x65E9},{0x9182,0x66F9},{0x9183,0x5DE3}, +{0x9184,0x69CD},{0x9185,0x69FD},{0x9186,0x6F15},{0x9187,0x71E5}, +{0x9188,0x4E89},{0x9189,0x75E9},{0x918A,0x76F8},{0x918B,0x7A93}, +{0x918C,0x7CDF},{0x918D,0x7DCF},{0x918E,0x7D9C},{0x918F,0x8061}, +{0x9190,0x8349},{0x9191,0x8358},{0x9192,0x846C},{0x9193,0x84BC}, +{0x9194,0x85FB},{0x9195,0x88C5},{0x9196,0x8D70},{0x9197,0x9001}, +{0x9198,0x906D},{0x9199,0x9397},{0x919A,0x971C},{0x919B,0x9A12}, +{0x919C,0x50CF},{0x919D,0x5897},{0x919E,0x618E},{0x919F,0x81D3}, +{0x91A0,0x8535},{0x91A1,0x8D08},{0x91A2,0x9020},{0x91A3,0x4FC3}, +{0x91A4,0x5074},{0x91A5,0x5247},{0x91A6,0x5373},{0x91A7,0x606F}, +{0x91A8,0x6349},{0x91A9,0x675F},{0x91AA,0x6E2C},{0x91AB,0x8DB3}, +{0x91AC,0x901F},{0x91AD,0x4FD7},{0x91AE,0x5C5E},{0x91AF,0x8CCA}, +{0x91B0,0x65CF},{0x91B1,0x7D9A},{0x91B2,0x5352},{0x91B3,0x8896}, +{0x91B4,0x5176},{0x91B5,0x63C3},{0x91B6,0x5B58},{0x91B7,0x5B6B}, +{0x91B8,0x5C0A},{0x91B9,0x640D},{0x91BA,0x6751},{0x91BB,0x905C}, +{0x91BC,0x4ED6},{0x91BD,0x591A},{0x91BE,0x592A},{0x91BF,0x6C70}, +{0x91C0,0x8A51},{0x91C1,0x553E},{0x91C2,0x5815},{0x91C3,0x59A5}, +{0x91C4,0x60F0},{0x91C5,0x6253},{0x91C6,0x67C1},{0x91C7,0x8235}, +{0x91C8,0x6955},{0x91C9,0x9640},{0x91CA,0x99C4},{0x91CB,0x9A28}, +{0x91CC,0x4F53},{0x91CD,0x5806},{0x91CE,0x5BFE},{0x91CF,0x8010}, +{0x91D0,0x5CB1},{0x91D1,0x5E2F},{0x91D2,0x5F85},{0x91D3,0x6020}, +{0x91D4,0x614B},{0x91D5,0x6234},{0x91D6,0x66FF},{0x91D7,0x6CF0}, +{0x91D8,0x6EDE},{0x91D9,0x80CE},{0x91DA,0x817F},{0x91DB,0x82D4}, +{0x91DC,0x888B},{0x91DD,0x8CB8},{0x91DE,0x9000},{0x91DF,0x902E}, +{0x91E0,0x968A},{0x91E1,0x9EDB},{0x91E2,0x9BDB},{0x91E3,0x4EE3}, +{0x91E4,0x53F0},{0x91E5,0x5927},{0x91E6,0x7B2C},{0x91E7,0x918D}, +{0x91E8,0x984C},{0x91E9,0x9DF9},{0x91EA,0x6EDD},{0x91EB,0x7027}, +{0x91EC,0x5353},{0x91ED,0x5544},{0x91EE,0x5B85},{0x91EF,0x6258}, +{0x91F0,0x629E},{0x91F1,0x62D3},{0x91F2,0x6CA2},{0x91F3,0x6FEF}, +{0x91F4,0x7422},{0x91F5,0x8A17},{0x91F6,0x9438},{0x91F7,0x6FC1}, +{0x91F8,0x8AFE},{0x91F9,0x8338},{0x91FA,0x51E7},{0x91FB,0x86F8}, +{0x91FC,0x53EA},{0x9240,0x53E9},{0x9241,0x4F46},{0x9242,0x9054}, +{0x9243,0x8FB0},{0x9244,0x596A},{0x9245,0x8131},{0x9246,0x5DFD}, +{0x9247,0x7AEA},{0x9248,0x8FBF},{0x9249,0x68DA},{0x924A,0x8C37}, +{0x924B,0x72F8},{0x924C,0x9C48},{0x924D,0x6A3D},{0x924E,0x8AB0}, +{0x924F,0x4E39},{0x9250,0x5358},{0x9251,0x5606},{0x9252,0x5766}, +{0x9253,0x62C5},{0x9254,0x63A2},{0x9255,0x65E6},{0x9256,0x6B4E}, +{0x9257,0x6DE1},{0x9258,0x6E5B},{0x9259,0x70AD},{0x925A,0x77ED}, +{0x925B,0x7AEF},{0x925C,0x7BAA},{0x925D,0x7DBB},{0x925E,0x803D}, +{0x925F,0x80C6},{0x9260,0x86CB},{0x9261,0x8A95},{0x9262,0x935B}, +{0x9263,0x56E3},{0x9264,0x58C7},{0x9265,0x5F3E},{0x9266,0x65AD}, +{0x9267,0x6696},{0x9268,0x6A80},{0x9269,0x6BB5},{0x926A,0x7537}, +{0x926B,0x8AC7},{0x926C,0x5024},{0x926D,0x77E5},{0x926E,0x5730}, +{0x926F,0x5F1B},{0x9270,0x6065},{0x9271,0x667A},{0x9272,0x6C60}, +{0x9273,0x75F4},{0x9274,0x7A1A},{0x9275,0x7F6E},{0x9276,0x81F4}, +{0x9277,0x8718},{0x9278,0x9045},{0x9279,0x99B3},{0x927A,0x7BC9}, +{0x927B,0x755C},{0x927C,0x7AF9},{0x927D,0x7B51},{0x927E,0x84C4}, +{0x9280,0x9010},{0x9281,0x79E9},{0x9282,0x7A92},{0x9283,0x8336}, +{0x9284,0x5AE1},{0x9285,0x7740},{0x9286,0x4E2D},{0x9287,0x4EF2}, +{0x9288,0x5B99},{0x9289,0x5FE0},{0x928A,0x62BD},{0x928B,0x663C}, +{0x928C,0x67F1},{0x928D,0x6CE8},{0x928E,0x866B},{0x928F,0x8877}, +{0x9290,0x8A3B},{0x9291,0x914E},{0x9292,0x92F3},{0x9293,0x99D0}, +{0x9294,0x6A17},{0x9295,0x7026},{0x9296,0x732A},{0x9297,0x82E7}, +{0x9298,0x8457},{0x9299,0x8CAF},{0x929A,0x4E01},{0x929B,0x5146}, +{0x929C,0x51CB},{0x929D,0x558B},{0x929E,0x5BF5},{0x929F,0x5E16}, +{0x92A0,0x5E33},{0x92A1,0x5E81},{0x92A2,0x5F14},{0x92A3,0x5F35}, +{0x92A4,0x5F6B},{0x92A5,0x5FB4},{0x92A6,0x61F2},{0x92A7,0x6311}, +{0x92A8,0x66A2},{0x92A9,0x671D},{0x92AA,0x6F6E},{0x92AB,0x7252}, +{0x92AC,0x753A},{0x92AD,0x773A},{0x92AE,0x8074},{0x92AF,0x8139}, +{0x92B0,0x8178},{0x92B1,0x8776},{0x92B2,0x8ABF},{0x92B3,0x8ADC}, +{0x92B4,0x8D85},{0x92B5,0x8DF3},{0x92B6,0x929A},{0x92B7,0x9577}, +{0x92B8,0x9802},{0x92B9,0x9CE5},{0x92BA,0x52C5},{0x92BB,0x6357}, +{0x92BC,0x76F4},{0x92BD,0x6715},{0x92BE,0x6C88},{0x92BF,0x73CD}, +{0x92C0,0x8CC3},{0x92C1,0x93AE},{0x92C2,0x9673},{0x92C3,0x6D25}, +{0x92C4,0x589C},{0x92C5,0x690E},{0x92C6,0x69CC},{0x92C7,0x8FFD}, +{0x92C8,0x939A},{0x92C9,0x75DB},{0x92CA,0x901A},{0x92CB,0x585A}, +{0x92CC,0x6802},{0x92CD,0x63B4},{0x92CE,0x69FB},{0x92CF,0x4F43}, +{0x92D0,0x6F2C},{0x92D1,0x67D8},{0x92D2,0x8FBB},{0x92D3,0x8526}, +{0x92D4,0x7DB4},{0x92D5,0x9354},{0x92D6,0x693F},{0x92D7,0x6F70}, +{0x92D8,0x576A},{0x92D9,0x58F7},{0x92DA,0x5B2C},{0x92DB,0x7D2C}, +{0x92DC,0x722A},{0x92DD,0x540A},{0x92DE,0x91E3},{0x92DF,0x9DB4}, +{0x92E0,0x4EAD},{0x92E1,0x4F4E},{0x92E2,0x505C},{0x92E3,0x5075}, +{0x92E4,0x5243},{0x92E5,0x8C9E},{0x92E6,0x5448},{0x92E7,0x5824}, +{0x92E8,0x5B9A},{0x92E9,0x5E1D},{0x92EA,0x5E95},{0x92EB,0x5EAD}, +{0x92EC,0x5EF7},{0x92ED,0x5F1F},{0x92EE,0x608C},{0x92EF,0x62B5}, +{0x92F0,0x633A},{0x92F1,0x63D0},{0x92F2,0x68AF},{0x92F3,0x6C40}, +{0x92F4,0x7887},{0x92F5,0x798E},{0x92F6,0x7A0B},{0x92F7,0x7DE0}, +{0x92F8,0x8247},{0x92F9,0x8A02},{0x92FA,0x8AE6},{0x92FB,0x8E44}, +{0x92FC,0x9013},{0x9340,0x90B8},{0x9341,0x912D},{0x9342,0x91D8}, +{0x9343,0x9F0E},{0x9344,0x6CE5},{0x9345,0x6458},{0x9346,0x64E2}, +{0x9347,0x6575},{0x9348,0x6EF4},{0x9349,0x7684},{0x934A,0x7B1B}, +{0x934B,0x9069},{0x934C,0x93D1},{0x934D,0x6EBA},{0x934E,0x54F2}, +{0x934F,0x5FB9},{0x9350,0x64A4},{0x9351,0x8F4D},{0x9352,0x8FED}, +{0x9353,0x9244},{0x9354,0x5178},{0x9355,0x586B},{0x9356,0x5929}, +{0x9357,0x5C55},{0x9358,0x5E97},{0x9359,0x6DFB},{0x935A,0x7E8F}, +{0x935B,0x751C},{0x935C,0x8CBC},{0x935D,0x8EE2},{0x935E,0x985B}, +{0x935F,0x70B9},{0x9360,0x4F1D},{0x9361,0x6BBF},{0x9362,0x6FB1}, +{0x9363,0x7530},{0x9364,0x96FB},{0x9365,0x514E},{0x9366,0x5410}, +{0x9367,0x5835},{0x9368,0x5857},{0x9369,0x59AC},{0x936A,0x5C60}, +{0x936B,0x5F92},{0x936C,0x6597},{0x936D,0x675C},{0x936E,0x6E21}, +{0x936F,0x767B},{0x9370,0x83DF},{0x9371,0x8CED},{0x9372,0x9014}, +{0x9373,0x90FD},{0x9374,0x934D},{0x9375,0x7825},{0x9376,0x783A}, +{0x9377,0x52AA},{0x9378,0x5EA6},{0x9379,0x571F},{0x937A,0x5974}, +{0x937B,0x6012},{0x937C,0x5012},{0x937D,0x515A},{0x937E,0x51AC}, +{0x9380,0x51CD},{0x9381,0x5200},{0x9382,0x5510},{0x9383,0x5854}, +{0x9384,0x5858},{0x9385,0x5957},{0x9386,0x5B95},{0x9387,0x5CF6}, +{0x9388,0x5D8B},{0x9389,0x60BC},{0x938A,0x6295},{0x938B,0x642D}, +{0x938C,0x6771},{0x938D,0x6843},{0x938E,0x68BC},{0x938F,0x68DF}, +{0x9390,0x76D7},{0x9391,0x6DD8},{0x9392,0x6E6F},{0x9393,0x6D9B}, +{0x9394,0x706F},{0x9395,0x71C8},{0x9396,0x5F53},{0x9397,0x75D8}, +{0x9398,0x7977},{0x9399,0x7B49},{0x939A,0x7B54},{0x939B,0x7B52}, +{0x939C,0x7CD6},{0x939D,0x7D71},{0x939E,0x5230},{0x939F,0x8463}, +{0x93A0,0x8569},{0x93A1,0x85E4},{0x93A2,0x8A0E},{0x93A3,0x8B04}, +{0x93A4,0x8C46},{0x93A5,0x8E0F},{0x93A6,0x9003},{0x93A7,0x900F}, +{0x93A8,0x9419},{0x93A9,0x9676},{0x93AA,0x982D},{0x93AB,0x9A30}, +{0x93AC,0x95D8},{0x93AD,0x50CD},{0x93AE,0x52D5},{0x93AF,0x540C}, +{0x93B0,0x5802},{0x93B1,0x5C0E},{0x93B2,0x61A7},{0x93B3,0x649E}, +{0x93B4,0x6D1E},{0x93B5,0x77B3},{0x93B6,0x7AE5},{0x93B7,0x80F4}, +{0x93B8,0x8404},{0x93B9,0x9053},{0x93BA,0x9285},{0x93BB,0x5CE0}, +{0x93BC,0x9D07},{0x93BD,0x533F},{0x93BE,0x5F97},{0x93BF,0x5FB3}, +{0x93C0,0x6D9C},{0x93C1,0x7279},{0x93C2,0x7763},{0x93C3,0x79BF}, +{0x93C4,0x7BE4},{0x93C5,0x6BD2},{0x93C6,0x72EC},{0x93C7,0x8AAD}, +{0x93C8,0x6803},{0x93C9,0x6A61},{0x93CA,0x51F8},{0x93CB,0x7A81}, +{0x93CC,0x6934},{0x93CD,0x5C4A},{0x93CE,0x9CF6},{0x93CF,0x82EB}, +{0x93D0,0x5BC5},{0x93D1,0x9149},{0x93D2,0x701E},{0x93D3,0x5678}, +{0x93D4,0x5C6F},{0x93D5,0x60C7},{0x93D6,0x6566},{0x93D7,0x6C8C}, +{0x93D8,0x8C5A},{0x93D9,0x9041},{0x93DA,0x9813},{0x93DB,0x5451}, +{0x93DC,0x66C7},{0x93DD,0x920D},{0x93DE,0x5948},{0x93DF,0x90A3}, +{0x93E0,0x5185},{0x93E1,0x4E4D},{0x93E2,0x51EA},{0x93E3,0x8599}, +{0x93E4,0x8B0E},{0x93E5,0x7058},{0x93E6,0x637A},{0x93E7,0x934B}, +{0x93E8,0x6962},{0x93E9,0x99B4},{0x93EA,0x7E04},{0x93EB,0x7577}, +{0x93EC,0x5357},{0x93ED,0x6960},{0x93EE,0x8EDF},{0x93EF,0x96E3}, +{0x93F0,0x6C5D},{0x93F1,0x4E8C},{0x93F2,0x5C3C},{0x93F3,0x5F10}, +{0x93F4,0x8FE9},{0x93F5,0x5302},{0x93F6,0x8CD1},{0x93F7,0x8089}, +{0x93F8,0x8679},{0x93F9,0x5EFF},{0x93FA,0x65E5},{0x93FB,0x4E73}, +{0x93FC,0x5165},{0x9440,0x5982},{0x9441,0x5C3F},{0x9442,0x97EE}, +{0x9443,0x4EFB},{0x9444,0x598A},{0x9445,0x5FCD},{0x9446,0x8A8D}, +{0x9447,0x6FE1},{0x9448,0x79B0},{0x9449,0x7962},{0x944A,0x5BE7}, +{0x944B,0x8471},{0x944C,0x732B},{0x944D,0x71B1},{0x944E,0x5E74}, +{0x944F,0x5FF5},{0x9450,0x637B},{0x9451,0x649A},{0x9452,0x71C3}, +{0x9453,0x7C98},{0x9454,0x4E43},{0x9455,0x5EFC},{0x9456,0x4E4B}, +{0x9457,0x57DC},{0x9458,0x56A2},{0x9459,0x60A9},{0x945A,0x6FC3}, +{0x945B,0x7D0D},{0x945C,0x80FD},{0x945D,0x8133},{0x945E,0x81BF}, +{0x945F,0x8FB2},{0x9460,0x8997},{0x9461,0x86A4},{0x9462,0x5DF4}, +{0x9463,0x628A},{0x9464,0x64AD},{0x9465,0x8987},{0x9466,0x6777}, +{0x9467,0x6CE2},{0x9468,0x6D3E},{0x9469,0x7436},{0x946A,0x7834}, +{0x946B,0x5A46},{0x946C,0x7F75},{0x946D,0x82AD},{0x946E,0x99AC}, +{0x946F,0x4FF3},{0x9470,0x5EC3},{0x9471,0x62DD},{0x9472,0x6392}, +{0x9473,0x6557},{0x9474,0x676F},{0x9475,0x76C3},{0x9476,0x724C}, +{0x9477,0x80CC},{0x9478,0x80BA},{0x9479,0x8F29},{0x947A,0x914D}, +{0x947B,0x500D},{0x947C,0x57F9},{0x947D,0x5A92},{0x947E,0x6885}, +{0x9480,0x6973},{0x9481,0x7164},{0x9482,0x72FD},{0x9483,0x8CB7}, +{0x9484,0x58F2},{0x9485,0x8CE0},{0x9486,0x966A},{0x9487,0x9019}, +{0x9488,0x877F},{0x9489,0x79E4},{0x948A,0x77E7},{0x948B,0x8429}, +{0x948C,0x4F2F},{0x948D,0x5265},{0x948E,0x535A},{0x948F,0x62CD}, +{0x9490,0x67CF},{0x9491,0x6CCA},{0x9492,0x767D},{0x9493,0x7B94}, +{0x9494,0x7C95},{0x9495,0x8236},{0x9496,0x8584},{0x9497,0x8FEB}, +{0x9498,0x66DD},{0x9499,0x6F20},{0x949A,0x7206},{0x949B,0x7E1B}, +{0x949C,0x83AB},{0x949D,0x99C1},{0x949E,0x9EA6},{0x949F,0x51FD}, +{0x94A0,0x7BB1},{0x94A1,0x7872},{0x94A2,0x7BB8},{0x94A3,0x8087}, +{0x94A4,0x7B48},{0x94A5,0x6AE8},{0x94A6,0x5E61},{0x94A7,0x808C}, +{0x94A8,0x7551},{0x94A9,0x7560},{0x94AA,0x516B},{0x94AB,0x9262}, +{0x94AC,0x6E8C},{0x94AD,0x767A},{0x94AE,0x9197},{0x94AF,0x9AEA}, +{0x94B0,0x4F10},{0x94B1,0x7F70},{0x94B2,0x629C},{0x94B3,0x7B4F}, +{0x94B4,0x95A5},{0x94B5,0x9CE9},{0x94B6,0x567A},{0x94B7,0x5859}, +{0x94B8,0x86E4},{0x94B9,0x96BC},{0x94BA,0x4F34},{0x94BB,0x5224}, +{0x94BC,0x534A},{0x94BD,0x53CD},{0x94BE,0x53DB},{0x94BF,0x5E06}, +{0x94C0,0x642C},{0x94C1,0x6591},{0x94C2,0x677F},{0x94C3,0x6C3E}, +{0x94C4,0x6C4E},{0x94C5,0x7248},{0x94C6,0x72AF},{0x94C7,0x73ED}, +{0x94C8,0x7554},{0x94C9,0x7E41},{0x94CA,0x822C},{0x94CB,0x85E9}, +{0x94CC,0x8CA9},{0x94CD,0x7BC4},{0x94CE,0x91C6},{0x94CF,0x7169}, +{0x94D0,0x9812},{0x94D1,0x98EF},{0x94D2,0x633D},{0x94D3,0x6669}, +{0x94D4,0x756A},{0x94D5,0x76E4},{0x94D6,0x78D0},{0x94D7,0x8543}, +{0x94D8,0x86EE},{0x94D9,0x532A},{0x94DA,0x5351},{0x94DB,0x5426}, +{0x94DC,0x5983},{0x94DD,0x5E87},{0x94DE,0x5F7C},{0x94DF,0x60B2}, +{0x94E0,0x6249},{0x94E1,0x6279},{0x94E2,0x62AB},{0x94E3,0x6590}, +{0x94E4,0x6BD4},{0x94E5,0x6CCC},{0x94E6,0x75B2},{0x94E7,0x76AE}, +{0x94E8,0x7891},{0x94E9,0x79D8},{0x94EA,0x7DCB},{0x94EB,0x7F77}, +{0x94EC,0x80A5},{0x94ED,0x88AB},{0x94EE,0x8AB9},{0x94EF,0x8CBB}, +{0x94F0,0x907F},{0x94F1,0x975E},{0x94F2,0x98DB},{0x94F3,0x6A0B}, +{0x94F4,0x7C38},{0x94F5,0x5099},{0x94F6,0x5C3E},{0x94F7,0x5FAE}, +{0x94F8,0x6787},{0x94F9,0x6BD8},{0x94FA,0x7435},{0x94FB,0x7709}, +{0x94FC,0x7F8E},{0x9540,0x9F3B},{0x9541,0x67CA},{0x9542,0x7A17}, +{0x9543,0x5339},{0x9544,0x758B},{0x9545,0x9AED},{0x9546,0x5F66}, +{0x9547,0x819D},{0x9548,0x83F1},{0x9549,0x8098},{0x954A,0x5F3C}, +{0x954B,0x5FC5},{0x954C,0x7562},{0x954D,0x7B46},{0x954E,0x903C}, +{0x954F,0x6867},{0x9550,0x59EB},{0x9551,0x5A9B},{0x9552,0x7D10}, +{0x9553,0x767E},{0x9554,0x8B2C},{0x9555,0x4FF5},{0x9556,0x5F6A}, +{0x9557,0x6A19},{0x9558,0x6C37},{0x9559,0x6F02},{0x955A,0x74E2}, +{0x955B,0x7968},{0x955C,0x8868},{0x955D,0x8A55},{0x955E,0x8C79}, +{0x955F,0x5EDF},{0x9560,0x63CF},{0x9561,0x75C5},{0x9562,0x79D2}, +{0x9563,0x82D7},{0x9564,0x9328},{0x9565,0x92F2},{0x9566,0x849C}, +{0x9567,0x86ED},{0x9568,0x9C2D},{0x9569,0x54C1},{0x956A,0x5F6C}, +{0x956B,0x658C},{0x956C,0x6D5C},{0x956D,0x7015},{0x956E,0x8CA7}, +{0x956F,0x8CD3},{0x9570,0x983B},{0x9571,0x654F},{0x9572,0x74F6}, +{0x9573,0x4E0D},{0x9574,0x4ED8},{0x9575,0x57E0},{0x9576,0x592B}, +{0x9577,0x5A66},{0x9578,0x5BCC},{0x9579,0x51A8},{0x957A,0x5E03}, +{0x957B,0x5E9C},{0x957C,0x6016},{0x957D,0x6276},{0x957E,0x6577}, +{0x9580,0x65A7},{0x9581,0x666E},{0x9582,0x6D6E},{0x9583,0x7236}, +{0x9584,0x7B26},{0x9585,0x8150},{0x9586,0x819A},{0x9587,0x8299}, +{0x9588,0x8B5C},{0x9589,0x8CA0},{0x958A,0x8CE6},{0x958B,0x8D74}, +{0x958C,0x961C},{0x958D,0x9644},{0x958E,0x4FAE},{0x958F,0x64AB}, +{0x9590,0x6B66},{0x9591,0x821E},{0x9592,0x8461},{0x9593,0x856A}, +{0x9594,0x90E8},{0x9595,0x5C01},{0x9596,0x6953},{0x9597,0x98A8}, +{0x9598,0x847A},{0x9599,0x8557},{0x959A,0x4F0F},{0x959B,0x526F}, +{0x959C,0x5FA9},{0x959D,0x5E45},{0x959E,0x670D},{0x959F,0x798F}, +{0x95A0,0x8179},{0x95A1,0x8907},{0x95A2,0x8986},{0x95A3,0x6DF5}, +{0x95A4,0x5F17},{0x95A5,0x6255},{0x95A6,0x6CB8},{0x95A7,0x4ECF}, +{0x95A8,0x7269},{0x95A9,0x9B92},{0x95AA,0x5206},{0x95AB,0x543B}, +{0x95AC,0x5674},{0x95AD,0x58B3},{0x95AE,0x61A4},{0x95AF,0x626E}, +{0x95B0,0x711A},{0x95B1,0x596E},{0x95B2,0x7C89},{0x95B3,0x7CDE}, +{0x95B4,0x7D1B},{0x95B5,0x96F0},{0x95B6,0x6587},{0x95B7,0x805E}, +{0x95B8,0x4E19},{0x95B9,0x4F75},{0x95BA,0x5175},{0x95BB,0x5840}, +{0x95BC,0x5E63},{0x95BD,0x5E73},{0x95BE,0x5F0A},{0x95BF,0x67C4}, +{0x95C0,0x4E26},{0x95C1,0x853D},{0x95C2,0x9589},{0x95C3,0x965B}, +{0x95C4,0x7C73},{0x95C5,0x9801},{0x95C6,0x50FB},{0x95C7,0x58C1}, +{0x95C8,0x7656},{0x95C9,0x78A7},{0x95CA,0x5225},{0x95CB,0x77A5}, +{0x95CC,0x8511},{0x95CD,0x7B86},{0x95CE,0x504F},{0x95CF,0x5909}, +{0x95D0,0x7247},{0x95D1,0x7BC7},{0x95D2,0x7DE8},{0x95D3,0x8FBA}, +{0x95D4,0x8FD4},{0x95D5,0x904D},{0x95D6,0x4FBF},{0x95D7,0x52C9}, +{0x95D8,0x5A29},{0x95D9,0x5F01},{0x95DA,0x97AD},{0x95DB,0x4FDD}, +{0x95DC,0x8217},{0x95DD,0x92EA},{0x95DE,0x5703},{0x95DF,0x6355}, +{0x95E0,0x6B69},{0x95E1,0x752B},{0x95E2,0x88DC},{0x95E3,0x8F14}, +{0x95E4,0x7A42},{0x95E5,0x52DF},{0x95E6,0x5893},{0x95E7,0x6155}, +{0x95E8,0x620A},{0x95E9,0x66AE},{0x95EA,0x6BCD},{0x95EB,0x7C3F}, +{0x95EC,0x83E9},{0x95ED,0x5023},{0x95EE,0x4FF8},{0x95EF,0x5305}, +{0x95F0,0x5446},{0x95F1,0x5831},{0x95F2,0x5949},{0x95F3,0x5B9D}, +{0x95F4,0x5CF0},{0x95F5,0x5CEF},{0x95F6,0x5D29},{0x95F7,0x5E96}, +{0x95F8,0x62B1},{0x95F9,0x6367},{0x95FA,0x653E},{0x95FB,0x65B9}, +{0x95FC,0x670B},{0x9640,0x6CD5},{0x9641,0x6CE1},{0x9642,0x70F9}, +{0x9643,0x7832},{0x9644,0x7E2B},{0x9645,0x80DE},{0x9646,0x82B3}, +{0x9647,0x840C},{0x9648,0x84EC},{0x9649,0x8702},{0x964A,0x8912}, +{0x964B,0x8A2A},{0x964C,0x8C4A},{0x964D,0x90A6},{0x964E,0x92D2}, +{0x964F,0x98FD},{0x9650,0x9CF3},{0x9651,0x9D6C},{0x9652,0x4E4F}, +{0x9653,0x4EA1},{0x9654,0x508D},{0x9655,0x5256},{0x9656,0x574A}, +{0x9657,0x59A8},{0x9658,0x5E3D},{0x9659,0x5FD8},{0x965A,0x5FD9}, +{0x965B,0x623F},{0x965C,0x66B4},{0x965D,0x671B},{0x965E,0x67D0}, +{0x965F,0x68D2},{0x9660,0x5192},{0x9661,0x7D21},{0x9662,0x80AA}, +{0x9663,0x81A8},{0x9664,0x8B00},{0x9665,0x8C8C},{0x9666,0x8CBF}, +{0x9667,0x927E},{0x9668,0x9632},{0x9669,0x5420},{0x966A,0x982C}, +{0x966B,0x5317},{0x966C,0x50D5},{0x966D,0x535C},{0x966E,0x58A8}, +{0x966F,0x64B2},{0x9670,0x6734},{0x9671,0x7267},{0x9672,0x7766}, +{0x9673,0x7A46},{0x9674,0x91E6},{0x9675,0x52C3},{0x9676,0x6CA1}, +{0x9677,0x6B86},{0x9678,0x5800},{0x9679,0x5E4C},{0x967A,0x5954}, +{0x967B,0x672C},{0x967C,0x7FFB},{0x967D,0x51E1},{0x967E,0x76C6}, +{0x9680,0x6469},{0x9681,0x78E8},{0x9682,0x9B54},{0x9683,0x9EBB}, +{0x9684,0x57CB},{0x9685,0x59B9},{0x9686,0x6627},{0x9687,0x679A}, +{0x9688,0x6BCE},{0x9689,0x54E9},{0x968A,0x69D9},{0x968B,0x5E55}, +{0x968C,0x819C},{0x968D,0x6795},{0x968E,0x9BAA},{0x968F,0x67FE}, +{0x9690,0x9C52},{0x9691,0x685D},{0x9692,0x4EA6},{0x9693,0x4FE3}, +{0x9694,0x53C8},{0x9695,0x62B9},{0x9696,0x672B},{0x9697,0x6CAB}, +{0x9698,0x8FC4},{0x9699,0x4FAD},{0x969A,0x7E6D},{0x969B,0x9EBF}, +{0x969C,0x4E07},{0x969D,0x6162},{0x969E,0x6E80},{0x969F,0x6F2B}, +{0x96A0,0x8513},{0x96A1,0x5473},{0x96A2,0x672A},{0x96A3,0x9B45}, +{0x96A4,0x5DF3},{0x96A5,0x7B95},{0x96A6,0x5CAC},{0x96A7,0x5BC6}, +{0x96A8,0x871C},{0x96A9,0x6E4A},{0x96AA,0x84D1},{0x96AB,0x7A14}, +{0x96AC,0x8108},{0x96AD,0x5999},{0x96AE,0x7C8D},{0x96AF,0x6C11}, +{0x96B0,0x7720},{0x96B1,0x52D9},{0x96B2,0x5922},{0x96B3,0x7121}, +{0x96B4,0x725F},{0x96B5,0x77DB},{0x96B6,0x9727},{0x96B7,0x9D61}, +{0x96B8,0x690B},{0x96B9,0x5A7F},{0x96BA,0x5A18},{0x96BB,0x51A5}, +{0x96BC,0x540D},{0x96BD,0x547D},{0x96BE,0x660E},{0x96BF,0x76DF}, +{0x96C0,0x8FF7},{0x96C1,0x9298},{0x96C2,0x9CF4},{0x96C3,0x59EA}, +{0x96C4,0x725D},{0x96C5,0x6EC5},{0x96C6,0x514D},{0x96C7,0x68C9}, +{0x96C8,0x7DBF},{0x96C9,0x7DEC},{0x96CA,0x9762},{0x96CB,0x9EBA}, +{0x96CC,0x6478},{0x96CD,0x6A21},{0x96CE,0x8302},{0x96CF,0x5984}, +{0x96D0,0x5B5F},{0x96D1,0x6BDB},{0x96D2,0x731B},{0x96D3,0x76F2}, +{0x96D4,0x7DB2},{0x96D5,0x8017},{0x96D6,0x8499},{0x96D7,0x5132}, +{0x96D8,0x6728},{0x96D9,0x9ED9},{0x96DA,0x76EE},{0x96DB,0x6762}, +{0x96DC,0x52FF},{0x96DD,0x9905},{0x96DE,0x5C24},{0x96DF,0x623B}, +{0x96E0,0x7C7E},{0x96E1,0x8CB0},{0x96E2,0x554F},{0x96E3,0x60B6}, +{0x96E4,0x7D0B},{0x96E5,0x9580},{0x96E6,0x5301},{0x96E7,0x4E5F}, +{0x96E8,0x51B6},{0x96E9,0x591C},{0x96EA,0x723A},{0x96EB,0x8036}, +{0x96EC,0x91CE},{0x96ED,0x5F25},{0x96EE,0x77E2},{0x96EF,0x5384}, +{0x96F0,0x5F79},{0x96F1,0x7D04},{0x96F2,0x85AC},{0x96F3,0x8A33}, +{0x96F4,0x8E8D},{0x96F5,0x9756},{0x96F6,0x67F3},{0x96F7,0x85AE}, +{0x96F8,0x9453},{0x96F9,0x6109},{0x96FA,0x6108},{0x96FB,0x6CB9}, +{0x96FC,0x7652},{0x9740,0x8AED},{0x9741,0x8F38},{0x9742,0x552F}, +{0x9743,0x4F51},{0x9744,0x512A},{0x9745,0x52C7},{0x9746,0x53CB}, +{0x9747,0x5BA5},{0x9748,0x5E7D},{0x9749,0x60A0},{0x974A,0x6182}, +{0x974B,0x63D6},{0x974C,0x6709},{0x974D,0x67DA},{0x974E,0x6E67}, +{0x974F,0x6D8C},{0x9750,0x7336},{0x9751,0x7337},{0x9752,0x7531}, +{0x9753,0x7950},{0x9754,0x88D5},{0x9755,0x8A98},{0x9756,0x904A}, +{0x9757,0x9091},{0x9758,0x90F5},{0x9759,0x96C4},{0x975A,0x878D}, +{0x975B,0x5915},{0x975C,0x4E88},{0x975D,0x4F59},{0x975E,0x4E0E}, +{0x975F,0x8A89},{0x9760,0x8F3F},{0x9761,0x9810},{0x9762,0x50AD}, +{0x9763,0x5E7C},{0x9764,0x5996},{0x9765,0x5BB9},{0x9766,0x5EB8}, +{0x9767,0x63DA},{0x9768,0x63FA},{0x9769,0x64C1},{0x976A,0x66DC}, +{0x976B,0x694A},{0x976C,0x69D8},{0x976D,0x6D0B},{0x976E,0x6EB6}, +{0x976F,0x7194},{0x9770,0x7528},{0x9771,0x7AAF},{0x9772,0x7F8A}, +{0x9773,0x8000},{0x9774,0x8449},{0x9775,0x84C9},{0x9776,0x8981}, +{0x9777,0x8B21},{0x9778,0x8E0A},{0x9779,0x9065},{0x977A,0x967D}, +{0x977B,0x990A},{0x977C,0x617E},{0x977D,0x6291},{0x977E,0x6B32}, +{0x9780,0x6C83},{0x9781,0x6D74},{0x9782,0x7FCC},{0x9783,0x7FFC}, +{0x9784,0x6DC0},{0x9785,0x7F85},{0x9786,0x87BA},{0x9787,0x88F8}, +{0x9788,0x6765},{0x9789,0x83B1},{0x978A,0x983C},{0x978B,0x96F7}, +{0x978C,0x6D1B},{0x978D,0x7D61},{0x978E,0x843D},{0x978F,0x916A}, +{0x9790,0x4E71},{0x9791,0x5375},{0x9792,0x5D50},{0x9793,0x6B04}, +{0x9794,0x6FEB},{0x9795,0x85CD},{0x9796,0x862D},{0x9797,0x89A7}, +{0x9798,0x5229},{0x9799,0x540F},{0x979A,0x5C65},{0x979B,0x674E}, +{0x979C,0x68A8},{0x979D,0x7406},{0x979E,0x7483},{0x979F,0x75E2}, +{0x97A0,0x88CF},{0x97A1,0x88E1},{0x97A2,0x91CC},{0x97A3,0x96E2}, +{0x97A4,0x9678},{0x97A5,0x5F8B},{0x97A6,0x7387},{0x97A7,0x7ACB}, +{0x97A8,0x844E},{0x97A9,0x63A0},{0x97AA,0x7565},{0x97AB,0x5289}, +{0x97AC,0x6D41},{0x97AD,0x6E9C},{0x97AE,0x7409},{0x97AF,0x7559}, +{0x97B0,0x786B},{0x97B1,0x7C92},{0x97B2,0x9686},{0x97B3,0x7ADC}, +{0x97B4,0x9F8D},{0x97B5,0x4FB6},{0x97B6,0x616E},{0x97B7,0x65C5}, +{0x97B8,0x865C},{0x97B9,0x4E86},{0x97BA,0x4EAE},{0x97BB,0x50DA}, +{0x97BC,0x4E21},{0x97BD,0x51CC},{0x97BE,0x5BEE},{0x97BF,0x6599}, +{0x97C0,0x6881},{0x97C1,0x6DBC},{0x97C2,0x731F},{0x97C3,0x7642}, +{0x97C4,0x77AD},{0x97C5,0x7A1C},{0x97C6,0x7CE7},{0x97C7,0x826F}, +{0x97C8,0x8AD2},{0x97C9,0x907C},{0x97CA,0x91CF},{0x97CB,0x9675}, +{0x97CC,0x9818},{0x97CD,0x529B},{0x97CE,0x7DD1},{0x97CF,0x502B}, +{0x97D0,0x5398},{0x97D1,0x6797},{0x97D2,0x6DCB},{0x97D3,0x71D0}, +{0x97D4,0x7433},{0x97D5,0x81E8},{0x97D6,0x8F2A},{0x97D7,0x96A3}, +{0x97D8,0x9C57},{0x97D9,0x9E9F},{0x97DA,0x7460},{0x97DB,0x5841}, +{0x97DC,0x6D99},{0x97DD,0x7D2F},{0x97DE,0x985E},{0x97DF,0x4EE4}, +{0x97E0,0x4F36},{0x97E1,0x4F8B},{0x97E2,0x51B7},{0x97E3,0x52B1}, +{0x97E4,0x5DBA},{0x97E5,0x601C},{0x97E6,0x73B2},{0x97E7,0x793C}, +{0x97E8,0x82D3},{0x97E9,0x9234},{0x97EA,0x96B7},{0x97EB,0x96F6}, +{0x97EC,0x970A},{0x97ED,0x9E97},{0x97EE,0x9F62},{0x97EF,0x66A6}, +{0x97F0,0x6B74},{0x97F1,0x5217},{0x97F2,0x52A3},{0x97F3,0x70C8}, +{0x97F4,0x88C2},{0x97F5,0x5EC9},{0x97F6,0x604B},{0x97F7,0x6190}, +{0x97F8,0x6F23},{0x97F9,0x7149},{0x97FA,0x7C3E},{0x97FB,0x7DF4}, +{0x97FC,0x806F},{0x9840,0x84EE},{0x9841,0x9023},{0x9842,0x932C}, +{0x9843,0x5442},{0x9844,0x9B6F},{0x9845,0x6AD3},{0x9846,0x7089}, +{0x9847,0x8CC2},{0x9848,0x8DEF},{0x9849,0x9732},{0x984A,0x52B4}, +{0x984B,0x5A41},{0x984C,0x5ECA},{0x984D,0x5F04},{0x984E,0x6717}, +{0x984F,0x697C},{0x9850,0x6994},{0x9851,0x6D6A},{0x9852,0x6F0F}, +{0x9853,0x7262},{0x9854,0x72FC},{0x9855,0x7BED},{0x9856,0x8001}, +{0x9857,0x807E},{0x9858,0x874B},{0x9859,0x90CE},{0x985A,0x516D}, +{0x985B,0x9E93},{0x985C,0x7984},{0x985D,0x808B},{0x985E,0x9332}, +{0x985F,0x8AD6},{0x9860,0x502D},{0x9861,0x548C},{0x9862,0x8A71}, +{0x9863,0x6B6A},{0x9864,0x8CC4},{0x9865,0x8107},{0x9866,0x60D1}, +{0x9867,0x67A0},{0x9868,0x9DF2},{0x9869,0x4E99},{0x986A,0x4E98}, +{0x986B,0x9C10},{0x986C,0x8A6B},{0x986D,0x85C1},{0x986E,0x8568}, +{0x986F,0x6900},{0x9870,0x6E7E},{0x9871,0x7897},{0x9872,0x8155}, +{0x989F,0x5F0C},{0x98A0,0x4E10},{0x98A1,0x4E15},{0x98A2,0x4E2A}, +{0x98A3,0x4E31},{0x98A4,0x4E36},{0x98A5,0x4E3C},{0x98A6,0x4E3F}, +{0x98A7,0x4E42},{0x98A8,0x4E56},{0x98A9,0x4E58},{0x98AA,0x4E82}, +{0x98AB,0x4E85},{0x98AC,0x8C6B},{0x98AD,0x4E8A},{0x98AE,0x8212}, +{0x98AF,0x5F0D},{0x98B0,0x4E8E},{0x98B1,0x4E9E},{0x98B2,0x4E9F}, +{0x98B3,0x4EA0},{0x98B4,0x4EA2},{0x98B5,0x4EB0},{0x98B6,0x4EB3}, +{0x98B7,0x4EB6},{0x98B8,0x4ECE},{0x98B9,0x4ECD},{0x98BA,0x4EC4}, +{0x98BB,0x4EC6},{0x98BC,0x4EC2},{0x98BD,0x4ED7},{0x98BE,0x4EDE}, +{0x98BF,0x4EED},{0x98C0,0x4EDF},{0x98C1,0x4EF7},{0x98C2,0x4F09}, +{0x98C3,0x4F5A},{0x98C4,0x4F30},{0x98C5,0x4F5B},{0x98C6,0x4F5D}, +{0x98C7,0x4F57},{0x98C8,0x4F47},{0x98C9,0x4F76},{0x98CA,0x4F88}, +{0x98CB,0x4F8F},{0x98CC,0x4F98},{0x98CD,0x4F7B},{0x98CE,0x4F69}, +{0x98CF,0x4F70},{0x98D0,0x4F91},{0x98D1,0x4F6F},{0x98D2,0x4F86}, +{0x98D3,0x4F96},{0x98D4,0x5118},{0x98D5,0x4FD4},{0x98D6,0x4FDF}, +{0x98D7,0x4FCE},{0x98D8,0x4FD8},{0x98D9,0x4FDB},{0x98DA,0x4FD1}, +{0x98DB,0x4FDA},{0x98DC,0x4FD0},{0x98DD,0x4FE4},{0x98DE,0x4FE5}, +{0x98DF,0x501A},{0x98E0,0x5028},{0x98E1,0x5014},{0x98E2,0x502A}, +{0x98E3,0x5025},{0x98E4,0x5005},{0x98E5,0x4F1C},{0x98E6,0x4FF6}, +{0x98E7,0x5021},{0x98E8,0x5029},{0x98E9,0x502C},{0x98EA,0x4FFE}, +{0x98EB,0x4FEF},{0x98EC,0x5011},{0x98ED,0x5006},{0x98EE,0x5043}, +{0x98EF,0x5047},{0x98F0,0x6703},{0x98F1,0x5055},{0x98F2,0x5050}, +{0x98F3,0x5048},{0x98F4,0x505A},{0x98F5,0x5056},{0x98F6,0x506C}, +{0x98F7,0x5078},{0x98F8,0x5080},{0x98F9,0x509A},{0x98FA,0x5085}, +{0x98FB,0x50B4},{0x98FC,0x50B2},{0x9940,0x50C9},{0x9941,0x50CA}, +{0x9942,0x50B3},{0x9943,0x50C2},{0x9944,0x50D6},{0x9945,0x50DE}, +{0x9946,0x50E5},{0x9947,0x50ED},{0x9948,0x50E3},{0x9949,0x50EE}, +{0x994A,0x50F9},{0x994B,0x50F5},{0x994C,0x5109},{0x994D,0x5101}, +{0x994E,0x5102},{0x994F,0x5116},{0x9950,0x5115},{0x9951,0x5114}, +{0x9952,0x511A},{0x9953,0x5121},{0x9954,0x513A},{0x9955,0x5137}, +{0x9956,0x513C},{0x9957,0x513B},{0x9958,0x513F},{0x9959,0x5140}, +{0x995A,0x5152},{0x995B,0x514C},{0x995C,0x5154},{0x995D,0x5162}, +{0x995E,0x7AF8},{0x995F,0x5169},{0x9960,0x516A},{0x9961,0x516E}, +{0x9962,0x5180},{0x9963,0x5182},{0x9964,0x56D8},{0x9965,0x518C}, +{0x9966,0x5189},{0x9967,0x518F},{0x9968,0x5191},{0x9969,0x5193}, +{0x996A,0x5195},{0x996B,0x5196},{0x996C,0x51A4},{0x996D,0x51A6}, +{0x996E,0x51A2},{0x996F,0x51A9},{0x9970,0x51AA},{0x9971,0x51AB}, +{0x9972,0x51B3},{0x9973,0x51B1},{0x9974,0x51B2},{0x9975,0x51B0}, +{0x9976,0x51B5},{0x9977,0x51BD},{0x9978,0x51C5},{0x9979,0x51C9}, +{0x997A,0x51DB},{0x997B,0x51E0},{0x997C,0x8655},{0x997D,0x51E9}, +{0x997E,0x51ED},{0x9980,0x51F0},{0x9981,0x51F5},{0x9982,0x51FE}, +{0x9983,0x5204},{0x9984,0x520B},{0x9985,0x5214},{0x9986,0x520E}, +{0x9987,0x5227},{0x9988,0x522A},{0x9989,0x522E},{0x998A,0x5233}, +{0x998B,0x5239},{0x998C,0x524F},{0x998D,0x5244},{0x998E,0x524B}, +{0x998F,0x524C},{0x9990,0x525E},{0x9991,0x5254},{0x9992,0x526A}, +{0x9993,0x5274},{0x9994,0x5269},{0x9995,0x5273},{0x9996,0x527F}, +{0x9997,0x527D},{0x9998,0x528D},{0x9999,0x5294},{0x999A,0x5292}, +{0x999B,0x5271},{0x999C,0x5288},{0x999D,0x5291},{0x999E,0x8FA8}, +{0x999F,0x8FA7},{0x99A0,0x52AC},{0x99A1,0x52AD},{0x99A2,0x52BC}, +{0x99A3,0x52B5},{0x99A4,0x52C1},{0x99A5,0x52CD},{0x99A6,0x52D7}, +{0x99A7,0x52DE},{0x99A8,0x52E3},{0x99A9,0x52E6},{0x99AA,0x98ED}, +{0x99AB,0x52E0},{0x99AC,0x52F3},{0x99AD,0x52F5},{0x99AE,0x52F8}, +{0x99AF,0x52F9},{0x99B0,0x5306},{0x99B1,0x5308},{0x99B2,0x7538}, +{0x99B3,0x530D},{0x99B4,0x5310},{0x99B5,0x530F},{0x99B6,0x5315}, +{0x99B7,0x531A},{0x99B8,0x5323},{0x99B9,0x532F},{0x99BA,0x5331}, +{0x99BB,0x5333},{0x99BC,0x5338},{0x99BD,0x5340},{0x99BE,0x5346}, +{0x99BF,0x5345},{0x99C0,0x4E17},{0x99C1,0x5349},{0x99C2,0x534D}, +{0x99C3,0x51D6},{0x99C4,0x535E},{0x99C5,0x5369},{0x99C6,0x536E}, +{0x99C7,0x5918},{0x99C8,0x537B},{0x99C9,0x5377},{0x99CA,0x5382}, +{0x99CB,0x5396},{0x99CC,0x53A0},{0x99CD,0x53A6},{0x99CE,0x53A5}, +{0x99CF,0x53AE},{0x99D0,0x53B0},{0x99D1,0x53B6},{0x99D2,0x53C3}, +{0x99D3,0x7C12},{0x99D4,0x96D9},{0x99D5,0x53DF},{0x99D6,0x66FC}, +{0x99D7,0x71EE},{0x99D8,0x53EE},{0x99D9,0x53E8},{0x99DA,0x53ED}, +{0x99DB,0x53FA},{0x99DC,0x5401},{0x99DD,0x543D},{0x99DE,0x5440}, +{0x99DF,0x542C},{0x99E0,0x542D},{0x99E1,0x543C},{0x99E2,0x542E}, +{0x99E3,0x5436},{0x99E4,0x5429},{0x99E5,0x541D},{0x99E6,0x544E}, +{0x99E7,0x548F},{0x99E8,0x5475},{0x99E9,0x548E},{0x99EA,0x545F}, +{0x99EB,0x5471},{0x99EC,0x5477},{0x99ED,0x5470},{0x99EE,0x5492}, +{0x99EF,0x547B},{0x99F0,0x5480},{0x99F1,0x5476},{0x99F2,0x5484}, +{0x99F3,0x5490},{0x99F4,0x5486},{0x99F5,0x54C7},{0x99F6,0x54A2}, +{0x99F7,0x54B8},{0x99F8,0x54A5},{0x99F9,0x54AC},{0x99FA,0x54C4}, +{0x99FB,0x54C8},{0x99FC,0x54A8},{0x9A40,0x54AB},{0x9A41,0x54C2}, +{0x9A42,0x54A4},{0x9A43,0x54BE},{0x9A44,0x54BC},{0x9A45,0x54D8}, +{0x9A46,0x54E5},{0x9A47,0x54E6},{0x9A48,0x550F},{0x9A49,0x5514}, +{0x9A4A,0x54FD},{0x9A4B,0x54EE},{0x9A4C,0x54ED},{0x9A4D,0x54FA}, +{0x9A4E,0x54E2},{0x9A4F,0x5539},{0x9A50,0x5540},{0x9A51,0x5563}, +{0x9A52,0x554C},{0x9A53,0x552E},{0x9A54,0x555C},{0x9A55,0x5545}, +{0x9A56,0x5556},{0x9A57,0x5557},{0x9A58,0x5538},{0x9A59,0x5533}, +{0x9A5A,0x555D},{0x9A5B,0x5599},{0x9A5C,0x5580},{0x9A5D,0x54AF}, +{0x9A5E,0x558A},{0x9A5F,0x559F},{0x9A60,0x557B},{0x9A61,0x557E}, +{0x9A62,0x5598},{0x9A63,0x559E},{0x9A64,0x55AE},{0x9A65,0x557C}, +{0x9A66,0x5583},{0x9A67,0x55A9},{0x9A68,0x5587},{0x9A69,0x55A8}, +{0x9A6A,0x55DA},{0x9A6B,0x55C5},{0x9A6C,0x55DF},{0x9A6D,0x55C4}, +{0x9A6E,0x55DC},{0x9A6F,0x55E4},{0x9A70,0x55D4},{0x9A71,0x5614}, +{0x9A72,0x55F7},{0x9A73,0x5616},{0x9A74,0x55FE},{0x9A75,0x55FD}, +{0x9A76,0x561B},{0x9A77,0x55F9},{0x9A78,0x564E},{0x9A79,0x5650}, +{0x9A7A,0x71DF},{0x9A7B,0x5634},{0x9A7C,0x5636},{0x9A7D,0x5632}, +{0x9A7E,0x5638},{0x9A80,0x566B},{0x9A81,0x5664},{0x9A82,0x562F}, +{0x9A83,0x566C},{0x9A84,0x566A},{0x9A85,0x5686},{0x9A86,0x5680}, +{0x9A87,0x568A},{0x9A88,0x56A0},{0x9A89,0x5694},{0x9A8A,0x568F}, +{0x9A8B,0x56A5},{0x9A8C,0x56AE},{0x9A8D,0x56B6},{0x9A8E,0x56B4}, +{0x9A8F,0x56C2},{0x9A90,0x56BC},{0x9A91,0x56C1},{0x9A92,0x56C3}, +{0x9A93,0x56C0},{0x9A94,0x56C8},{0x9A95,0x56CE},{0x9A96,0x56D1}, +{0x9A97,0x56D3},{0x9A98,0x56D7},{0x9A99,0x56EE},{0x9A9A,0x56F9}, +{0x9A9B,0x5700},{0x9A9C,0x56FF},{0x9A9D,0x5704},{0x9A9E,0x5709}, +{0x9A9F,0x5708},{0x9AA0,0x570B},{0x9AA1,0x570D},{0x9AA2,0x5713}, +{0x9AA3,0x5718},{0x9AA4,0x5716},{0x9AA5,0x55C7},{0x9AA6,0x571C}, +{0x9AA7,0x5726},{0x9AA8,0x5737},{0x9AA9,0x5738},{0x9AAA,0x574E}, +{0x9AAB,0x573B},{0x9AAC,0x5740},{0x9AAD,0x574F},{0x9AAE,0x5769}, +{0x9AAF,0x57C0},{0x9AB0,0x5788},{0x9AB1,0x5761},{0x9AB2,0x577F}, +{0x9AB3,0x5789},{0x9AB4,0x5793},{0x9AB5,0x57A0},{0x9AB6,0x57B3}, +{0x9AB7,0x57A4},{0x9AB8,0x57AA},{0x9AB9,0x57B0},{0x9ABA,0x57C3}, +{0x9ABB,0x57C6},{0x9ABC,0x57D4},{0x9ABD,0x57D2},{0x9ABE,0x57D3}, +{0x9ABF,0x580A},{0x9AC0,0x57D6},{0x9AC1,0x57E3},{0x9AC2,0x580B}, +{0x9AC3,0x5819},{0x9AC4,0x581D},{0x9AC5,0x5872},{0x9AC6,0x5821}, +{0x9AC7,0x5862},{0x9AC8,0x584B},{0x9AC9,0x5870},{0x9ACA,0x6BC0}, +{0x9ACB,0x5852},{0x9ACC,0x583D},{0x9ACD,0x5879},{0x9ACE,0x5885}, +{0x9ACF,0x58B9},{0x9AD0,0x589F},{0x9AD1,0x58AB},{0x9AD2,0x58BA}, +{0x9AD3,0x58DE},{0x9AD4,0x58BB},{0x9AD5,0x58B8},{0x9AD6,0x58AE}, +{0x9AD7,0x58C5},{0x9AD8,0x58D3},{0x9AD9,0x58D1},{0x9ADA,0x58D7}, +{0x9ADB,0x58D9},{0x9ADC,0x58D8},{0x9ADD,0x58E5},{0x9ADE,0x58DC}, +{0x9ADF,0x58E4},{0x9AE0,0x58DF},{0x9AE1,0x58EF},{0x9AE2,0x58FA}, +{0x9AE3,0x58F9},{0x9AE4,0x58FB},{0x9AE5,0x58FC},{0x9AE6,0x58FD}, +{0x9AE7,0x5902},{0x9AE8,0x590A},{0x9AE9,0x5910},{0x9AEA,0x591B}, +{0x9AEB,0x68A6},{0x9AEC,0x5925},{0x9AED,0x592C},{0x9AEE,0x592D}, +{0x9AEF,0x5932},{0x9AF0,0x5938},{0x9AF1,0x593E},{0x9AF2,0x7AD2}, +{0x9AF3,0x5955},{0x9AF4,0x5950},{0x9AF5,0x594E},{0x9AF6,0x595A}, +{0x9AF7,0x5958},{0x9AF8,0x5962},{0x9AF9,0x5960},{0x9AFA,0x5967}, +{0x9AFB,0x596C},{0x9AFC,0x5969},{0x9B40,0x5978},{0x9B41,0x5981}, +{0x9B42,0x599D},{0x9B43,0x4F5E},{0x9B44,0x4FAB},{0x9B45,0x59A3}, +{0x9B46,0x59B2},{0x9B47,0x59C6},{0x9B48,0x59E8},{0x9B49,0x59DC}, +{0x9B4A,0x598D},{0x9B4B,0x59D9},{0x9B4C,0x59DA},{0x9B4D,0x5A25}, +{0x9B4E,0x5A1F},{0x9B4F,0x5A11},{0x9B50,0x5A1C},{0x9B51,0x5A09}, +{0x9B52,0x5A1A},{0x9B53,0x5A40},{0x9B54,0x5A6C},{0x9B55,0x5A49}, +{0x9B56,0x5A35},{0x9B57,0x5A36},{0x9B58,0x5A62},{0x9B59,0x5A6A}, +{0x9B5A,0x5A9A},{0x9B5B,0x5ABC},{0x9B5C,0x5ABE},{0x9B5D,0x5ACB}, +{0x9B5E,0x5AC2},{0x9B5F,0x5ABD},{0x9B60,0x5AE3},{0x9B61,0x5AD7}, +{0x9B62,0x5AE6},{0x9B63,0x5AE9},{0x9B64,0x5AD6},{0x9B65,0x5AFA}, +{0x9B66,0x5AFB},{0x9B67,0x5B0C},{0x9B68,0x5B0B},{0x9B69,0x5B16}, +{0x9B6A,0x5B32},{0x9B6B,0x5AD0},{0x9B6C,0x5B2A},{0x9B6D,0x5B36}, +{0x9B6E,0x5B3E},{0x9B6F,0x5B43},{0x9B70,0x5B45},{0x9B71,0x5B40}, +{0x9B72,0x5B51},{0x9B73,0x5B55},{0x9B74,0x5B5A},{0x9B75,0x5B5B}, +{0x9B76,0x5B65},{0x9B77,0x5B69},{0x9B78,0x5B70},{0x9B79,0x5B73}, +{0x9B7A,0x5B75},{0x9B7B,0x5B78},{0x9B7C,0x6588},{0x9B7D,0x5B7A}, +{0x9B7E,0x5B80},{0x9B80,0x5B83},{0x9B81,0x5BA6},{0x9B82,0x5BB8}, +{0x9B83,0x5BC3},{0x9B84,0x5BC7},{0x9B85,0x5BC9},{0x9B86,0x5BD4}, +{0x9B87,0x5BD0},{0x9B88,0x5BE4},{0x9B89,0x5BE6},{0x9B8A,0x5BE2}, +{0x9B8B,0x5BDE},{0x9B8C,0x5BE5},{0x9B8D,0x5BEB},{0x9B8E,0x5BF0}, +{0x9B8F,0x5BF6},{0x9B90,0x5BF3},{0x9B91,0x5C05},{0x9B92,0x5C07}, +{0x9B93,0x5C08},{0x9B94,0x5C0D},{0x9B95,0x5C13},{0x9B96,0x5C20}, +{0x9B97,0x5C22},{0x9B98,0x5C28},{0x9B99,0x5C38},{0x9B9A,0x5C39}, +{0x9B9B,0x5C41},{0x9B9C,0x5C46},{0x9B9D,0x5C4E},{0x9B9E,0x5C53}, +{0x9B9F,0x5C50},{0x9BA0,0x5C4F},{0x9BA1,0x5B71},{0x9BA2,0x5C6C}, +{0x9BA3,0x5C6E},{0x9BA4,0x4E62},{0x9BA5,0x5C76},{0x9BA6,0x5C79}, +{0x9BA7,0x5C8C},{0x9BA8,0x5C91},{0x9BA9,0x5C94},{0x9BAA,0x599B}, +{0x9BAB,0x5CAB},{0x9BAC,0x5CBB},{0x9BAD,0x5CB6},{0x9BAE,0x5CBC}, +{0x9BAF,0x5CB7},{0x9BB0,0x5CC5},{0x9BB1,0x5CBE},{0x9BB2,0x5CC7}, +{0x9BB3,0x5CD9},{0x9BB4,0x5CE9},{0x9BB5,0x5CFD},{0x9BB6,0x5CFA}, +{0x9BB7,0x5CED},{0x9BB8,0x5D8C},{0x9BB9,0x5CEA},{0x9BBA,0x5D0B}, +{0x9BBB,0x5D15},{0x9BBC,0x5D17},{0x9BBD,0x5D5C},{0x9BBE,0x5D1F}, +{0x9BBF,0x5D1B},{0x9BC0,0x5D11},{0x9BC1,0x5D14},{0x9BC2,0x5D22}, +{0x9BC3,0x5D1A},{0x9BC4,0x5D19},{0x9BC5,0x5D18},{0x9BC6,0x5D4C}, +{0x9BC7,0x5D52},{0x9BC8,0x5D4E},{0x9BC9,0x5D4B},{0x9BCA,0x5D6C}, +{0x9BCB,0x5D73},{0x9BCC,0x5D76},{0x9BCD,0x5D87},{0x9BCE,0x5D84}, +{0x9BCF,0x5D82},{0x9BD0,0x5DA2},{0x9BD1,0x5D9D},{0x9BD2,0x5DAC}, +{0x9BD3,0x5DAE},{0x9BD4,0x5DBD},{0x9BD5,0x5D90},{0x9BD6,0x5DB7}, +{0x9BD7,0x5DBC},{0x9BD8,0x5DC9},{0x9BD9,0x5DCD},{0x9BDA,0x5DD3}, +{0x9BDB,0x5DD2},{0x9BDC,0x5DD6},{0x9BDD,0x5DDB},{0x9BDE,0x5DEB}, +{0x9BDF,0x5DF2},{0x9BE0,0x5DF5},{0x9BE1,0x5E0B},{0x9BE2,0x5E1A}, +{0x9BE3,0x5E19},{0x9BE4,0x5E11},{0x9BE5,0x5E1B},{0x9BE6,0x5E36}, +{0x9BE7,0x5E37},{0x9BE8,0x5E44},{0x9BE9,0x5E43},{0x9BEA,0x5E40}, +{0x9BEB,0x5E4E},{0x9BEC,0x5E57},{0x9BED,0x5E54},{0x9BEE,0x5E5F}, +{0x9BEF,0x5E62},{0x9BF0,0x5E64},{0x9BF1,0x5E47},{0x9BF2,0x5E75}, +{0x9BF3,0x5E76},{0x9BF4,0x5E7A},{0x9BF5,0x9EBC},{0x9BF6,0x5E7F}, +{0x9BF7,0x5EA0},{0x9BF8,0x5EC1},{0x9BF9,0x5EC2},{0x9BFA,0x5EC8}, +{0x9BFB,0x5ED0},{0x9BFC,0x5ECF},{0x9C40,0x5ED6},{0x9C41,0x5EE3}, +{0x9C42,0x5EDD},{0x9C43,0x5EDA},{0x9C44,0x5EDB},{0x9C45,0x5EE2}, +{0x9C46,0x5EE1},{0x9C47,0x5EE8},{0x9C48,0x5EE9},{0x9C49,0x5EEC}, +{0x9C4A,0x5EF1},{0x9C4B,0x5EF3},{0x9C4C,0x5EF0},{0x9C4D,0x5EF4}, +{0x9C4E,0x5EF8},{0x9C4F,0x5EFE},{0x9C50,0x5F03},{0x9C51,0x5F09}, +{0x9C52,0x5F5D},{0x9C53,0x5F5C},{0x9C54,0x5F0B},{0x9C55,0x5F11}, +{0x9C56,0x5F16},{0x9C57,0x5F29},{0x9C58,0x5F2D},{0x9C59,0x5F38}, +{0x9C5A,0x5F41},{0x9C5B,0x5F48},{0x9C5C,0x5F4C},{0x9C5D,0x5F4E}, +{0x9C5E,0x5F2F},{0x9C5F,0x5F51},{0x9C60,0x5F56},{0x9C61,0x5F57}, +{0x9C62,0x5F59},{0x9C63,0x5F61},{0x9C64,0x5F6D},{0x9C65,0x5F73}, +{0x9C66,0x5F77},{0x9C67,0x5F83},{0x9C68,0x5F82},{0x9C69,0x5F7F}, +{0x9C6A,0x5F8A},{0x9C6B,0x5F88},{0x9C6C,0x5F91},{0x9C6D,0x5F87}, +{0x9C6E,0x5F9E},{0x9C6F,0x5F99},{0x9C70,0x5F98},{0x9C71,0x5FA0}, +{0x9C72,0x5FA8},{0x9C73,0x5FAD},{0x9C74,0x5FBC},{0x9C75,0x5FD6}, +{0x9C76,0x5FFB},{0x9C77,0x5FE4},{0x9C78,0x5FF8},{0x9C79,0x5FF1}, +{0x9C7A,0x5FDD},{0x9C7B,0x60B3},{0x9C7C,0x5FFF},{0x9C7D,0x6021}, +{0x9C7E,0x6060},{0x9C80,0x6019},{0x9C81,0x6010},{0x9C82,0x6029}, +{0x9C83,0x600E},{0x9C84,0x6031},{0x9C85,0x601B},{0x9C86,0x6015}, +{0x9C87,0x602B},{0x9C88,0x6026},{0x9C89,0x600F},{0x9C8A,0x603A}, +{0x9C8B,0x605A},{0x9C8C,0x6041},{0x9C8D,0x606A},{0x9C8E,0x6077}, +{0x9C8F,0x605F},{0x9C90,0x604A},{0x9C91,0x6046},{0x9C92,0x604D}, +{0x9C93,0x6063},{0x9C94,0x6043},{0x9C95,0x6064},{0x9C96,0x6042}, +{0x9C97,0x606C},{0x9C98,0x606B},{0x9C99,0x6059},{0x9C9A,0x6081}, +{0x9C9B,0x608D},{0x9C9C,0x60E7},{0x9C9D,0x6083},{0x9C9E,0x609A}, +{0x9C9F,0x6084},{0x9CA0,0x609B},{0x9CA1,0x6096},{0x9CA2,0x6097}, +{0x9CA3,0x6092},{0x9CA4,0x60A7},{0x9CA5,0x608B},{0x9CA6,0x60E1}, +{0x9CA7,0x60B8},{0x9CA8,0x60E0},{0x9CA9,0x60D3},{0x9CAA,0x60B4}, +{0x9CAB,0x5FF0},{0x9CAC,0x60BD},{0x9CAD,0x60C6},{0x9CAE,0x60B5}, +{0x9CAF,0x60D8},{0x9CB0,0x614D},{0x9CB1,0x6115},{0x9CB2,0x6106}, +{0x9CB3,0x60F6},{0x9CB4,0x60F7},{0x9CB5,0x6100},{0x9CB6,0x60F4}, +{0x9CB7,0x60FA},{0x9CB8,0x6103},{0x9CB9,0x6121},{0x9CBA,0x60FB}, +{0x9CBB,0x60F1},{0x9CBC,0x610D},{0x9CBD,0x610E},{0x9CBE,0x6147}, +{0x9CBF,0x613E},{0x9CC0,0x6128},{0x9CC1,0x6127},{0x9CC2,0x614A}, +{0x9CC3,0x613F},{0x9CC4,0x613C},{0x9CC5,0x612C},{0x9CC6,0x6134}, +{0x9CC7,0x613D},{0x9CC8,0x6142},{0x9CC9,0x6144},{0x9CCA,0x6173}, +{0x9CCB,0x6177},{0x9CCC,0x6158},{0x9CCD,0x6159},{0x9CCE,0x615A}, +{0x9CCF,0x616B},{0x9CD0,0x6174},{0x9CD1,0x616F},{0x9CD2,0x6165}, +{0x9CD3,0x6171},{0x9CD4,0x615F},{0x9CD5,0x615D},{0x9CD6,0x6153}, +{0x9CD7,0x6175},{0x9CD8,0x6199},{0x9CD9,0x6196},{0x9CDA,0x6187}, +{0x9CDB,0x61AC},{0x9CDC,0x6194},{0x9CDD,0x619A},{0x9CDE,0x618A}, +{0x9CDF,0x6191},{0x9CE0,0x61AB},{0x9CE1,0x61AE},{0x9CE2,0x61CC}, +{0x9CE3,0x61CA},{0x9CE4,0x61C9},{0x9CE5,0x61F7},{0x9CE6,0x61C8}, +{0x9CE7,0x61C3},{0x9CE8,0x61C6},{0x9CE9,0x61BA},{0x9CEA,0x61CB}, +{0x9CEB,0x7F79},{0x9CEC,0x61CD},{0x9CED,0x61E6},{0x9CEE,0x61E3}, +{0x9CEF,0x61F6},{0x9CF0,0x61FA},{0x9CF1,0x61F4},{0x9CF2,0x61FF}, +{0x9CF3,0x61FD},{0x9CF4,0x61FC},{0x9CF5,0x61FE},{0x9CF6,0x6200}, +{0x9CF7,0x6208},{0x9CF8,0x6209},{0x9CF9,0x620D},{0x9CFA,0x620C}, +{0x9CFB,0x6214},{0x9CFC,0x621B},{0x9D40,0x621E},{0x9D41,0x6221}, +{0x9D42,0x622A},{0x9D43,0x622E},{0x9D44,0x6230},{0x9D45,0x6232}, +{0x9D46,0x6233},{0x9D47,0x6241},{0x9D48,0x624E},{0x9D49,0x625E}, +{0x9D4A,0x6263},{0x9D4B,0x625B},{0x9D4C,0x6260},{0x9D4D,0x6268}, +{0x9D4E,0x627C},{0x9D4F,0x6282},{0x9D50,0x6289},{0x9D51,0x627E}, +{0x9D52,0x6292},{0x9D53,0x6293},{0x9D54,0x6296},{0x9D55,0x62D4}, +{0x9D56,0x6283},{0x9D57,0x6294},{0x9D58,0x62D7},{0x9D59,0x62D1}, +{0x9D5A,0x62BB},{0x9D5B,0x62CF},{0x9D5C,0x62FF},{0x9D5D,0x62C6}, +{0x9D5E,0x64D4},{0x9D5F,0x62C8},{0x9D60,0x62DC},{0x9D61,0x62CC}, +{0x9D62,0x62CA},{0x9D63,0x62C2},{0x9D64,0x62C7},{0x9D65,0x629B}, +{0x9D66,0x62C9},{0x9D67,0x630C},{0x9D68,0x62EE},{0x9D69,0x62F1}, +{0x9D6A,0x6327},{0x9D6B,0x6302},{0x9D6C,0x6308},{0x9D6D,0x62EF}, +{0x9D6E,0x62F5},{0x9D6F,0x6350},{0x9D70,0x633E},{0x9D71,0x634D}, +{0x9D72,0x641C},{0x9D73,0x634F},{0x9D74,0x6396},{0x9D75,0x638E}, +{0x9D76,0x6380},{0x9D77,0x63AB},{0x9D78,0x6376},{0x9D79,0x63A3}, +{0x9D7A,0x638F},{0x9D7B,0x6389},{0x9D7C,0x639F},{0x9D7D,0x63B5}, +{0x9D7E,0x636B},{0x9D80,0x6369},{0x9D81,0x63BE},{0x9D82,0x63E9}, +{0x9D83,0x63C0},{0x9D84,0x63C6},{0x9D85,0x63E3},{0x9D86,0x63C9}, +{0x9D87,0x63D2},{0x9D88,0x63F6},{0x9D89,0x63C4},{0x9D8A,0x6416}, +{0x9D8B,0x6434},{0x9D8C,0x6406},{0x9D8D,0x6413},{0x9D8E,0x6426}, +{0x9D8F,0x6436},{0x9D90,0x651D},{0x9D91,0x6417},{0x9D92,0x6428}, +{0x9D93,0x640F},{0x9D94,0x6467},{0x9D95,0x646F},{0x9D96,0x6476}, +{0x9D97,0x644E},{0x9D98,0x652A},{0x9D99,0x6495},{0x9D9A,0x6493}, +{0x9D9B,0x64A5},{0x9D9C,0x64A9},{0x9D9D,0x6488},{0x9D9E,0x64BC}, +{0x9D9F,0x64DA},{0x9DA0,0x64D2},{0x9DA1,0x64C5},{0x9DA2,0x64C7}, +{0x9DA3,0x64BB},{0x9DA4,0x64D8},{0x9DA5,0x64C2},{0x9DA6,0x64F1}, +{0x9DA7,0x64E7},{0x9DA8,0x8209},{0x9DA9,0x64E0},{0x9DAA,0x64E1}, +{0x9DAB,0x62AC},{0x9DAC,0x64E3},{0x9DAD,0x64EF},{0x9DAE,0x652C}, +{0x9DAF,0x64F6},{0x9DB0,0x64F4},{0x9DB1,0x64F2},{0x9DB2,0x64FA}, +{0x9DB3,0x6500},{0x9DB4,0x64FD},{0x9DB5,0x6518},{0x9DB6,0x651C}, +{0x9DB7,0x6505},{0x9DB8,0x6524},{0x9DB9,0x6523},{0x9DBA,0x652B}, +{0x9DBB,0x6534},{0x9DBC,0x6535},{0x9DBD,0x6537},{0x9DBE,0x6536}, +{0x9DBF,0x6538},{0x9DC0,0x754B},{0x9DC1,0x6548},{0x9DC2,0x6556}, +{0x9DC3,0x6555},{0x9DC4,0x654D},{0x9DC5,0x6558},{0x9DC6,0x655E}, +{0x9DC7,0x655D},{0x9DC8,0x6572},{0x9DC9,0x6578},{0x9DCA,0x6582}, +{0x9DCB,0x6583},{0x9DCC,0x8B8A},{0x9DCD,0x659B},{0x9DCE,0x659F}, +{0x9DCF,0x65AB},{0x9DD0,0x65B7},{0x9DD1,0x65C3},{0x9DD2,0x65C6}, +{0x9DD3,0x65C1},{0x9DD4,0x65C4},{0x9DD5,0x65CC},{0x9DD6,0x65D2}, +{0x9DD7,0x65DB},{0x9DD8,0x65D9},{0x9DD9,0x65E0},{0x9DDA,0x65E1}, +{0x9DDB,0x65F1},{0x9DDC,0x6772},{0x9DDD,0x660A},{0x9DDE,0x6603}, +{0x9DDF,0x65FB},{0x9DE0,0x6773},{0x9DE1,0x6635},{0x9DE2,0x6636}, +{0x9DE3,0x6634},{0x9DE4,0x661C},{0x9DE5,0x664F},{0x9DE6,0x6644}, +{0x9DE7,0x6649},{0x9DE8,0x6641},{0x9DE9,0x665E},{0x9DEA,0x665D}, +{0x9DEB,0x6664},{0x9DEC,0x6667},{0x9DED,0x6668},{0x9DEE,0x665F}, +{0x9DEF,0x6662},{0x9DF0,0x6670},{0x9DF1,0x6683},{0x9DF2,0x6688}, +{0x9DF3,0x668E},{0x9DF4,0x6689},{0x9DF5,0x6684},{0x9DF6,0x6698}, +{0x9DF7,0x669D},{0x9DF8,0x66C1},{0x9DF9,0x66B9},{0x9DFA,0x66C9}, +{0x9DFB,0x66BE},{0x9DFC,0x66BC},{0x9E40,0x66C4},{0x9E41,0x66B8}, +{0x9E42,0x66D6},{0x9E43,0x66DA},{0x9E44,0x66E0},{0x9E45,0x663F}, +{0x9E46,0x66E6},{0x9E47,0x66E9},{0x9E48,0x66F0},{0x9E49,0x66F5}, +{0x9E4A,0x66F7},{0x9E4B,0x670F},{0x9E4C,0x6716},{0x9E4D,0x671E}, +{0x9E4E,0x6726},{0x9E4F,0x6727},{0x9E50,0x9738},{0x9E51,0x672E}, +{0x9E52,0x673F},{0x9E53,0x6736},{0x9E54,0x6741},{0x9E55,0x6738}, +{0x9E56,0x6737},{0x9E57,0x6746},{0x9E58,0x675E},{0x9E59,0x6760}, +{0x9E5A,0x6759},{0x9E5B,0x6763},{0x9E5C,0x6764},{0x9E5D,0x6789}, +{0x9E5E,0x6770},{0x9E5F,0x67A9},{0x9E60,0x677C},{0x9E61,0x676A}, +{0x9E62,0x678C},{0x9E63,0x678B},{0x9E64,0x67A6},{0x9E65,0x67A1}, +{0x9E66,0x6785},{0x9E67,0x67B7},{0x9E68,0x67EF},{0x9E69,0x67B4}, +{0x9E6A,0x67EC},{0x9E6B,0x67B3},{0x9E6C,0x67E9},{0x9E6D,0x67B8}, +{0x9E6E,0x67E4},{0x9E6F,0x67DE},{0x9E70,0x67DD},{0x9E71,0x67E2}, +{0x9E72,0x67EE},{0x9E73,0x67B9},{0x9E74,0x67CE},{0x9E75,0x67C6}, +{0x9E76,0x67E7},{0x9E77,0x6A9C},{0x9E78,0x681E},{0x9E79,0x6846}, +{0x9E7A,0x6829},{0x9E7B,0x6840},{0x9E7C,0x684D},{0x9E7D,0x6832}, +{0x9E7E,0x684E},{0x9E80,0x68B3},{0x9E81,0x682B},{0x9E82,0x6859}, +{0x9E83,0x6863},{0x9E84,0x6877},{0x9E85,0x687F},{0x9E86,0x689F}, +{0x9E87,0x688F},{0x9E88,0x68AD},{0x9E89,0x6894},{0x9E8A,0x689D}, +{0x9E8B,0x689B},{0x9E8C,0x6883},{0x9E8D,0x6AAE},{0x9E8E,0x68B9}, +{0x9E8F,0x6874},{0x9E90,0x68B5},{0x9E91,0x68A0},{0x9E92,0x68BA}, +{0x9E93,0x690F},{0x9E94,0x688D},{0x9E95,0x687E},{0x9E96,0x6901}, +{0x9E97,0x68CA},{0x9E98,0x6908},{0x9E99,0x68D8},{0x9E9A,0x6922}, +{0x9E9B,0x6926},{0x9E9C,0x68E1},{0x9E9D,0x690C},{0x9E9E,0x68CD}, +{0x9E9F,0x68D4},{0x9EA0,0x68E7},{0x9EA1,0x68D5},{0x9EA2,0x6936}, +{0x9EA3,0x6912},{0x9EA4,0x6904},{0x9EA5,0x68D7},{0x9EA6,0x68E3}, +{0x9EA7,0x6925},{0x9EA8,0x68F9},{0x9EA9,0x68E0},{0x9EAA,0x68EF}, +{0x9EAB,0x6928},{0x9EAC,0x692A},{0x9EAD,0x691A},{0x9EAE,0x6923}, +{0x9EAF,0x6921},{0x9EB0,0x68C6},{0x9EB1,0x6979},{0x9EB2,0x6977}, +{0x9EB3,0x695C},{0x9EB4,0x6978},{0x9EB5,0x696B},{0x9EB6,0x6954}, +{0x9EB7,0x697E},{0x9EB8,0x696E},{0x9EB9,0x6939},{0x9EBA,0x6974}, +{0x9EBB,0x693D},{0x9EBC,0x6959},{0x9EBD,0x6930},{0x9EBE,0x6961}, +{0x9EBF,0x695E},{0x9EC0,0x695D},{0x9EC1,0x6981},{0x9EC2,0x696A}, +{0x9EC3,0x69B2},{0x9EC4,0x69AE},{0x9EC5,0x69D0},{0x9EC6,0x69BF}, +{0x9EC7,0x69C1},{0x9EC8,0x69D3},{0x9EC9,0x69BE},{0x9ECA,0x69CE}, +{0x9ECB,0x5BE8},{0x9ECC,0x69CA},{0x9ECD,0x69DD},{0x9ECE,0x69BB}, +{0x9ECF,0x69C3},{0x9ED0,0x69A7},{0x9ED1,0x6A2E},{0x9ED2,0x6991}, +{0x9ED3,0x69A0},{0x9ED4,0x699C},{0x9ED5,0x6995},{0x9ED6,0x69B4}, +{0x9ED7,0x69DE},{0x9ED8,0x69E8},{0x9ED9,0x6A02},{0x9EDA,0x6A1B}, +{0x9EDB,0x69FF},{0x9EDC,0x6B0A},{0x9EDD,0x69F9},{0x9EDE,0x69F2}, +{0x9EDF,0x69E7},{0x9EE0,0x6A05},{0x9EE1,0x69B1},{0x9EE2,0x6A1E}, +{0x9EE3,0x69ED},{0x9EE4,0x6A14},{0x9EE5,0x69EB},{0x9EE6,0x6A0A}, +{0x9EE7,0x6A12},{0x9EE8,0x6AC1},{0x9EE9,0x6A23},{0x9EEA,0x6A13}, +{0x9EEB,0x6A44},{0x9EEC,0x6A0C},{0x9EED,0x6A72},{0x9EEE,0x6A36}, +{0x9EEF,0x6A78},{0x9EF0,0x6A47},{0x9EF1,0x6A62},{0x9EF2,0x6A59}, +{0x9EF3,0x6A66},{0x9EF4,0x6A48},{0x9EF5,0x6A38},{0x9EF6,0x6A22}, +{0x9EF7,0x6A90},{0x9EF8,0x6A8D},{0x9EF9,0x6AA0},{0x9EFA,0x6A84}, +{0x9EFB,0x6AA2},{0x9EFC,0x6AA3},{0x9F40,0x6A97},{0x9F41,0x8617}, +{0x9F42,0x6ABB},{0x9F43,0x6AC3},{0x9F44,0x6AC2},{0x9F45,0x6AB8}, +{0x9F46,0x6AB3},{0x9F47,0x6AAC},{0x9F48,0x6ADE},{0x9F49,0x6AD1}, +{0x9F4A,0x6ADF},{0x9F4B,0x6AAA},{0x9F4C,0x6ADA},{0x9F4D,0x6AEA}, +{0x9F4E,0x6AFB},{0x9F4F,0x6B05},{0x9F50,0x8616},{0x9F51,0x6AFA}, +{0x9F52,0x6B12},{0x9F53,0x6B16},{0x9F54,0x9B31},{0x9F55,0x6B1F}, +{0x9F56,0x6B38},{0x9F57,0x6B37},{0x9F58,0x76DC},{0x9F59,0x6B39}, +{0x9F5A,0x98EE},{0x9F5B,0x6B47},{0x9F5C,0x6B43},{0x9F5D,0x6B49}, +{0x9F5E,0x6B50},{0x9F5F,0x6B59},{0x9F60,0x6B54},{0x9F61,0x6B5B}, +{0x9F62,0x6B5F},{0x9F63,0x6B61},{0x9F64,0x6B78},{0x9F65,0x6B79}, +{0x9F66,0x6B7F},{0x9F67,0x6B80},{0x9F68,0x6B84},{0x9F69,0x6B83}, +{0x9F6A,0x6B8D},{0x9F6B,0x6B98},{0x9F6C,0x6B95},{0x9F6D,0x6B9E}, +{0x9F6E,0x6BA4},{0x9F6F,0x6BAA},{0x9F70,0x6BAB},{0x9F71,0x6BAF}, +{0x9F72,0x6BB2},{0x9F73,0x6BB1},{0x9F74,0x6BB3},{0x9F75,0x6BB7}, +{0x9F76,0x6BBC},{0x9F77,0x6BC6},{0x9F78,0x6BCB},{0x9F79,0x6BD3}, +{0x9F7A,0x6BDF},{0x9F7B,0x6BEC},{0x9F7C,0x6BEB},{0x9F7D,0x6BF3}, +{0x9F7E,0x6BEF},{0x9F80,0x9EBE},{0x9F81,0x6C08},{0x9F82,0x6C13}, +{0x9F83,0x6C14},{0x9F84,0x6C1B},{0x9F85,0x6C24},{0x9F86,0x6C23}, +{0x9F87,0x6C5E},{0x9F88,0x6C55},{0x9F89,0x6C62},{0x9F8A,0x6C6A}, +{0x9F8B,0x6C82},{0x9F8C,0x6C8D},{0x9F8D,0x6C9A},{0x9F8E,0x6C81}, +{0x9F8F,0x6C9B},{0x9F90,0x6C7E},{0x9F91,0x6C68},{0x9F92,0x6C73}, +{0x9F93,0x6C92},{0x9F94,0x6C90},{0x9F95,0x6CC4},{0x9F96,0x6CF1}, +{0x9F97,0x6CD3},{0x9F98,0x6CBD},{0x9F99,0x6CD7},{0x9F9A,0x6CC5}, +{0x9F9B,0x6CDD},{0x9F9C,0x6CAE},{0x9F9D,0x6CB1},{0x9F9E,0x6CBE}, +{0x9F9F,0x6CBA},{0x9FA0,0x6CDB},{0x9FA1,0x6CEF},{0x9FA2,0x6CD9}, +{0x9FA3,0x6CEA},{0x9FA4,0x6D1F},{0x9FA5,0x884D},{0x9FA6,0x6D36}, +{0x9FA7,0x6D2B},{0x9FA8,0x6D3D},{0x9FA9,0x6D38},{0x9FAA,0x6D19}, +{0x9FAB,0x6D35},{0x9FAC,0x6D33},{0x9FAD,0x6D12},{0x9FAE,0x6D0C}, +{0x9FAF,0x6D63},{0x9FB0,0x6D93},{0x9FB1,0x6D64},{0x9FB2,0x6D5A}, +{0x9FB3,0x6D79},{0x9FB4,0x6D59},{0x9FB5,0x6D8E},{0x9FB6,0x6D95}, +{0x9FB7,0x6FE4},{0x9FB8,0x6D85},{0x9FB9,0x6DF9},{0x9FBA,0x6E15}, +{0x9FBB,0x6E0A},{0x9FBC,0x6DB5},{0x9FBD,0x6DC7},{0x9FBE,0x6DE6}, +{0x9FBF,0x6DB8},{0x9FC0,0x6DC6},{0x9FC1,0x6DEC},{0x9FC2,0x6DDE}, +{0x9FC3,0x6DCC},{0x9FC4,0x6DE8},{0x9FC5,0x6DD2},{0x9FC6,0x6DC5}, +{0x9FC7,0x6DFA},{0x9FC8,0x6DD9},{0x9FC9,0x6DE4},{0x9FCA,0x6DD5}, +{0x9FCB,0x6DEA},{0x9FCC,0x6DEE},{0x9FCD,0x6E2D},{0x9FCE,0x6E6E}, +{0x9FCF,0x6E2E},{0x9FD0,0x6E19},{0x9FD1,0x6E72},{0x9FD2,0x6E5F}, +{0x9FD3,0x6E3E},{0x9FD4,0x6E23},{0x9FD5,0x6E6B},{0x9FD6,0x6E2B}, +{0x9FD7,0x6E76},{0x9FD8,0x6E4D},{0x9FD9,0x6E1F},{0x9FDA,0x6E43}, +{0x9FDB,0x6E3A},{0x9FDC,0x6E4E},{0x9FDD,0x6E24},{0x9FDE,0x6EFF}, +{0x9FDF,0x6E1D},{0x9FE0,0x6E38},{0x9FE1,0x6E82},{0x9FE2,0x6EAA}, +{0x9FE3,0x6E98},{0x9FE4,0x6EC9},{0x9FE5,0x6EB7},{0x9FE6,0x6ED3}, +{0x9FE7,0x6EBD},{0x9FE8,0x6EAF},{0x9FE9,0x6EC4},{0x9FEA,0x6EB2}, +{0x9FEB,0x6ED4},{0x9FEC,0x6ED5},{0x9FED,0x6E8F},{0x9FEE,0x6EA5}, +{0x9FEF,0x6EC2},{0x9FF0,0x6E9F},{0x9FF1,0x6F41},{0x9FF2,0x6F11}, +{0x9FF3,0x704C},{0x9FF4,0x6EEC},{0x9FF5,0x6EF8},{0x9FF6,0x6EFE}, +{0x9FF7,0x6F3F},{0x9FF8,0x6EF2},{0x9FF9,0x6F31},{0x9FFA,0x6EEF}, +{0x9FFB,0x6F32},{0x9FFC,0x6ECC},{0xE040,0x6F3E},{0xE041,0x6F13}, +{0xE042,0x6EF7},{0xE043,0x6F86},{0xE044,0x6F7A},{0xE045,0x6F78}, +{0xE046,0x6F81},{0xE047,0x6F80},{0xE048,0x6F6F},{0xE049,0x6F5B}, +{0xE04A,0x6FF3},{0xE04B,0x6F6D},{0xE04C,0x6F82},{0xE04D,0x6F7C}, +{0xE04E,0x6F58},{0xE04F,0x6F8E},{0xE050,0x6F91},{0xE051,0x6FC2}, +{0xE052,0x6F66},{0xE053,0x6FB3},{0xE054,0x6FA3},{0xE055,0x6FA1}, +{0xE056,0x6FA4},{0xE057,0x6FB9},{0xE058,0x6FC6},{0xE059,0x6FAA}, +{0xE05A,0x6FDF},{0xE05B,0x6FD5},{0xE05C,0x6FEC},{0xE05D,0x6FD4}, +{0xE05E,0x6FD8},{0xE05F,0x6FF1},{0xE060,0x6FEE},{0xE061,0x6FDB}, +{0xE062,0x7009},{0xE063,0x700B},{0xE064,0x6FFA},{0xE065,0x7011}, +{0xE066,0x7001},{0xE067,0x700F},{0xE068,0x6FFE},{0xE069,0x701B}, +{0xE06A,0x701A},{0xE06B,0x6F74},{0xE06C,0x701D},{0xE06D,0x7018}, +{0xE06E,0x701F},{0xE06F,0x7030},{0xE070,0x703E},{0xE071,0x7032}, +{0xE072,0x7051},{0xE073,0x7063},{0xE074,0x7099},{0xE075,0x7092}, +{0xE076,0x70AF},{0xE077,0x70F1},{0xE078,0x70AC},{0xE079,0x70B8}, +{0xE07A,0x70B3},{0xE07B,0x70AE},{0xE07C,0x70DF},{0xE07D,0x70CB}, +{0xE07E,0x70DD},{0xE080,0x70D9},{0xE081,0x7109},{0xE082,0x70FD}, +{0xE083,0x711C},{0xE084,0x7119},{0xE085,0x7165},{0xE086,0x7155}, +{0xE087,0x7188},{0xE088,0x7166},{0xE089,0x7162},{0xE08A,0x714C}, +{0xE08B,0x7156},{0xE08C,0x716C},{0xE08D,0x718F},{0xE08E,0x71FB}, +{0xE08F,0x7184},{0xE090,0x7195},{0xE091,0x71A8},{0xE092,0x71AC}, +{0xE093,0x71D7},{0xE094,0x71B9},{0xE095,0x71BE},{0xE096,0x71D2}, +{0xE097,0x71C9},{0xE098,0x71D4},{0xE099,0x71CE},{0xE09A,0x71E0}, +{0xE09B,0x71EC},{0xE09C,0x71E7},{0xE09D,0x71F5},{0xE09E,0x71FC}, +{0xE09F,0x71F9},{0xE0A0,0x71FF},{0xE0A1,0x720D},{0xE0A2,0x7210}, +{0xE0A3,0x721B},{0xE0A4,0x7228},{0xE0A5,0x722D},{0xE0A6,0x722C}, +{0xE0A7,0x7230},{0xE0A8,0x7232},{0xE0A9,0x723B},{0xE0AA,0x723C}, +{0xE0AB,0x723F},{0xE0AC,0x7240},{0xE0AD,0x7246},{0xE0AE,0x724B}, +{0xE0AF,0x7258},{0xE0B0,0x7274},{0xE0B1,0x727E},{0xE0B2,0x7282}, +{0xE0B3,0x7281},{0xE0B4,0x7287},{0xE0B5,0x7292},{0xE0B6,0x7296}, +{0xE0B7,0x72A2},{0xE0B8,0x72A7},{0xE0B9,0x72B9},{0xE0BA,0x72B2}, +{0xE0BB,0x72C3},{0xE0BC,0x72C6},{0xE0BD,0x72C4},{0xE0BE,0x72CE}, +{0xE0BF,0x72D2},{0xE0C0,0x72E2},{0xE0C1,0x72E0},{0xE0C2,0x72E1}, +{0xE0C3,0x72F9},{0xE0C4,0x72F7},{0xE0C5,0x500F},{0xE0C6,0x7317}, +{0xE0C7,0x730A},{0xE0C8,0x731C},{0xE0C9,0x7316},{0xE0CA,0x731D}, +{0xE0CB,0x7334},{0xE0CC,0x732F},{0xE0CD,0x7329},{0xE0CE,0x7325}, +{0xE0CF,0x733E},{0xE0D0,0x734E},{0xE0D1,0x734F},{0xE0D2,0x9ED8}, +{0xE0D3,0x7357},{0xE0D4,0x736A},{0xE0D5,0x7368},{0xE0D6,0x7370}, +{0xE0D7,0x7378},{0xE0D8,0x7375},{0xE0D9,0x737B},{0xE0DA,0x737A}, +{0xE0DB,0x73C8},{0xE0DC,0x73B3},{0xE0DD,0x73CE},{0xE0DE,0x73BB}, +{0xE0DF,0x73C0},{0xE0E0,0x73E5},{0xE0E1,0x73EE},{0xE0E2,0x73DE}, +{0xE0E3,0x74A2},{0xE0E4,0x7405},{0xE0E5,0x746F},{0xE0E6,0x7425}, +{0xE0E7,0x73F8},{0xE0E8,0x7432},{0xE0E9,0x743A},{0xE0EA,0x7455}, +{0xE0EB,0x743F},{0xE0EC,0x745F},{0xE0ED,0x7459},{0xE0EE,0x7441}, +{0xE0EF,0x745C},{0xE0F0,0x7469},{0xE0F1,0x7470},{0xE0F2,0x7463}, +{0xE0F3,0x746A},{0xE0F4,0x7476},{0xE0F5,0x747E},{0xE0F6,0x748B}, +{0xE0F7,0x749E},{0xE0F8,0x74A7},{0xE0F9,0x74CA},{0xE0FA,0x74CF}, +{0xE0FB,0x74D4},{0xE0FC,0x73F1},{0xE140,0x74E0},{0xE141,0x74E3}, +{0xE142,0x74E7},{0xE143,0x74E9},{0xE144,0x74EE},{0xE145,0x74F2}, +{0xE146,0x74F0},{0xE147,0x74F1},{0xE148,0x74F8},{0xE149,0x74F7}, +{0xE14A,0x7504},{0xE14B,0x7503},{0xE14C,0x7505},{0xE14D,0x750C}, +{0xE14E,0x750E},{0xE14F,0x750D},{0xE150,0x7515},{0xE151,0x7513}, +{0xE152,0x751E},{0xE153,0x7526},{0xE154,0x752C},{0xE155,0x753C}, +{0xE156,0x7544},{0xE157,0x754D},{0xE158,0x754A},{0xE159,0x7549}, +{0xE15A,0x755B},{0xE15B,0x7546},{0xE15C,0x755A},{0xE15D,0x7569}, +{0xE15E,0x7564},{0xE15F,0x7567},{0xE160,0x756B},{0xE161,0x756D}, +{0xE162,0x7578},{0xE163,0x7576},{0xE164,0x7586},{0xE165,0x7587}, +{0xE166,0x7574},{0xE167,0x758A},{0xE168,0x7589},{0xE169,0x7582}, +{0xE16A,0x7594},{0xE16B,0x759A},{0xE16C,0x759D},{0xE16D,0x75A5}, +{0xE16E,0x75A3},{0xE16F,0x75C2},{0xE170,0x75B3},{0xE171,0x75C3}, +{0xE172,0x75B5},{0xE173,0x75BD},{0xE174,0x75B8},{0xE175,0x75BC}, +{0xE176,0x75B1},{0xE177,0x75CD},{0xE178,0x75CA},{0xE179,0x75D2}, +{0xE17A,0x75D9},{0xE17B,0x75E3},{0xE17C,0x75DE},{0xE17D,0x75FE}, +{0xE17E,0x75FF},{0xE180,0x75FC},{0xE181,0x7601},{0xE182,0x75F0}, +{0xE183,0x75FA},{0xE184,0x75F2},{0xE185,0x75F3},{0xE186,0x760B}, +{0xE187,0x760D},{0xE188,0x7609},{0xE189,0x761F},{0xE18A,0x7627}, +{0xE18B,0x7620},{0xE18C,0x7621},{0xE18D,0x7622},{0xE18E,0x7624}, +{0xE18F,0x7634},{0xE190,0x7630},{0xE191,0x763B},{0xE192,0x7647}, +{0xE193,0x7648},{0xE194,0x7646},{0xE195,0x765C},{0xE196,0x7658}, +{0xE197,0x7661},{0xE198,0x7662},{0xE199,0x7668},{0xE19A,0x7669}, +{0xE19B,0x766A},{0xE19C,0x7667},{0xE19D,0x766C},{0xE19E,0x7670}, +{0xE19F,0x7672},{0xE1A0,0x7676},{0xE1A1,0x7678},{0xE1A2,0x767C}, +{0xE1A3,0x7680},{0xE1A4,0x7683},{0xE1A5,0x7688},{0xE1A6,0x768B}, +{0xE1A7,0x768E},{0xE1A8,0x7696},{0xE1A9,0x7693},{0xE1AA,0x7699}, +{0xE1AB,0x769A},{0xE1AC,0x76B0},{0xE1AD,0x76B4},{0xE1AE,0x76B8}, +{0xE1AF,0x76B9},{0xE1B0,0x76BA},{0xE1B1,0x76C2},{0xE1B2,0x76CD}, +{0xE1B3,0x76D6},{0xE1B4,0x76D2},{0xE1B5,0x76DE},{0xE1B6,0x76E1}, +{0xE1B7,0x76E5},{0xE1B8,0x76E7},{0xE1B9,0x76EA},{0xE1BA,0x862F}, +{0xE1BB,0x76FB},{0xE1BC,0x7708},{0xE1BD,0x7707},{0xE1BE,0x7704}, +{0xE1BF,0x7729},{0xE1C0,0x7724},{0xE1C1,0x771E},{0xE1C2,0x7725}, +{0xE1C3,0x7726},{0xE1C4,0x771B},{0xE1C5,0x7737},{0xE1C6,0x7738}, +{0xE1C7,0x7747},{0xE1C8,0x775A},{0xE1C9,0x7768},{0xE1CA,0x776B}, +{0xE1CB,0x775B},{0xE1CC,0x7765},{0xE1CD,0x777F},{0xE1CE,0x777E}, +{0xE1CF,0x7779},{0xE1D0,0x778E},{0xE1D1,0x778B},{0xE1D2,0x7791}, +{0xE1D3,0x77A0},{0xE1D4,0x779E},{0xE1D5,0x77B0},{0xE1D6,0x77B6}, +{0xE1D7,0x77B9},{0xE1D8,0x77BF},{0xE1D9,0x77BC},{0xE1DA,0x77BD}, +{0xE1DB,0x77BB},{0xE1DC,0x77C7},{0xE1DD,0x77CD},{0xE1DE,0x77D7}, +{0xE1DF,0x77DA},{0xE1E0,0x77DC},{0xE1E1,0x77E3},{0xE1E2,0x77EE}, +{0xE1E3,0x77FC},{0xE1E4,0x780C},{0xE1E5,0x7812},{0xE1E6,0x7926}, +{0xE1E7,0x7820},{0xE1E8,0x792A},{0xE1E9,0x7845},{0xE1EA,0x788E}, +{0xE1EB,0x7874},{0xE1EC,0x7886},{0xE1ED,0x787C},{0xE1EE,0x789A}, +{0xE1EF,0x788C},{0xE1F0,0x78A3},{0xE1F1,0x78B5},{0xE1F2,0x78AA}, +{0xE1F3,0x78AF},{0xE1F4,0x78D1},{0xE1F5,0x78C6},{0xE1F6,0x78CB}, +{0xE1F7,0x78D4},{0xE1F8,0x78BE},{0xE1F9,0x78BC},{0xE1FA,0x78C5}, +{0xE1FB,0x78CA},{0xE1FC,0x78EC},{0xE240,0x78E7},{0xE241,0x78DA}, +{0xE242,0x78FD},{0xE243,0x78F4},{0xE244,0x7907},{0xE245,0x7912}, +{0xE246,0x7911},{0xE247,0x7919},{0xE248,0x792C},{0xE249,0x792B}, +{0xE24A,0x7940},{0xE24B,0x7960},{0xE24C,0x7957},{0xE24D,0x795F}, +{0xE24E,0x795A},{0xE24F,0x7955},{0xE250,0x7953},{0xE251,0x797A}, +{0xE252,0x797F},{0xE253,0x798A},{0xE254,0x799D},{0xE255,0x79A7}, +{0xE256,0x9F4B},{0xE257,0x79AA},{0xE258,0x79AE},{0xE259,0x79B3}, +{0xE25A,0x79B9},{0xE25B,0x79BA},{0xE25C,0x79C9},{0xE25D,0x79D5}, +{0xE25E,0x79E7},{0xE25F,0x79EC},{0xE260,0x79E1},{0xE261,0x79E3}, +{0xE262,0x7A08},{0xE263,0x7A0D},{0xE264,0x7A18},{0xE265,0x7A19}, +{0xE266,0x7A20},{0xE267,0x7A1F},{0xE268,0x7980},{0xE269,0x7A31}, +{0xE26A,0x7A3B},{0xE26B,0x7A3E},{0xE26C,0x7A37},{0xE26D,0x7A43}, +{0xE26E,0x7A57},{0xE26F,0x7A49},{0xE270,0x7A61},{0xE271,0x7A62}, +{0xE272,0x7A69},{0xE273,0x9F9D},{0xE274,0x7A70},{0xE275,0x7A79}, +{0xE276,0x7A7D},{0xE277,0x7A88},{0xE278,0x7A97},{0xE279,0x7A95}, +{0xE27A,0x7A98},{0xE27B,0x7A96},{0xE27C,0x7AA9},{0xE27D,0x7AC8}, +{0xE27E,0x7AB0},{0xE280,0x7AB6},{0xE281,0x7AC5},{0xE282,0x7AC4}, +{0xE283,0x7ABF},{0xE284,0x9083},{0xE285,0x7AC7},{0xE286,0x7ACA}, +{0xE287,0x7ACD},{0xE288,0x7ACF},{0xE289,0x7AD5},{0xE28A,0x7AD3}, +{0xE28B,0x7AD9},{0xE28C,0x7ADA},{0xE28D,0x7ADD},{0xE28E,0x7AE1}, +{0xE28F,0x7AE2},{0xE290,0x7AE6},{0xE291,0x7AED},{0xE292,0x7AF0}, +{0xE293,0x7B02},{0xE294,0x7B0F},{0xE295,0x7B0A},{0xE296,0x7B06}, +{0xE297,0x7B33},{0xE298,0x7B18},{0xE299,0x7B19},{0xE29A,0x7B1E}, +{0xE29B,0x7B35},{0xE29C,0x7B28},{0xE29D,0x7B36},{0xE29E,0x7B50}, +{0xE29F,0x7B7A},{0xE2A0,0x7B04},{0xE2A1,0x7B4D},{0xE2A2,0x7B0B}, +{0xE2A3,0x7B4C},{0xE2A4,0x7B45},{0xE2A5,0x7B75},{0xE2A6,0x7B65}, +{0xE2A7,0x7B74},{0xE2A8,0x7B67},{0xE2A9,0x7B70},{0xE2AA,0x7B71}, +{0xE2AB,0x7B6C},{0xE2AC,0x7B6E},{0xE2AD,0x7B9D},{0xE2AE,0x7B98}, +{0xE2AF,0x7B9F},{0xE2B0,0x7B8D},{0xE2B1,0x7B9C},{0xE2B2,0x7B9A}, +{0xE2B3,0x7B8B},{0xE2B4,0x7B92},{0xE2B5,0x7B8F},{0xE2B6,0x7B5D}, +{0xE2B7,0x7B99},{0xE2B8,0x7BCB},{0xE2B9,0x7BC1},{0xE2BA,0x7BCC}, +{0xE2BB,0x7BCF},{0xE2BC,0x7BB4},{0xE2BD,0x7BC6},{0xE2BE,0x7BDD}, +{0xE2BF,0x7BE9},{0xE2C0,0x7C11},{0xE2C1,0x7C14},{0xE2C2,0x7BE6}, +{0xE2C3,0x7BE5},{0xE2C4,0x7C60},{0xE2C5,0x7C00},{0xE2C6,0x7C07}, +{0xE2C7,0x7C13},{0xE2C8,0x7BF3},{0xE2C9,0x7BF7},{0xE2CA,0x7C17}, +{0xE2CB,0x7C0D},{0xE2CC,0x7BF6},{0xE2CD,0x7C23},{0xE2CE,0x7C27}, +{0xE2CF,0x7C2A},{0xE2D0,0x7C1F},{0xE2D1,0x7C37},{0xE2D2,0x7C2B}, +{0xE2D3,0x7C3D},{0xE2D4,0x7C4C},{0xE2D5,0x7C43},{0xE2D6,0x7C54}, +{0xE2D7,0x7C4F},{0xE2D8,0x7C40},{0xE2D9,0x7C50},{0xE2DA,0x7C58}, +{0xE2DB,0x7C5F},{0xE2DC,0x7C64},{0xE2DD,0x7C56},{0xE2DE,0x7C65}, +{0xE2DF,0x7C6C},{0xE2E0,0x7C75},{0xE2E1,0x7C83},{0xE2E2,0x7C90}, +{0xE2E3,0x7CA4},{0xE2E4,0x7CAD},{0xE2E5,0x7CA2},{0xE2E6,0x7CAB}, +{0xE2E7,0x7CA1},{0xE2E8,0x7CA8},{0xE2E9,0x7CB3},{0xE2EA,0x7CB2}, +{0xE2EB,0x7CB1},{0xE2EC,0x7CAE},{0xE2ED,0x7CB9},{0xE2EE,0x7CBD}, +{0xE2EF,0x7CC0},{0xE2F0,0x7CC5},{0xE2F1,0x7CC2},{0xE2F2,0x7CD8}, +{0xE2F3,0x7CD2},{0xE2F4,0x7CDC},{0xE2F5,0x7CE2},{0xE2F6,0x9B3B}, +{0xE2F7,0x7CEF},{0xE2F8,0x7CF2},{0xE2F9,0x7CF4},{0xE2FA,0x7CF6}, +{0xE2FB,0x7CFA},{0xE2FC,0x7D06},{0xE340,0x7D02},{0xE341,0x7D1C}, +{0xE342,0x7D15},{0xE343,0x7D0A},{0xE344,0x7D45},{0xE345,0x7D4B}, +{0xE346,0x7D2E},{0xE347,0x7D32},{0xE348,0x7D3F},{0xE349,0x7D35}, +{0xE34A,0x7D46},{0xE34B,0x7D73},{0xE34C,0x7D56},{0xE34D,0x7D4E}, +{0xE34E,0x7D72},{0xE34F,0x7D68},{0xE350,0x7D6E},{0xE351,0x7D4F}, +{0xE352,0x7D63},{0xE353,0x7D93},{0xE354,0x7D89},{0xE355,0x7D5B}, +{0xE356,0x7D8F},{0xE357,0x7D7D},{0xE358,0x7D9B},{0xE359,0x7DBA}, +{0xE35A,0x7DAE},{0xE35B,0x7DA3},{0xE35C,0x7DB5},{0xE35D,0x7DC7}, +{0xE35E,0x7DBD},{0xE35F,0x7DAB},{0xE360,0x7E3D},{0xE361,0x7DA2}, +{0xE362,0x7DAF},{0xE363,0x7DDC},{0xE364,0x7DB8},{0xE365,0x7D9F}, +{0xE366,0x7DB0},{0xE367,0x7DD8},{0xE368,0x7DDD},{0xE369,0x7DE4}, +{0xE36A,0x7DDE},{0xE36B,0x7DFB},{0xE36C,0x7DF2},{0xE36D,0x7DE1}, +{0xE36E,0x7E05},{0xE36F,0x7E0A},{0xE370,0x7E23},{0xE371,0x7E21}, +{0xE372,0x7E12},{0xE373,0x7E31},{0xE374,0x7E1F},{0xE375,0x7E09}, +{0xE376,0x7E0B},{0xE377,0x7E22},{0xE378,0x7E46},{0xE379,0x7E66}, +{0xE37A,0x7E3B},{0xE37B,0x7E35},{0xE37C,0x7E39},{0xE37D,0x7E43}, +{0xE37E,0x7E37},{0xE380,0x7E32},{0xE381,0x7E3A},{0xE382,0x7E67}, +{0xE383,0x7E5D},{0xE384,0x7E56},{0xE385,0x7E5E},{0xE386,0x7E59}, +{0xE387,0x7E5A},{0xE388,0x7E79},{0xE389,0x7E6A},{0xE38A,0x7E69}, +{0xE38B,0x7E7C},{0xE38C,0x7E7B},{0xE38D,0x7E83},{0xE38E,0x7DD5}, +{0xE38F,0x7E7D},{0xE390,0x8FAE},{0xE391,0x7E7F},{0xE392,0x7E88}, +{0xE393,0x7E89},{0xE394,0x7E8C},{0xE395,0x7E92},{0xE396,0x7E90}, +{0xE397,0x7E93},{0xE398,0x7E94},{0xE399,0x7E96},{0xE39A,0x7E8E}, +{0xE39B,0x7E9B},{0xE39C,0x7E9C},{0xE39D,0x7F38},{0xE39E,0x7F3A}, +{0xE39F,0x7F45},{0xE3A0,0x7F4C},{0xE3A1,0x7F4D},{0xE3A2,0x7F4E}, +{0xE3A3,0x7F50},{0xE3A4,0x7F51},{0xE3A5,0x7F55},{0xE3A6,0x7F54}, +{0xE3A7,0x7F58},{0xE3A8,0x7F5F},{0xE3A9,0x7F60},{0xE3AA,0x7F68}, +{0xE3AB,0x7F69},{0xE3AC,0x7F67},{0xE3AD,0x7F78},{0xE3AE,0x7F82}, +{0xE3AF,0x7F86},{0xE3B0,0x7F83},{0xE3B1,0x7F88},{0xE3B2,0x7F87}, +{0xE3B3,0x7F8C},{0xE3B4,0x7F94},{0xE3B5,0x7F9E},{0xE3B6,0x7F9D}, +{0xE3B7,0x7F9A},{0xE3B8,0x7FA3},{0xE3B9,0x7FAF},{0xE3BA,0x7FB2}, +{0xE3BB,0x7FB9},{0xE3BC,0x7FAE},{0xE3BD,0x7FB6},{0xE3BE,0x7FB8}, +{0xE3BF,0x8B71},{0xE3C0,0x7FC5},{0xE3C1,0x7FC6},{0xE3C2,0x7FCA}, +{0xE3C3,0x7FD5},{0xE3C4,0x7FD4},{0xE3C5,0x7FE1},{0xE3C6,0x7FE6}, +{0xE3C7,0x7FE9},{0xE3C8,0x7FF3},{0xE3C9,0x7FF9},{0xE3CA,0x98DC}, +{0xE3CB,0x8006},{0xE3CC,0x8004},{0xE3CD,0x800B},{0xE3CE,0x8012}, +{0xE3CF,0x8018},{0xE3D0,0x8019},{0xE3D1,0x801C},{0xE3D2,0x8021}, +{0xE3D3,0x8028},{0xE3D4,0x803F},{0xE3D5,0x803B},{0xE3D6,0x804A}, +{0xE3D7,0x8046},{0xE3D8,0x8052},{0xE3D9,0x8058},{0xE3DA,0x805A}, +{0xE3DB,0x805F},{0xE3DC,0x8062},{0xE3DD,0x8068},{0xE3DE,0x8073}, +{0xE3DF,0x8072},{0xE3E0,0x8070},{0xE3E1,0x8076},{0xE3E2,0x8079}, +{0xE3E3,0x807D},{0xE3E4,0x807F},{0xE3E5,0x8084},{0xE3E6,0x8086}, +{0xE3E7,0x8085},{0xE3E8,0x809B},{0xE3E9,0x8093},{0xE3EA,0x809A}, +{0xE3EB,0x80AD},{0xE3EC,0x5190},{0xE3ED,0x80AC},{0xE3EE,0x80DB}, +{0xE3EF,0x80E5},{0xE3F0,0x80D9},{0xE3F1,0x80DD},{0xE3F2,0x80C4}, +{0xE3F3,0x80DA},{0xE3F4,0x80D6},{0xE3F5,0x8109},{0xE3F6,0x80EF}, +{0xE3F7,0x80F1},{0xE3F8,0x811B},{0xE3F9,0x8129},{0xE3FA,0x8123}, +{0xE3FB,0x812F},{0xE3FC,0x814B},{0xE440,0x968B},{0xE441,0x8146}, +{0xE442,0x813E},{0xE443,0x8153},{0xE444,0x8151},{0xE445,0x80FC}, +{0xE446,0x8171},{0xE447,0x816E},{0xE448,0x8165},{0xE449,0x8166}, +{0xE44A,0x8174},{0xE44B,0x8183},{0xE44C,0x8188},{0xE44D,0x818A}, +{0xE44E,0x8180},{0xE44F,0x8182},{0xE450,0x81A0},{0xE451,0x8195}, +{0xE452,0x81A4},{0xE453,0x81A3},{0xE454,0x815F},{0xE455,0x8193}, +{0xE456,0x81A9},{0xE457,0x81B0},{0xE458,0x81B5},{0xE459,0x81BE}, +{0xE45A,0x81B8},{0xE45B,0x81BD},{0xE45C,0x81C0},{0xE45D,0x81C2}, +{0xE45E,0x81BA},{0xE45F,0x81C9},{0xE460,0x81CD},{0xE461,0x81D1}, +{0xE462,0x81D9},{0xE463,0x81D8},{0xE464,0x81C8},{0xE465,0x81DA}, +{0xE466,0x81DF},{0xE467,0x81E0},{0xE468,0x81E7},{0xE469,0x81FA}, +{0xE46A,0x81FB},{0xE46B,0x81FE},{0xE46C,0x8201},{0xE46D,0x8202}, +{0xE46E,0x8205},{0xE46F,0x8207},{0xE470,0x820A},{0xE471,0x820D}, +{0xE472,0x8210},{0xE473,0x8216},{0xE474,0x8229},{0xE475,0x822B}, +{0xE476,0x8238},{0xE477,0x8233},{0xE478,0x8240},{0xE479,0x8259}, +{0xE47A,0x8258},{0xE47B,0x825D},{0xE47C,0x825A},{0xE47D,0x825F}, +{0xE47E,0x8264},{0xE480,0x8262},{0xE481,0x8268},{0xE482,0x826A}, +{0xE483,0x826B},{0xE484,0x822E},{0xE485,0x8271},{0xE486,0x8277}, +{0xE487,0x8278},{0xE488,0x827E},{0xE489,0x828D},{0xE48A,0x8292}, +{0xE48B,0x82AB},{0xE48C,0x829F},{0xE48D,0x82BB},{0xE48E,0x82AC}, +{0xE48F,0x82E1},{0xE490,0x82E3},{0xE491,0x82DF},{0xE492,0x82D2}, +{0xE493,0x82F4},{0xE494,0x82F3},{0xE495,0x82FA},{0xE496,0x8393}, +{0xE497,0x8303},{0xE498,0x82FB},{0xE499,0x82F9},{0xE49A,0x82DE}, +{0xE49B,0x8306},{0xE49C,0x82DC},{0xE49D,0x8309},{0xE49E,0x82D9}, +{0xE49F,0x8335},{0xE4A0,0x8334},{0xE4A1,0x8316},{0xE4A2,0x8332}, +{0xE4A3,0x8331},{0xE4A4,0x8340},{0xE4A5,0x8339},{0xE4A6,0x8350}, +{0xE4A7,0x8345},{0xE4A8,0x832F},{0xE4A9,0x832B},{0xE4AA,0x8317}, +{0xE4AB,0x8318},{0xE4AC,0x8385},{0xE4AD,0x839A},{0xE4AE,0x83AA}, +{0xE4AF,0x839F},{0xE4B0,0x83A2},{0xE4B1,0x8396},{0xE4B2,0x8323}, +{0xE4B3,0x838E},{0xE4B4,0x8387},{0xE4B5,0x838A},{0xE4B6,0x837C}, +{0xE4B7,0x83B5},{0xE4B8,0x8373},{0xE4B9,0x8375},{0xE4BA,0x83A0}, +{0xE4BB,0x8389},{0xE4BC,0x83A8},{0xE4BD,0x83F4},{0xE4BE,0x8413}, +{0xE4BF,0x83EB},{0xE4C0,0x83CE},{0xE4C1,0x83FD},{0xE4C2,0x8403}, +{0xE4C3,0x83D8},{0xE4C4,0x840B},{0xE4C5,0x83C1},{0xE4C6,0x83F7}, +{0xE4C7,0x8407},{0xE4C8,0x83E0},{0xE4C9,0x83F2},{0xE4CA,0x840D}, +{0xE4CB,0x8422},{0xE4CC,0x8420},{0xE4CD,0x83BD},{0xE4CE,0x8438}, +{0xE4CF,0x8506},{0xE4D0,0x83FB},{0xE4D1,0x846D},{0xE4D2,0x842A}, +{0xE4D3,0x843C},{0xE4D4,0x855A},{0xE4D5,0x8484},{0xE4D6,0x8477}, +{0xE4D7,0x846B},{0xE4D8,0x84AD},{0xE4D9,0x846E},{0xE4DA,0x8482}, +{0xE4DB,0x8469},{0xE4DC,0x8446},{0xE4DD,0x842C},{0xE4DE,0x846F}, +{0xE4DF,0x8479},{0xE4E0,0x8435},{0xE4E1,0x84CA},{0xE4E2,0x8462}, +{0xE4E3,0x84B9},{0xE4E4,0x84BF},{0xE4E5,0x849F},{0xE4E6,0x84D9}, +{0xE4E7,0x84CD},{0xE4E8,0x84BB},{0xE4E9,0x84DA},{0xE4EA,0x84D0}, +{0xE4EB,0x84C1},{0xE4EC,0x84C6},{0xE4ED,0x84D6},{0xE4EE,0x84A1}, +{0xE4EF,0x8521},{0xE4F0,0x84FF},{0xE4F1,0x84F4},{0xE4F2,0x8517}, +{0xE4F3,0x8518},{0xE4F4,0x852C},{0xE4F5,0x851F},{0xE4F6,0x8515}, +{0xE4F7,0x8514},{0xE4F8,0x84FC},{0xE4F9,0x8540},{0xE4FA,0x8563}, +{0xE4FB,0x8558},{0xE4FC,0x8548},{0xE540,0x8541},{0xE541,0x8602}, +{0xE542,0x854B},{0xE543,0x8555},{0xE544,0x8580},{0xE545,0x85A4}, +{0xE546,0x8588},{0xE547,0x8591},{0xE548,0x858A},{0xE549,0x85A8}, +{0xE54A,0x856D},{0xE54B,0x8594},{0xE54C,0x859B},{0xE54D,0x85EA}, +{0xE54E,0x8587},{0xE54F,0x859C},{0xE550,0x8577},{0xE551,0x857E}, +{0xE552,0x8590},{0xE553,0x85C9},{0xE554,0x85BA},{0xE555,0x85CF}, +{0xE556,0x85B9},{0xE557,0x85D0},{0xE558,0x85D5},{0xE559,0x85DD}, +{0xE55A,0x85E5},{0xE55B,0x85DC},{0xE55C,0x85F9},{0xE55D,0x860A}, +{0xE55E,0x8613},{0xE55F,0x860B},{0xE560,0x85FE},{0xE561,0x85FA}, +{0xE562,0x8606},{0xE563,0x8622},{0xE564,0x861A},{0xE565,0x8630}, +{0xE566,0x863F},{0xE567,0x864D},{0xE568,0x4E55},{0xE569,0x8654}, +{0xE56A,0x865F},{0xE56B,0x8667},{0xE56C,0x8671},{0xE56D,0x8693}, +{0xE56E,0x86A3},{0xE56F,0x86A9},{0xE570,0x86AA},{0xE571,0x868B}, +{0xE572,0x868C},{0xE573,0x86B6},{0xE574,0x86AF},{0xE575,0x86C4}, +{0xE576,0x86C6},{0xE577,0x86B0},{0xE578,0x86C9},{0xE579,0x8823}, +{0xE57A,0x86AB},{0xE57B,0x86D4},{0xE57C,0x86DE},{0xE57D,0x86E9}, +{0xE57E,0x86EC},{0xE580,0x86DF},{0xE581,0x86DB},{0xE582,0x86EF}, +{0xE583,0x8712},{0xE584,0x8706},{0xE585,0x8708},{0xE586,0x8700}, +{0xE587,0x8703},{0xE588,0x86FB},{0xE589,0x8711},{0xE58A,0x8709}, +{0xE58B,0x870D},{0xE58C,0x86F9},{0xE58D,0x870A},{0xE58E,0x8734}, +{0xE58F,0x873F},{0xE590,0x8737},{0xE591,0x873B},{0xE592,0x8725}, +{0xE593,0x8729},{0xE594,0x871A},{0xE595,0x8760},{0xE596,0x875F}, +{0xE597,0x8778},{0xE598,0x874C},{0xE599,0x874E},{0xE59A,0x8774}, +{0xE59B,0x8757},{0xE59C,0x8768},{0xE59D,0x876E},{0xE59E,0x8759}, +{0xE59F,0x8753},{0xE5A0,0x8763},{0xE5A1,0x876A},{0xE5A2,0x8805}, +{0xE5A3,0x87A2},{0xE5A4,0x879F},{0xE5A5,0x8782},{0xE5A6,0x87AF}, +{0xE5A7,0x87CB},{0xE5A8,0x87BD},{0xE5A9,0x87C0},{0xE5AA,0x87D0}, +{0xE5AB,0x96D6},{0xE5AC,0x87AB},{0xE5AD,0x87C4},{0xE5AE,0x87B3}, +{0xE5AF,0x87C7},{0xE5B0,0x87C6},{0xE5B1,0x87BB},{0xE5B2,0x87EF}, +{0xE5B3,0x87F2},{0xE5B4,0x87E0},{0xE5B5,0x880F},{0xE5B6,0x880D}, +{0xE5B7,0x87FE},{0xE5B8,0x87F6},{0xE5B9,0x87F7},{0xE5BA,0x880E}, +{0xE5BB,0x87D2},{0xE5BC,0x8811},{0xE5BD,0x8816},{0xE5BE,0x8815}, +{0xE5BF,0x8822},{0xE5C0,0x8821},{0xE5C1,0x8831},{0xE5C2,0x8836}, +{0xE5C3,0x8839},{0xE5C4,0x8827},{0xE5C5,0x883B},{0xE5C6,0x8844}, +{0xE5C7,0x8842},{0xE5C8,0x8852},{0xE5C9,0x8859},{0xE5CA,0x885E}, +{0xE5CB,0x8862},{0xE5CC,0x886B},{0xE5CD,0x8881},{0xE5CE,0x887E}, +{0xE5CF,0x889E},{0xE5D0,0x8875},{0xE5D1,0x887D},{0xE5D2,0x88B5}, +{0xE5D3,0x8872},{0xE5D4,0x8882},{0xE5D5,0x8897},{0xE5D6,0x8892}, +{0xE5D7,0x88AE},{0xE5D8,0x8899},{0xE5D9,0x88A2},{0xE5DA,0x888D}, +{0xE5DB,0x88A4},{0xE5DC,0x88B0},{0xE5DD,0x88BF},{0xE5DE,0x88B1}, +{0xE5DF,0x88C3},{0xE5E0,0x88C4},{0xE5E1,0x88D4},{0xE5E2,0x88D8}, +{0xE5E3,0x88D9},{0xE5E4,0x88DD},{0xE5E5,0x88F9},{0xE5E6,0x8902}, +{0xE5E7,0x88FC},{0xE5E8,0x88F4},{0xE5E9,0x88E8},{0xE5EA,0x88F2}, +{0xE5EB,0x8904},{0xE5EC,0x890C},{0xE5ED,0x890A},{0xE5EE,0x8913}, +{0xE5EF,0x8943},{0xE5F0,0x891E},{0xE5F1,0x8925},{0xE5F2,0x892A}, +{0xE5F3,0x892B},{0xE5F4,0x8941},{0xE5F5,0x8944},{0xE5F6,0x893B}, +{0xE5F7,0x8936},{0xE5F8,0x8938},{0xE5F9,0x894C},{0xE5FA,0x891D}, +{0xE5FB,0x8960},{0xE5FC,0x895E},{0xE640,0x8966},{0xE641,0x8964}, +{0xE642,0x896D},{0xE643,0x896A},{0xE644,0x896F},{0xE645,0x8974}, +{0xE646,0x8977},{0xE647,0x897E},{0xE648,0x8983},{0xE649,0x8988}, +{0xE64A,0x898A},{0xE64B,0x8993},{0xE64C,0x8998},{0xE64D,0x89A1}, +{0xE64E,0x89A9},{0xE64F,0x89A6},{0xE650,0x89AC},{0xE651,0x89AF}, +{0xE652,0x89B2},{0xE653,0x89BA},{0xE654,0x89BD},{0xE655,0x89BF}, +{0xE656,0x89C0},{0xE657,0x89DA},{0xE658,0x89DC},{0xE659,0x89DD}, +{0xE65A,0x89E7},{0xE65B,0x89F4},{0xE65C,0x89F8},{0xE65D,0x8A03}, +{0xE65E,0x8A16},{0xE65F,0x8A10},{0xE660,0x8A0C},{0xE661,0x8A1B}, +{0xE662,0x8A1D},{0xE663,0x8A25},{0xE664,0x8A36},{0xE665,0x8A41}, +{0xE666,0x8A5B},{0xE667,0x8A52},{0xE668,0x8A46},{0xE669,0x8A48}, +{0xE66A,0x8A7C},{0xE66B,0x8A6D},{0xE66C,0x8A6C},{0xE66D,0x8A62}, +{0xE66E,0x8A85},{0xE66F,0x8A82},{0xE670,0x8A84},{0xE671,0x8AA8}, +{0xE672,0x8AA1},{0xE673,0x8A91},{0xE674,0x8AA5},{0xE675,0x8AA6}, +{0xE676,0x8A9A},{0xE677,0x8AA3},{0xE678,0x8AC4},{0xE679,0x8ACD}, +{0xE67A,0x8AC2},{0xE67B,0x8ADA},{0xE67C,0x8AEB},{0xE67D,0x8AF3}, +{0xE67E,0x8AE7},{0xE680,0x8AE4},{0xE681,0x8AF1},{0xE682,0x8B14}, +{0xE683,0x8AE0},{0xE684,0x8AE2},{0xE685,0x8AF7},{0xE686,0x8ADE}, +{0xE687,0x8ADB},{0xE688,0x8B0C},{0xE689,0x8B07},{0xE68A,0x8B1A}, +{0xE68B,0x8AE1},{0xE68C,0x8B16},{0xE68D,0x8B10},{0xE68E,0x8B17}, +{0xE68F,0x8B20},{0xE690,0x8B33},{0xE691,0x97AB},{0xE692,0x8B26}, +{0xE693,0x8B2B},{0xE694,0x8B3E},{0xE695,0x8B28},{0xE696,0x8B41}, +{0xE697,0x8B4C},{0xE698,0x8B4F},{0xE699,0x8B4E},{0xE69A,0x8B49}, +{0xE69B,0x8B56},{0xE69C,0x8B5B},{0xE69D,0x8B5A},{0xE69E,0x8B6B}, +{0xE69F,0x8B5F},{0xE6A0,0x8B6C},{0xE6A1,0x8B6F},{0xE6A2,0x8B74}, +{0xE6A3,0x8B7D},{0xE6A4,0x8B80},{0xE6A5,0x8B8C},{0xE6A6,0x8B8E}, +{0xE6A7,0x8B92},{0xE6A8,0x8B93},{0xE6A9,0x8B96},{0xE6AA,0x8B99}, +{0xE6AB,0x8B9A},{0xE6AC,0x8C3A},{0xE6AD,0x8C41},{0xE6AE,0x8C3F}, +{0xE6AF,0x8C48},{0xE6B0,0x8C4C},{0xE6B1,0x8C4E},{0xE6B2,0x8C50}, +{0xE6B3,0x8C55},{0xE6B4,0x8C62},{0xE6B5,0x8C6C},{0xE6B6,0x8C78}, +{0xE6B7,0x8C7A},{0xE6B8,0x8C82},{0xE6B9,0x8C89},{0xE6BA,0x8C85}, +{0xE6BB,0x8C8A},{0xE6BC,0x8C8D},{0xE6BD,0x8C8E},{0xE6BE,0x8C94}, +{0xE6BF,0x8C7C},{0xE6C0,0x8C98},{0xE6C1,0x621D},{0xE6C2,0x8CAD}, +{0xE6C3,0x8CAA},{0xE6C4,0x8CBD},{0xE6C5,0x8CB2},{0xE6C6,0x8CB3}, +{0xE6C7,0x8CAE},{0xE6C8,0x8CB6},{0xE6C9,0x8CC8},{0xE6CA,0x8CC1}, +{0xE6CB,0x8CE4},{0xE6CC,0x8CE3},{0xE6CD,0x8CDA},{0xE6CE,0x8CFD}, +{0xE6CF,0x8CFA},{0xE6D0,0x8CFB},{0xE6D1,0x8D04},{0xE6D2,0x8D05}, +{0xE6D3,0x8D0A},{0xE6D4,0x8D07},{0xE6D5,0x8D0F},{0xE6D6,0x8D0D}, +{0xE6D7,0x8D10},{0xE6D8,0x9F4E},{0xE6D9,0x8D13},{0xE6DA,0x8CCD}, +{0xE6DB,0x8D14},{0xE6DC,0x8D16},{0xE6DD,0x8D67},{0xE6DE,0x8D6D}, +{0xE6DF,0x8D71},{0xE6E0,0x8D73},{0xE6E1,0x8D81},{0xE6E2,0x8D99}, +{0xE6E3,0x8DC2},{0xE6E4,0x8DBE},{0xE6E5,0x8DBA},{0xE6E6,0x8DCF}, +{0xE6E7,0x8DDA},{0xE6E8,0x8DD6},{0xE6E9,0x8DCC},{0xE6EA,0x8DDB}, +{0xE6EB,0x8DCB},{0xE6EC,0x8DEA},{0xE6ED,0x8DEB},{0xE6EE,0x8DDF}, +{0xE6EF,0x8DE3},{0xE6F0,0x8DFC},{0xE6F1,0x8E08},{0xE6F2,0x8E09}, +{0xE6F3,0x8DFF},{0xE6F4,0x8E1D},{0xE6F5,0x8E1E},{0xE6F6,0x8E10}, +{0xE6F7,0x8E1F},{0xE6F8,0x8E42},{0xE6F9,0x8E35},{0xE6FA,0x8E30}, +{0xE6FB,0x8E34},{0xE6FC,0x8E4A},{0xE740,0x8E47},{0xE741,0x8E49}, +{0xE742,0x8E4C},{0xE743,0x8E50},{0xE744,0x8E48},{0xE745,0x8E59}, +{0xE746,0x8E64},{0xE747,0x8E60},{0xE748,0x8E2A},{0xE749,0x8E63}, +{0xE74A,0x8E55},{0xE74B,0x8E76},{0xE74C,0x8E72},{0xE74D,0x8E7C}, +{0xE74E,0x8E81},{0xE74F,0x8E87},{0xE750,0x8E85},{0xE751,0x8E84}, +{0xE752,0x8E8B},{0xE753,0x8E8A},{0xE754,0x8E93},{0xE755,0x8E91}, +{0xE756,0x8E94},{0xE757,0x8E99},{0xE758,0x8EAA},{0xE759,0x8EA1}, +{0xE75A,0x8EAC},{0xE75B,0x8EB0},{0xE75C,0x8EC6},{0xE75D,0x8EB1}, +{0xE75E,0x8EBE},{0xE75F,0x8EC5},{0xE760,0x8EC8},{0xE761,0x8ECB}, +{0xE762,0x8EDB},{0xE763,0x8EE3},{0xE764,0x8EFC},{0xE765,0x8EFB}, +{0xE766,0x8EEB},{0xE767,0x8EFE},{0xE768,0x8F0A},{0xE769,0x8F05}, +{0xE76A,0x8F15},{0xE76B,0x8F12},{0xE76C,0x8F19},{0xE76D,0x8F13}, +{0xE76E,0x8F1C},{0xE76F,0x8F1F},{0xE770,0x8F1B},{0xE771,0x8F0C}, +{0xE772,0x8F26},{0xE773,0x8F33},{0xE774,0x8F3B},{0xE775,0x8F39}, +{0xE776,0x8F45},{0xE777,0x8F42},{0xE778,0x8F3E},{0xE779,0x8F4C}, +{0xE77A,0x8F49},{0xE77B,0x8F46},{0xE77C,0x8F4E},{0xE77D,0x8F57}, +{0xE77E,0x8F5C},{0xE780,0x8F62},{0xE781,0x8F63},{0xE782,0x8F64}, +{0xE783,0x8F9C},{0xE784,0x8F9F},{0xE785,0x8FA3},{0xE786,0x8FAD}, +{0xE787,0x8FAF},{0xE788,0x8FB7},{0xE789,0x8FDA},{0xE78A,0x8FE5}, +{0xE78B,0x8FE2},{0xE78C,0x8FEA},{0xE78D,0x8FEF},{0xE78E,0x9087}, +{0xE78F,0x8FF4},{0xE790,0x9005},{0xE791,0x8FF9},{0xE792,0x8FFA}, +{0xE793,0x9011},{0xE794,0x9015},{0xE795,0x9021},{0xE796,0x900D}, +{0xE797,0x901E},{0xE798,0x9016},{0xE799,0x900B},{0xE79A,0x9027}, +{0xE79B,0x9036},{0xE79C,0x9035},{0xE79D,0x9039},{0xE79E,0x8FF8}, +{0xE79F,0x904F},{0xE7A0,0x9050},{0xE7A1,0x9051},{0xE7A2,0x9052}, +{0xE7A3,0x900E},{0xE7A4,0x9049},{0xE7A5,0x903E},{0xE7A6,0x9056}, +{0xE7A7,0x9058},{0xE7A8,0x905E},{0xE7A9,0x9068},{0xE7AA,0x906F}, +{0xE7AB,0x9076},{0xE7AC,0x96A8},{0xE7AD,0x9072},{0xE7AE,0x9082}, +{0xE7AF,0x907D},{0xE7B0,0x9081},{0xE7B1,0x9080},{0xE7B2,0x908A}, +{0xE7B3,0x9089},{0xE7B4,0x908F},{0xE7B5,0x90A8},{0xE7B6,0x90AF}, +{0xE7B7,0x90B1},{0xE7B8,0x90B5},{0xE7B9,0x90E2},{0xE7BA,0x90E4}, +{0xE7BB,0x6248},{0xE7BC,0x90DB},{0xE7BD,0x9102},{0xE7BE,0x9112}, +{0xE7BF,0x9119},{0xE7C0,0x9132},{0xE7C1,0x9130},{0xE7C2,0x914A}, +{0xE7C3,0x9156},{0xE7C4,0x9158},{0xE7C5,0x9163},{0xE7C6,0x9165}, +{0xE7C7,0x9169},{0xE7C8,0x9173},{0xE7C9,0x9172},{0xE7CA,0x918B}, +{0xE7CB,0x9189},{0xE7CC,0x9182},{0xE7CD,0x91A2},{0xE7CE,0x91AB}, +{0xE7CF,0x91AF},{0xE7D0,0x91AA},{0xE7D1,0x91B5},{0xE7D2,0x91B4}, +{0xE7D3,0x91BA},{0xE7D4,0x91C0},{0xE7D5,0x91C1},{0xE7D6,0x91C9}, +{0xE7D7,0x91CB},{0xE7D8,0x91D0},{0xE7D9,0x91D6},{0xE7DA,0x91DF}, +{0xE7DB,0x91E1},{0xE7DC,0x91DB},{0xE7DD,0x91FC},{0xE7DE,0x91F5}, +{0xE7DF,0x91F6},{0xE7E0,0x921E},{0xE7E1,0x91FF},{0xE7E2,0x9214}, +{0xE7E3,0x922C},{0xE7E4,0x9215},{0xE7E5,0x9211},{0xE7E6,0x925E}, +{0xE7E7,0x9257},{0xE7E8,0x9245},{0xE7E9,0x9249},{0xE7EA,0x9264}, +{0xE7EB,0x9248},{0xE7EC,0x9295},{0xE7ED,0x923F},{0xE7EE,0x924B}, +{0xE7EF,0x9250},{0xE7F0,0x929C},{0xE7F1,0x9296},{0xE7F2,0x9293}, +{0xE7F3,0x929B},{0xE7F4,0x925A},{0xE7F5,0x92CF},{0xE7F6,0x92B9}, +{0xE7F7,0x92B7},{0xE7F8,0x92E9},{0xE7F9,0x930F},{0xE7FA,0x92FA}, +{0xE7FB,0x9344},{0xE7FC,0x932E},{0xE840,0x9319},{0xE841,0x9322}, +{0xE842,0x931A},{0xE843,0x9323},{0xE844,0x933A},{0xE845,0x9335}, +{0xE846,0x933B},{0xE847,0x935C},{0xE848,0x9360},{0xE849,0x937C}, +{0xE84A,0x936E},{0xE84B,0x9356},{0xE84C,0x93B0},{0xE84D,0x93AC}, +{0xE84E,0x93AD},{0xE84F,0x9394},{0xE850,0x93B9},{0xE851,0x93D6}, +{0xE852,0x93D7},{0xE853,0x93E8},{0xE854,0x93E5},{0xE855,0x93D8}, +{0xE856,0x93C3},{0xE857,0x93DD},{0xE858,0x93D0},{0xE859,0x93C8}, +{0xE85A,0x93E4},{0xE85B,0x941A},{0xE85C,0x9414},{0xE85D,0x9413}, +{0xE85E,0x9403},{0xE85F,0x9407},{0xE860,0x9410},{0xE861,0x9436}, +{0xE862,0x942B},{0xE863,0x9435},{0xE864,0x9421},{0xE865,0x943A}, +{0xE866,0x9441},{0xE867,0x9452},{0xE868,0x9444},{0xE869,0x945B}, +{0xE86A,0x9460},{0xE86B,0x9462},{0xE86C,0x945E},{0xE86D,0x946A}, +{0xE86E,0x9229},{0xE86F,0x9470},{0xE870,0x9475},{0xE871,0x9477}, +{0xE872,0x947D},{0xE873,0x945A},{0xE874,0x947C},{0xE875,0x947E}, +{0xE876,0x9481},{0xE877,0x947F},{0xE878,0x9582},{0xE879,0x9587}, +{0xE87A,0x958A},{0xE87B,0x9594},{0xE87C,0x9596},{0xE87D,0x9598}, +{0xE87E,0x9599},{0xE880,0x95A0},{0xE881,0x95A8},{0xE882,0x95A7}, +{0xE883,0x95AD},{0xE884,0x95BC},{0xE885,0x95BB},{0xE886,0x95B9}, +{0xE887,0x95BE},{0xE888,0x95CA},{0xE889,0x6FF6},{0xE88A,0x95C3}, +{0xE88B,0x95CD},{0xE88C,0x95CC},{0xE88D,0x95D5},{0xE88E,0x95D4}, +{0xE88F,0x95D6},{0xE890,0x95DC},{0xE891,0x95E1},{0xE892,0x95E5}, +{0xE893,0x95E2},{0xE894,0x9621},{0xE895,0x9628},{0xE896,0x962E}, +{0xE897,0x962F},{0xE898,0x9642},{0xE899,0x964C},{0xE89A,0x964F}, +{0xE89B,0x964B},{0xE89C,0x9677},{0xE89D,0x965C},{0xE89E,0x965E}, +{0xE89F,0x965D},{0xE8A0,0x965F},{0xE8A1,0x9666},{0xE8A2,0x9672}, +{0xE8A3,0x966C},{0xE8A4,0x968D},{0xE8A5,0x9698},{0xE8A6,0x9695}, +{0xE8A7,0x9697},{0xE8A8,0x96AA},{0xE8A9,0x96A7},{0xE8AA,0x96B1}, +{0xE8AB,0x96B2},{0xE8AC,0x96B0},{0xE8AD,0x96B4},{0xE8AE,0x96B6}, +{0xE8AF,0x96B8},{0xE8B0,0x96B9},{0xE8B1,0x96CE},{0xE8B2,0x96CB}, +{0xE8B3,0x96C9},{0xE8B4,0x96CD},{0xE8B5,0x894D},{0xE8B6,0x96DC}, +{0xE8B7,0x970D},{0xE8B8,0x96D5},{0xE8B9,0x96F9},{0xE8BA,0x9704}, +{0xE8BB,0x9706},{0xE8BC,0x9708},{0xE8BD,0x9713},{0xE8BE,0x970E}, +{0xE8BF,0x9711},{0xE8C0,0x970F},{0xE8C1,0x9716},{0xE8C2,0x9719}, +{0xE8C3,0x9724},{0xE8C4,0x972A},{0xE8C5,0x9730},{0xE8C6,0x9739}, +{0xE8C7,0x973D},{0xE8C8,0x973E},{0xE8C9,0x9744},{0xE8CA,0x9746}, +{0xE8CB,0x9748},{0xE8CC,0x9742},{0xE8CD,0x9749},{0xE8CE,0x975C}, +{0xE8CF,0x9760},{0xE8D0,0x9764},{0xE8D1,0x9766},{0xE8D2,0x9768}, +{0xE8D3,0x52D2},{0xE8D4,0x976B},{0xE8D5,0x9771},{0xE8D6,0x9779}, +{0xE8D7,0x9785},{0xE8D8,0x977C},{0xE8D9,0x9781},{0xE8DA,0x977A}, +{0xE8DB,0x9786},{0xE8DC,0x978B},{0xE8DD,0x978F},{0xE8DE,0x9790}, +{0xE8DF,0x979C},{0xE8E0,0x97A8},{0xE8E1,0x97A6},{0xE8E2,0x97A3}, +{0xE8E3,0x97B3},{0xE8E4,0x97B4},{0xE8E5,0x97C3},{0xE8E6,0x97C6}, +{0xE8E7,0x97C8},{0xE8E8,0x97CB},{0xE8E9,0x97DC},{0xE8EA,0x97ED}, +{0xE8EB,0x9F4F},{0xE8EC,0x97F2},{0xE8ED,0x7ADF},{0xE8EE,0x97F6}, +{0xE8EF,0x97F5},{0xE8F0,0x980F},{0xE8F1,0x980C},{0xE8F2,0x9838}, +{0xE8F3,0x9824},{0xE8F4,0x9821},{0xE8F5,0x9837},{0xE8F6,0x983D}, +{0xE8F7,0x9846},{0xE8F8,0x984F},{0xE8F9,0x984B},{0xE8FA,0x986B}, +{0xE8FB,0x986F},{0xE8FC,0x9870},{0xE940,0x9871},{0xE941,0x9874}, +{0xE942,0x9873},{0xE943,0x98AA},{0xE944,0x98AF},{0xE945,0x98B1}, +{0xE946,0x98B6},{0xE947,0x98C4},{0xE948,0x98C3},{0xE949,0x98C6}, +{0xE94A,0x98E9},{0xE94B,0x98EB},{0xE94C,0x9903},{0xE94D,0x9909}, +{0xE94E,0x9912},{0xE94F,0x9914},{0xE950,0x9918},{0xE951,0x9921}, +{0xE952,0x991D},{0xE953,0x991E},{0xE954,0x9924},{0xE955,0x9920}, +{0xE956,0x992C},{0xE957,0x992E},{0xE958,0x993D},{0xE959,0x993E}, +{0xE95A,0x9942},{0xE95B,0x9949},{0xE95C,0x9945},{0xE95D,0x9950}, +{0xE95E,0x994B},{0xE95F,0x9951},{0xE960,0x9952},{0xE961,0x994C}, +{0xE962,0x9955},{0xE963,0x9997},{0xE964,0x9998},{0xE965,0x99A5}, +{0xE966,0x99AD},{0xE967,0x99AE},{0xE968,0x99BC},{0xE969,0x99DF}, +{0xE96A,0x99DB},{0xE96B,0x99DD},{0xE96C,0x99D8},{0xE96D,0x99D1}, +{0xE96E,0x99ED},{0xE96F,0x99EE},{0xE970,0x99F1},{0xE971,0x99F2}, +{0xE972,0x99FB},{0xE973,0x99F8},{0xE974,0x9A01},{0xE975,0x9A0F}, +{0xE976,0x9A05},{0xE977,0x99E2},{0xE978,0x9A19},{0xE979,0x9A2B}, +{0xE97A,0x9A37},{0xE97B,0x9A45},{0xE97C,0x9A42},{0xE97D,0x9A40}, +{0xE97E,0x9A43},{0xE980,0x9A3E},{0xE981,0x9A55},{0xE982,0x9A4D}, +{0xE983,0x9A5B},{0xE984,0x9A57},{0xE985,0x9A5F},{0xE986,0x9A62}, +{0xE987,0x9A65},{0xE988,0x9A64},{0xE989,0x9A69},{0xE98A,0x9A6B}, +{0xE98B,0x9A6A},{0xE98C,0x9AAD},{0xE98D,0x9AB0},{0xE98E,0x9ABC}, +{0xE98F,0x9AC0},{0xE990,0x9ACF},{0xE991,0x9AD1},{0xE992,0x9AD3}, +{0xE993,0x9AD4},{0xE994,0x9ADE},{0xE995,0x9ADF},{0xE996,0x9AE2}, +{0xE997,0x9AE3},{0xE998,0x9AE6},{0xE999,0x9AEF},{0xE99A,0x9AEB}, +{0xE99B,0x9AEE},{0xE99C,0x9AF4},{0xE99D,0x9AF1},{0xE99E,0x9AF7}, +{0xE99F,0x9AFB},{0xE9A0,0x9B06},{0xE9A1,0x9B18},{0xE9A2,0x9B1A}, +{0xE9A3,0x9B1F},{0xE9A4,0x9B22},{0xE9A5,0x9B23},{0xE9A6,0x9B25}, +{0xE9A7,0x9B27},{0xE9A8,0x9B28},{0xE9A9,0x9B29},{0xE9AA,0x9B2A}, +{0xE9AB,0x9B2E},{0xE9AC,0x9B2F},{0xE9AD,0x9B32},{0xE9AE,0x9B44}, +{0xE9AF,0x9B43},{0xE9B0,0x9B4F},{0xE9B1,0x9B4D},{0xE9B2,0x9B4E}, +{0xE9B3,0x9B51},{0xE9B4,0x9B58},{0xE9B5,0x9B74},{0xE9B6,0x9B93}, +{0xE9B7,0x9B83},{0xE9B8,0x9B91},{0xE9B9,0x9B96},{0xE9BA,0x9B97}, +{0xE9BB,0x9B9F},{0xE9BC,0x9BA0},{0xE9BD,0x9BA8},{0xE9BE,0x9BB4}, +{0xE9BF,0x9BC0},{0xE9C0,0x9BCA},{0xE9C1,0x9BB9},{0xE9C2,0x9BC6}, +{0xE9C3,0x9BCF},{0xE9C4,0x9BD1},{0xE9C5,0x9BD2},{0xE9C6,0x9BE3}, +{0xE9C7,0x9BE2},{0xE9C8,0x9BE4},{0xE9C9,0x9BD4},{0xE9CA,0x9BE1}, +{0xE9CB,0x9C3A},{0xE9CC,0x9BF2},{0xE9CD,0x9BF1},{0xE9CE,0x9BF0}, +{0xE9CF,0x9C15},{0xE9D0,0x9C14},{0xE9D1,0x9C09},{0xE9D2,0x9C13}, +{0xE9D3,0x9C0C},{0xE9D4,0x9C06},{0xE9D5,0x9C08},{0xE9D6,0x9C12}, +{0xE9D7,0x9C0A},{0xE9D8,0x9C04},{0xE9D9,0x9C2E},{0xE9DA,0x9C1B}, +{0xE9DB,0x9C25},{0xE9DC,0x9C24},{0xE9DD,0x9C21},{0xE9DE,0x9C30}, +{0xE9DF,0x9C47},{0xE9E0,0x9C32},{0xE9E1,0x9C46},{0xE9E2,0x9C3E}, +{0xE9E3,0x9C5A},{0xE9E4,0x9C60},{0xE9E5,0x9C67},{0xE9E6,0x9C76}, +{0xE9E7,0x9C78},{0xE9E8,0x9CE7},{0xE9E9,0x9CEC},{0xE9EA,0x9CF0}, +{0xE9EB,0x9D09},{0xE9EC,0x9D08},{0xE9ED,0x9CEB},{0xE9EE,0x9D03}, +{0xE9EF,0x9D06},{0xE9F0,0x9D2A},{0xE9F1,0x9D26},{0xE9F2,0x9DAF}, +{0xE9F3,0x9D23},{0xE9F4,0x9D1F},{0xE9F5,0x9D44},{0xE9F6,0x9D15}, +{0xE9F7,0x9D12},{0xE9F8,0x9D41},{0xE9F9,0x9D3F},{0xE9FA,0x9D3E}, +{0xE9FB,0x9D46},{0xE9FC,0x9D48},{0xEA40,0x9D5D},{0xEA41,0x9D5E}, +{0xEA42,0x9D64},{0xEA43,0x9D51},{0xEA44,0x9D50},{0xEA45,0x9D59}, +{0xEA46,0x9D72},{0xEA47,0x9D89},{0xEA48,0x9D87},{0xEA49,0x9DAB}, +{0xEA4A,0x9D6F},{0xEA4B,0x9D7A},{0xEA4C,0x9D9A},{0xEA4D,0x9DA4}, +{0xEA4E,0x9DA9},{0xEA4F,0x9DB2},{0xEA50,0x9DC4},{0xEA51,0x9DC1}, +{0xEA52,0x9DBB},{0xEA53,0x9DB8},{0xEA54,0x9DBA},{0xEA55,0x9DC6}, +{0xEA56,0x9DCF},{0xEA57,0x9DC2},{0xEA58,0x9DD9},{0xEA59,0x9DD3}, +{0xEA5A,0x9DF8},{0xEA5B,0x9DE6},{0xEA5C,0x9DED},{0xEA5D,0x9DEF}, +{0xEA5E,0x9DFD},{0xEA5F,0x9E1A},{0xEA60,0x9E1B},{0xEA61,0x9E1E}, +{0xEA62,0x9E75},{0xEA63,0x9E79},{0xEA64,0x9E7D},{0xEA65,0x9E81}, +{0xEA66,0x9E88},{0xEA67,0x9E8B},{0xEA68,0x9E8C},{0xEA69,0x9E92}, +{0xEA6A,0x9E95},{0xEA6B,0x9E91},{0xEA6C,0x9E9D},{0xEA6D,0x9EA5}, +{0xEA6E,0x9EA9},{0xEA6F,0x9EB8},{0xEA70,0x9EAA},{0xEA71,0x9EAD}, +{0xEA72,0x9761},{0xEA73,0x9ECC},{0xEA74,0x9ECE},{0xEA75,0x9ECF}, +{0xEA76,0x9ED0},{0xEA77,0x9ED4},{0xEA78,0x9EDC},{0xEA79,0x9EDE}, +{0xEA7A,0x9EDD},{0xEA7B,0x9EE0},{0xEA7C,0x9EE5},{0xEA7D,0x9EE8}, +{0xEA7E,0x9EEF},{0xEA80,0x9EF4},{0xEA81,0x9EF6},{0xEA82,0x9EF7}, +{0xEA83,0x9EF9},{0xEA84,0x9EFB},{0xEA85,0x9EFC},{0xEA86,0x9EFD}, +{0xEA87,0x9F07},{0xEA88,0x9F08},{0xEA89,0x76B7},{0xEA8A,0x9F15}, +{0xEA8B,0x9F21},{0xEA8C,0x9F2C},{0xEA8D,0x9F3E},{0xEA8E,0x9F4A}, +{0xEA8F,0x9F52},{0xEA90,0x9F54},{0xEA91,0x9F63},{0xEA92,0x9F5F}, +{0xEA93,0x9F60},{0xEA94,0x9F61},{0xEA95,0x9F66},{0xEA96,0x9F67}, +{0xEA97,0x9F6C},{0xEA98,0x9F6A},{0xEA99,0x9F77},{0xEA9A,0x9F72}, +{0xEA9B,0x9F76},{0xEA9C,0x9F95},{0xEA9D,0x9F9C},{0xEA9E,0x9FA0}, +{0xEA9F,0x582F},{0xEAA0,0x69C7},{0xEAA1,0x9059},{0xEAA2,0x7464}, +{0xEAA3,0x51DC},{0xEAA4,0x7199},{0xED40,0x7E8A},{0xED41,0x891C}, +{0xED42,0x9348},{0xED43,0x9288},{0xED44,0x84DC},{0xED45,0x4FC9}, +{0xED46,0x70BB},{0xED47,0x6631},{0xED48,0x68C8},{0xED49,0x92F9}, +{0xED4A,0x66FB},{0xED4B,0x5F45},{0xED4C,0x4E28},{0xED4D,0x4EE1}, +{0xED4E,0x4EFC},{0xED4F,0x4F00},{0xED50,0x4F03},{0xED51,0x4F39}, +{0xED52,0x4F56},{0xED53,0x4F92},{0xED54,0x4F8A},{0xED55,0x4F9A}, +{0xED56,0x4F94},{0xED57,0x4FCD},{0xED58,0x5040},{0xED59,0x5022}, +{0xED5A,0x4FFF},{0xED5B,0x501E},{0xED5C,0x5046},{0xED5D,0x5070}, +{0xED5E,0x5042},{0xED5F,0x5094},{0xED60,0x50F4},{0xED61,0x50D8}, +{0xED62,0x514A},{0xED63,0x5164},{0xED64,0x519D},{0xED65,0x51BE}, +{0xED66,0x51EC},{0xED67,0x5215},{0xED68,0x529C},{0xED69,0x52A6}, +{0xED6A,0x52C0},{0xED6B,0x52DB},{0xED6C,0x5300},{0xED6D,0x5307}, +{0xED6E,0x5324},{0xED6F,0x5372},{0xED70,0x5393},{0xED71,0x53B2}, +{0xED72,0x53DD},{0xED73,0xFA0E},{0xED74,0x549C},{0xED75,0x548A}, +{0xED76,0x54A9},{0xED77,0x54FF},{0xED78,0x5586},{0xED79,0x5759}, +{0xED7A,0x5765},{0xED7B,0x57AC},{0xED7C,0x57C8},{0xED7D,0x57C7}, +{0xED7E,0xFA0F},{0xED80,0xFA10},{0xED81,0x589E},{0xED82,0x58B2}, +{0xED83,0x590B},{0xED84,0x5953},{0xED85,0x595B},{0xED86,0x595D}, +{0xED87,0x5963},{0xED88,0x59A4},{0xED89,0x59BA},{0xED8A,0x5B56}, +{0xED8B,0x5BC0},{0xED8C,0x752F},{0xED8D,0x5BD8},{0xED8E,0x5BEC}, +{0xED8F,0x5C1E},{0xED90,0x5CA6},{0xED91,0x5CBA},{0xED92,0x5CF5}, +{0xED93,0x5D27},{0xED94,0x5D53},{0xED95,0xFA11},{0xED96,0x5D42}, +{0xED97,0x5D6D},{0xED98,0x5DB8},{0xED99,0x5DB9},{0xED9A,0x5DD0}, +{0xED9B,0x5F21},{0xED9C,0x5F34},{0xED9D,0x5F67},{0xED9E,0x5FB7}, +{0xED9F,0x5FDE},{0xEDA0,0x605D},{0xEDA1,0x6085},{0xEDA2,0x608A}, +{0xEDA3,0x60DE},{0xEDA4,0x60D5},{0xEDA5,0x6120},{0xEDA6,0x60F2}, +{0xEDA7,0x6111},{0xEDA8,0x6137},{0xEDA9,0x6130},{0xEDAA,0x6198}, +{0xEDAB,0x6213},{0xEDAC,0x62A6},{0xEDAD,0x63F5},{0xEDAE,0x6460}, +{0xEDAF,0x649D},{0xEDB0,0x64CE},{0xEDB1,0x654E},{0xEDB2,0x6600}, +{0xEDB3,0x6615},{0xEDB4,0x663B},{0xEDB5,0x6609},{0xEDB6,0x662E}, +{0xEDB7,0x661E},{0xEDB8,0x6624},{0xEDB9,0x6665},{0xEDBA,0x6657}, +{0xEDBB,0x6659},{0xEDBC,0xFA12},{0xEDBD,0x6673},{0xEDBE,0x6699}, +{0xEDBF,0x66A0},{0xEDC0,0x66B2},{0xEDC1,0x66BF},{0xEDC2,0x66FA}, +{0xEDC3,0x670E},{0xEDC4,0xF929},{0xEDC5,0x6766},{0xEDC6,0x67BB}, +{0xEDC7,0x6852},{0xEDC8,0x67C0},{0xEDC9,0x6801},{0xEDCA,0x6844}, +{0xEDCB,0x68CF},{0xEDCC,0xFA13},{0xEDCD,0x6968},{0xEDCE,0xFA14}, +{0xEDCF,0x6998},{0xEDD0,0x69E2},{0xEDD1,0x6A30},{0xEDD2,0x6A6B}, +{0xEDD3,0x6A46},{0xEDD4,0x6A73},{0xEDD5,0x6A7E},{0xEDD6,0x6AE2}, +{0xEDD7,0x6AE4},{0xEDD8,0x6BD6},{0xEDD9,0x6C3F},{0xEDDA,0x6C5C}, +{0xEDDB,0x6C86},{0xEDDC,0x6C6F},{0xEDDD,0x6CDA},{0xEDDE,0x6D04}, +{0xEDDF,0x6D87},{0xEDE0,0x6D6F},{0xEDE1,0x6D96},{0xEDE2,0x6DAC}, +{0xEDE3,0x6DCF},{0xEDE4,0x6DF8},{0xEDE5,0x6DF2},{0xEDE6,0x6DFC}, +{0xEDE7,0x6E39},{0xEDE8,0x6E5C},{0xEDE9,0x6E27},{0xEDEA,0x6E3C}, +{0xEDEB,0x6EBF},{0xEDEC,0x6F88},{0xEDED,0x6FB5},{0xEDEE,0x6FF5}, +{0xEDEF,0x7005},{0xEDF0,0x7007},{0xEDF1,0x7028},{0xEDF2,0x7085}, +{0xEDF3,0x70AB},{0xEDF4,0x710F},{0xEDF5,0x7104},{0xEDF6,0x715C}, +{0xEDF7,0x7146},{0xEDF8,0x7147},{0xEDF9,0xFA15},{0xEDFA,0x71C1}, +{0xEDFB,0x71FE},{0xEDFC,0x72B1},{0xEE40,0x72BE},{0xEE41,0x7324}, +{0xEE42,0xFA16},{0xEE43,0x7377},{0xEE44,0x73BD},{0xEE45,0x73C9}, +{0xEE46,0x73D6},{0xEE47,0x73E3},{0xEE48,0x73D2},{0xEE49,0x7407}, +{0xEE4A,0x73F5},{0xEE4B,0x7426},{0xEE4C,0x742A},{0xEE4D,0x7429}, +{0xEE4E,0x742E},{0xEE4F,0x7462},{0xEE50,0x7489},{0xEE51,0x749F}, +{0xEE52,0x7501},{0xEE53,0x756F},{0xEE54,0x7682},{0xEE55,0x769C}, +{0xEE56,0x769E},{0xEE57,0x769B},{0xEE58,0x76A6},{0xEE59,0xFA17}, +{0xEE5A,0x7746},{0xEE5B,0x52AF},{0xEE5C,0x7821},{0xEE5D,0x784E}, +{0xEE5E,0x7864},{0xEE5F,0x787A},{0xEE60,0x7930},{0xEE61,0xFA18}, +{0xEE62,0xFA19},{0xEE63,0xFA1A},{0xEE64,0x7994},{0xEE65,0xFA1B}, +{0xEE66,0x799B},{0xEE67,0x7AD1},{0xEE68,0x7AE7},{0xEE69,0xFA1C}, +{0xEE6A,0x7AEB},{0xEE6B,0x7B9E},{0xEE6C,0xFA1D},{0xEE6D,0x7D48}, +{0xEE6E,0x7D5C},{0xEE6F,0x7DB7},{0xEE70,0x7DA0},{0xEE71,0x7DD6}, +{0xEE72,0x7E52},{0xEE73,0x7F47},{0xEE74,0x7FA1},{0xEE75,0xFA1E}, +{0xEE76,0x8301},{0xEE77,0x8362},{0xEE78,0x837F},{0xEE79,0x83C7}, +{0xEE7A,0x83F6},{0xEE7B,0x8448},{0xEE7C,0x84B4},{0xEE7D,0x8553}, +{0xEE7E,0x8559},{0xEE80,0x856B},{0xEE81,0xFA1F},{0xEE82,0x85B0}, +{0xEE83,0xFA20},{0xEE84,0xFA21},{0xEE85,0x8807},{0xEE86,0x88F5}, +{0xEE87,0x8A12},{0xEE88,0x8A37},{0xEE89,0x8A79},{0xEE8A,0x8AA7}, +{0xEE8B,0x8ABE},{0xEE8C,0x8ADF},{0xEE8D,0xFA22},{0xEE8E,0x8AF6}, +{0xEE8F,0x8B53},{0xEE90,0x8B7F},{0xEE91,0x8CF0},{0xEE92,0x8CF4}, +{0xEE93,0x8D12},{0xEE94,0x8D76},{0xEE95,0xFA23},{0xEE96,0x8ECF}, +{0xEE97,0xFA24},{0xEE98,0xFA25},{0xEE99,0x9067},{0xEE9A,0x90DE}, +{0xEE9B,0xFA26},{0xEE9C,0x9115},{0xEE9D,0x9127},{0xEE9E,0x91DA}, +{0xEE9F,0x91D7},{0xEEA0,0x91DE},{0xEEA1,0x91ED},{0xEEA2,0x91EE}, +{0xEEA3,0x91E4},{0xEEA4,0x91E5},{0xEEA5,0x9206},{0xEEA6,0x9210}, +{0xEEA7,0x920A},{0xEEA8,0x923A},{0xEEA9,0x9240},{0xEEAA,0x923C}, +{0xEEAB,0x924E},{0xEEAC,0x9259},{0xEEAD,0x9251},{0xEEAE,0x9239}, +{0xEEAF,0x9267},{0xEEB0,0x92A7},{0xEEB1,0x9277},{0xEEB2,0x9278}, +{0xEEB3,0x92E7},{0xEEB4,0x92D7},{0xEEB5,0x92D9},{0xEEB6,0x92D0}, +{0xEEB7,0xFA27},{0xEEB8,0x92D5},{0xEEB9,0x92E0},{0xEEBA,0x92D3}, +{0xEEBB,0x9325},{0xEEBC,0x9321},{0xEEBD,0x92FB},{0xEEBE,0xFA28}, +{0xEEBF,0x931E},{0xEEC0,0x92FF},{0xEEC1,0x931D},{0xEEC2,0x9302}, +{0xEEC3,0x9370},{0xEEC4,0x9357},{0xEEC5,0x93A4},{0xEEC6,0x93C6}, +{0xEEC7,0x93DE},{0xEEC8,0x93F8},{0xEEC9,0x9431},{0xEECA,0x9445}, +{0xEECB,0x9448},{0xEECC,0x9592},{0xEECD,0xF9DC},{0xEECE,0xFA29}, +{0xEECF,0x969D},{0xEED0,0x96AF},{0xEED1,0x9733},{0xEED2,0x973B}, +{0xEED3,0x9743},{0xEED4,0x974D},{0xEED5,0x974F},{0xEED6,0x9751}, +{0xEED7,0x9755},{0xEED8,0x9857},{0xEED9,0x9865},{0xEEDA,0xFA2A}, +{0xEEDB,0xFA2B},{0xEEDC,0x9927},{0xEEDD,0xFA2C},{0xEEDE,0x999E}, +{0xEEDF,0x9A4E},{0xEEE0,0x9AD9},{0xEEE1,0x9ADC},{0xEEE2,0x9B75}, +{0xEEE3,0x9B72},{0xEEE4,0x9B8F},{0xEEE5,0x9BB1},{0xEEE6,0x9BBB}, +{0xEEE7,0x9C00},{0xEEE8,0x9D70},{0xEEE9,0x9D6B},{0xEEEA,0xFA2D}, +{0xEEEB,0x9E19},{0xEEEC,0x9ED1},{0xEEEF,0x2170},{0xEEF0,0x2171}, +{0xEEF1,0x2172},{0xEEF2,0x2173},{0xEEF3,0x2174},{0xEEF4,0x2175}, +{0xEEF5,0x2176},{0xEEF6,0x2177},{0xEEF7,0x2178},{0xEEF8,0x2179}, +{0xEEF9,0xFFE2},{0xEEFA,0xFFE4},{0xEEFB,0xFF07},{0xEEFC,0xFF02}, +{0xFA40,0x2170},{0xFA41,0x2171},{0xFA42,0x2172},{0xFA43,0x2173}, +{0xFA44,0x2174},{0xFA45,0x2175},{0xFA46,0x2176},{0xFA47,0x2177}, +{0xFA48,0x2178},{0xFA49,0x2179},{0xFA4A,0x2160},{0xFA4B,0x2161}, +{0xFA4C,0x2162},{0xFA4D,0x2163},{0xFA4E,0x2164},{0xFA4F,0x2165}, +{0xFA50,0x2166},{0xFA51,0x2167},{0xFA52,0x2168},{0xFA53,0x2169}, +{0xFA54,0xFFE2},{0xFA55,0xFFE4},{0xFA56,0xFF07},{0xFA57,0xFF02}, +{0xFA58,0x3231},{0xFA59,0x2116},{0xFA5A,0x2121},{0xFA5B,0x2235}, +{0xFA5C,0x7E8A},{0xFA5D,0x891C},{0xFA5E,0x9348},{0xFA5F,0x9288}, +{0xFA60,0x84DC},{0xFA61,0x4FC9},{0xFA62,0x70BB},{0xFA63,0x6631}, +{0xFA64,0x68C8},{0xFA65,0x92F9},{0xFA66,0x66FB},{0xFA67,0x5F45}, +{0xFA68,0x4E28},{0xFA69,0x4EE1},{0xFA6A,0x4EFC},{0xFA6B,0x4F00}, +{0xFA6C,0x4F03},{0xFA6D,0x4F39},{0xFA6E,0x4F56},{0xFA6F,0x4F92}, +{0xFA70,0x4F8A},{0xFA71,0x4F9A},{0xFA72,0x4F94},{0xFA73,0x4FCD}, +{0xFA74,0x5040},{0xFA75,0x5022},{0xFA76,0x4FFF},{0xFA77,0x501E}, +{0xFA78,0x5046},{0xFA79,0x5070},{0xFA7A,0x5042},{0xFA7B,0x5094}, +{0xFA7C,0x50F4},{0xFA7D,0x50D8},{0xFA7E,0x514A},{0xFA80,0x5164}, +{0xFA81,0x519D},{0xFA82,0x51BE},{0xFA83,0x51EC},{0xFA84,0x5215}, +{0xFA85,0x529C},{0xFA86,0x52A6},{0xFA87,0x52C0},{0xFA88,0x52DB}, +{0xFA89,0x5300},{0xFA8A,0x5307},{0xFA8B,0x5324},{0xFA8C,0x5372}, +{0xFA8D,0x5393},{0xFA8E,0x53B2},{0xFA8F,0x53DD},{0xFA90,0xFA0E}, +{0xFA91,0x549C},{0xFA92,0x548A},{0xFA93,0x54A9},{0xFA94,0x54FF}, +{0xFA95,0x5586},{0xFA96,0x5759},{0xFA97,0x5765},{0xFA98,0x57AC}, +{0xFA99,0x57C8},{0xFA9A,0x57C7},{0xFA9B,0xFA0F},{0xFA9C,0xFA10}, +{0xFA9D,0x589E},{0xFA9E,0x58B2},{0xFA9F,0x590B},{0xFAA0,0x5953}, +{0xFAA1,0x595B},{0xFAA2,0x595D},{0xFAA3,0x5963},{0xFAA4,0x59A4}, +{0xFAA5,0x59BA},{0xFAA6,0x5B56},{0xFAA7,0x5BC0},{0xFAA8,0x752F}, +{0xFAA9,0x5BD8},{0xFAAA,0x5BEC},{0xFAAB,0x5C1E},{0xFAAC,0x5CA6}, +{0xFAAD,0x5CBA},{0xFAAE,0x5CF5},{0xFAAF,0x5D27},{0xFAB0,0x5D53}, +{0xFAB1,0xFA11},{0xFAB2,0x5D42},{0xFAB3,0x5D6D},{0xFAB4,0x5DB8}, +{0xFAB5,0x5DB9},{0xFAB6,0x5DD0},{0xFAB7,0x5F21},{0xFAB8,0x5F34}, +{0xFAB9,0x5F67},{0xFABA,0x5FB7},{0xFABB,0x5FDE},{0xFABC,0x605D}, +{0xFABD,0x6085},{0xFABE,0x608A},{0xFABF,0x60DE},{0xFAC0,0x60D5}, +{0xFAC1,0x6120},{0xFAC2,0x60F2},{0xFAC3,0x6111},{0xFAC4,0x6137}, +{0xFAC5,0x6130},{0xFAC6,0x6198},{0xFAC7,0x6213},{0xFAC8,0x62A6}, +{0xFAC9,0x63F5},{0xFACA,0x6460},{0xFACB,0x649D},{0xFACC,0x64CE}, +{0xFACD,0x654E},{0xFACE,0x6600},{0xFACF,0x6615},{0xFAD0,0x663B}, +{0xFAD1,0x6609},{0xFAD2,0x662E},{0xFAD3,0x661E},{0xFAD4,0x6624}, +{0xFAD5,0x6665},{0xFAD6,0x6657},{0xFAD7,0x6659},{0xFAD8,0xFA12}, +{0xFAD9,0x6673},{0xFADA,0x6699},{0xFADB,0x66A0},{0xFADC,0x66B2}, +{0xFADD,0x66BF},{0xFADE,0x66FA},{0xFADF,0x670E},{0xFAE0,0xF929}, +{0xFAE1,0x6766},{0xFAE2,0x67BB},{0xFAE3,0x6852},{0xFAE4,0x67C0}, +{0xFAE5,0x6801},{0xFAE6,0x6844},{0xFAE7,0x68CF},{0xFAE8,0xFA13}, +{0xFAE9,0x6968},{0xFAEA,0xFA14},{0xFAEB,0x6998},{0xFAEC,0x69E2}, +{0xFAED,0x6A30},{0xFAEE,0x6A6B},{0xFAEF,0x6A46},{0xFAF0,0x6A73}, +{0xFAF1,0x6A7E},{0xFAF2,0x6AE2},{0xFAF3,0x6AE4},{0xFAF4,0x6BD6}, +{0xFAF5,0x6C3F},{0xFAF6,0x6C5C},{0xFAF7,0x6C86},{0xFAF8,0x6C6F}, +{0xFAF9,0x6CDA},{0xFAFA,0x6D04},{0xFAFB,0x6D87},{0xFAFC,0x6D6F}, +{0xFB40,0x6D96},{0xFB41,0x6DAC},{0xFB42,0x6DCF},{0xFB43,0x6DF8}, +{0xFB44,0x6DF2},{0xFB45,0x6DFC},{0xFB46,0x6E39},{0xFB47,0x6E5C}, +{0xFB48,0x6E27},{0xFB49,0x6E3C},{0xFB4A,0x6EBF},{0xFB4B,0x6F88}, +{0xFB4C,0x6FB5},{0xFB4D,0x6FF5},{0xFB4E,0x7005},{0xFB4F,0x7007}, +{0xFB50,0x7028},{0xFB51,0x7085},{0xFB52,0x70AB},{0xFB53,0x710F}, +{0xFB54,0x7104},{0xFB55,0x715C},{0xFB56,0x7146},{0xFB57,0x7147}, +{0xFB58,0xFA15},{0xFB59,0x71C1},{0xFB5A,0x71FE},{0xFB5B,0x72B1}, +{0xFB5C,0x72BE},{0xFB5D,0x7324},{0xFB5E,0xFA16},{0xFB5F,0x7377}, +{0xFB60,0x73BD},{0xFB61,0x73C9},{0xFB62,0x73D6},{0xFB63,0x73E3}, +{0xFB64,0x73D2},{0xFB65,0x7407},{0xFB66,0x73F5},{0xFB67,0x7426}, +{0xFB68,0x742A},{0xFB69,0x7429},{0xFB6A,0x742E},{0xFB6B,0x7462}, +{0xFB6C,0x7489},{0xFB6D,0x749F},{0xFB6E,0x7501},{0xFB6F,0x756F}, +{0xFB70,0x7682},{0xFB71,0x769C},{0xFB72,0x769E},{0xFB73,0x769B}, +{0xFB74,0x76A6},{0xFB75,0xFA17},{0xFB76,0x7746},{0xFB77,0x52AF}, +{0xFB78,0x7821},{0xFB79,0x784E},{0xFB7A,0x7864},{0xFB7B,0x787A}, +{0xFB7C,0x7930},{0xFB7D,0xFA18},{0xFB7E,0xFA19},{0xFB80,0xFA1A}, +{0xFB81,0x7994},{0xFB82,0xFA1B},{0xFB83,0x799B},{0xFB84,0x7AD1}, +{0xFB85,0x7AE7},{0xFB86,0xFA1C},{0xFB87,0x7AEB},{0xFB88,0x7B9E}, +{0xFB89,0xFA1D},{0xFB8A,0x7D48},{0xFB8B,0x7D5C},{0xFB8C,0x7DB7}, +{0xFB8D,0x7DA0},{0xFB8E,0x7DD6},{0xFB8F,0x7E52},{0xFB90,0x7F47}, +{0xFB91,0x7FA1},{0xFB92,0xFA1E},{0xFB93,0x8301},{0xFB94,0x8362}, +{0xFB95,0x837F},{0xFB96,0x83C7},{0xFB97,0x83F6},{0xFB98,0x8448}, +{0xFB99,0x84B4},{0xFB9A,0x8553},{0xFB9B,0x8559},{0xFB9C,0x856B}, +{0xFB9D,0xFA1F},{0xFB9E,0x85B0},{0xFB9F,0xFA20},{0xFBA0,0xFA21}, +{0xFBA1,0x8807},{0xFBA2,0x88F5},{0xFBA3,0x8A12},{0xFBA4,0x8A37}, +{0xFBA5,0x8A79},{0xFBA6,0x8AA7},{0xFBA7,0x8ABE},{0xFBA8,0x8ADF}, +{0xFBA9,0xFA22},{0xFBAA,0x8AF6},{0xFBAB,0x8B53},{0xFBAC,0x8B7F}, +{0xFBAD,0x8CF0},{0xFBAE,0x8CF4},{0xFBAF,0x8D12},{0xFBB0,0x8D76}, +{0xFBB1,0xFA23},{0xFBB2,0x8ECF},{0xFBB3,0xFA24},{0xFBB4,0xFA25}, +{0xFBB5,0x9067},{0xFBB6,0x90DE},{0xFBB7,0xFA26},{0xFBB8,0x9115}, +{0xFBB9,0x9127},{0xFBBA,0x91DA},{0xFBBB,0x91D7},{0xFBBC,0x91DE}, +{0xFBBD,0x91ED},{0xFBBE,0x91EE},{0xFBBF,0x91E4},{0xFBC0,0x91E5}, +{0xFBC1,0x9206},{0xFBC2,0x9210},{0xFBC3,0x920A},{0xFBC4,0x923A}, +{0xFBC5,0x9240},{0xFBC6,0x923C},{0xFBC7,0x924E},{0xFBC8,0x9259}, +{0xFBC9,0x9251},{0xFBCA,0x9239},{0xFBCB,0x9267},{0xFBCC,0x92A7}, +{0xFBCD,0x9277},{0xFBCE,0x9278},{0xFBCF,0x92E7},{0xFBD0,0x92D7}, +{0xFBD1,0x92D9},{0xFBD2,0x92D0},{0xFBD3,0xFA27},{0xFBD4,0x92D5}, +{0xFBD5,0x92E0},{0xFBD6,0x92D3},{0xFBD7,0x9325},{0xFBD8,0x9321}, +{0xFBD9,0x92FB},{0xFBDA,0xFA28},{0xFBDB,0x931E},{0xFBDC,0x92FF}, +{0xFBDD,0x931D},{0xFBDE,0x9302},{0xFBDF,0x9370},{0xFBE0,0x9357}, +{0xFBE1,0x93A4},{0xFBE2,0x93C6},{0xFBE3,0x93DE},{0xFBE4,0x93F8}, +{0xFBE5,0x9431},{0xFBE6,0x9445},{0xFBE7,0x9448},{0xFBE8,0x9592}, +{0xFBE9,0xF9DC},{0xFBEA,0xFA29},{0xFBEB,0x969D},{0xFBEC,0x96AF}, +{0xFBED,0x9733},{0xFBEE,0x973B},{0xFBEF,0x9743},{0xFBF0,0x974D}, +{0xFBF1,0x974F},{0xFBF2,0x9751},{0xFBF3,0x9755},{0xFBF4,0x9857}, +{0xFBF5,0x9865},{0xFBF6,0xFA2A},{0xFBF7,0xFA2B},{0xFBF8,0x9927}, +{0xFBF9,0xFA2C},{0xFBFA,0x999E},{0xFBFB,0x9A4E},{0xFBFC,0x9AD9}, +{0xFC40,0x9ADC},{0xFC41,0x9B75},{0xFC42,0x9B72},{0xFC43,0x9B8F}, +{0xFC44,0x9BB1},{0xFC45,0x9BBB},{0xFC46,0x9C00},{0xFC47,0x9D70}, +{0xFC48,0x9D6B},{0xFC49,0xFA2D},{0xFC4A,0x9E19},{0xFC4B,0x9ED1}}; +/* JIS */ + + +/* Convert a unicode string to a JIS string */ + + +/* Character to use is JIS to Unicode lookup fails */ +#define UNICODEDEFAULTCHAR (word) 0x005f /* '_' UNICODE_UNDERBAR */ + +void jis_to_unicode(byte *to, byte *p) +{ +int low, high, midpoint; +byte c; +word w, w1; +word j; /* JIS character as a word */ + + /* Convert JIS to word. Use one or two bytes of JIS */ + if (*p >= 0xa1 && *p <= 0xdf) /* Katakana */ + { + c = *p; + w = (word) c; + w += 0xfec0; + goto return_val; + } + else if (*p < 0x80) /* Ascii - not sure check if upper or lower */ + { + w = (word) *p; + goto return_val; + } + + /* The rest are two byte codes */ + w = (word) *p++; + w1 = (word) *p; + + j = (word)((w << 8) | (w1)); + + /* Now binary search the jis2ucode look up table */ + low=0; + high=NUMJISCHARS-1; + + while (low < high) + { + midpoint = (high + low) >> 1; + + if (j == jis2ucode[midpoint][0]) + { + w = jis2ucode[midpoint][1]; + goto return_val; + } + + if (j > jis2ucode[midpoint][0]) + { + if (low == midpoint) + { + low = high; + break; + } + low = midpoint; + } + else + { + if (high == midpoint) + { + break; + } + high = midpoint; + } + } + + if (j == jis2ucode[low][0]) + { + w = jis2ucode[low][1]; + goto return_val; + } + + /* No match, return a default character */ + w = UNICODEDEFAULTCHAR; + +return_val: + /* Make sure in intel order */ + fr_WORD (to, w); +} + +/* Character to use is Unicode to JIS fails */ +#define JISDEFAULTCHAR (word) 'X' + +/* convert unicode to jis put at pjis and return the length of the jis char */ +int unicode_to_jis(byte *pjis, byte *punicode) +{ +int low, high, midpoint; +word w,w1; +word u; /* Unicode character we want to convert to JIS */ + + /* little endian unicode - lowbye|hibyte */ + w = (word) *punicode++; + w1 = (word) *punicode; + + u = (word)((w1 << 8) | (w)); + /* Check for 1 byte jis */ + if (u >= 0xff61 /*0xfec0+0xa1*/ && u <= 0xff9f /*0xfec0+0xdf*/) /* Katakana */ + { + u -= 0xfec0; + *pjis = (byte) u; + return(1); + } + else if (u < 0x80) /* Ascii - not sure check if upper or lower */ + { + *pjis = (byte) u; + return(1); + } + else if (u == 0xffff) /* Special case lfn padding char */ + { + *pjis++ = 0xff; + *pjis = 0xff; + return(2); + } + + + /* Now binary search the ucode2jis look up table */ + low=0; + high=NUMUNICHARS-1; + + while (low < high) + { + midpoint = (high + low) >> 1; + + if (u == ucode2jis[midpoint][0]) + { + w = ucode2jis[midpoint][1]; + goto return_val; + } + + if (u > ucode2jis[midpoint][0]) + { + if (low == midpoint) + { + low = high; + break; + } + low = midpoint; + } + else + { + if (high == midpoint) + { + break; + } + high = midpoint; + } + } + + if (u == ucode2jis[low][0]) + { + w = ucode2jis[low][1]; + goto return_val; + } + + w = JISDEFAULTCHAR; /* No match */ +return_val: + + *pjis++ = (byte) (w>>8); + *pjis = (byte) (w&0xff); + return(2); /*__fn__*/ +} +#endif + diff --git a/build/libraries/fatfs/ARM7/csstrtab.c b/build/libraries/fatfs/ARM7/csstrtab.c new file mode 100644 index 0000000..4e0cf68 --- /dev/null +++ b/build/libraries/fatfs/ARM7/csstrtab.c @@ -0,0 +1,991 @@ +#include + +/* To preserve memory you may exclude strings from the string table + if the corresponding module is not being used. Set the definition + in this table to zero if you wish to exclude strings from the + from the corresponding module. +*/ + +#define INCLUDE_CHKDSK_STRINGS 1 +#define INCLUDE_FLDRVER_STRINGS 1 +#define INCLUDE_FLASHDRV_STRINGS 1 +#define INCLUDE_FLASHEMU_STRINGS 1 +#define INCLUDE_RTFSINIT_STRINGS 1 +#define INCLUDE_IDEDRV_STRINGS 1 +#define INCLUDE_PORTMAIN_STRINGS 1 +#define INCLUDE_PORTKERN_STRINGS 1 +#define INCLUDE_RTFSDEM_STRINGS 1 /* For APIRGRS.C */ +#define INCLUDE_TSTSH_STRINGS 1 + + +/* Set this value to 1 if strings are entered as ascii and we require + the unicode layer to expand them to 16 bit character strings + set it to zero if you replace the strrings in the table with + true UNICODE +*/ +typedef struct rtfs_string_table { + int string_id; + byte *string_value; +} RTFS_STRING_TABLE; + +KS_CONSTANT RTFS_STRING_TABLE prompt_table[] = { +#if (INCLUDE_CS_UNICODE) + {UPROMPT_CRITERR,(byte *)L"Type A to abort R to Retry"}, + {UPROMPT_REGRESS,(byte *)L"Regression Test Error, press any key to exit" }, +#else + {UPROMPT_CRITERR,(byte *)"Type A to abort R to Retry"}, + {UPROMPT_REGRESS,(byte *)"Regression Test Error, press any key to exit" }, +#endif +#if (INCLUDE_TSTSH_STRINGS) +#if (INCLUDE_CS_UNICODE) + {UPROMPT_TSTSH1,(byte *)L"CMD> " }, + {UPROMPT_TSTSH2,(byte *)L"Press Return " }, + {UPROMPT_TSTSH3,(byte *)L"Enter the drive to format as A:, B: etc " }, + {UPROMPT_TSTSH4,(byte *)L"Invalid drive selection to format, press return" }, + {UPROMPT_TSTSH5,(byte *)L"Format - check media failed. Press return" }, + {UPROMPT_TSTSH6,(byte *)L"Format: get media geometry failed. Press return" }, + {UPROMPT_TSTSH7,(byte *)L"Format: Press Y to format media " }, + {UPROMPT_TSTSH8,(byte *)L"Format: Media format failed. Press return" }, + {UPROMPT_TSTSH9,(byte *)L"Format: Press Y to partition media " }, + {UPROMPT_TSTSH10,(byte *)L"Format: Press Y to USE LBA formatting, N to use CHS " }, + {UPROMPT_TSTSH11,(byte *)L"Format: Select the number of lbas for the first partition :" }, + {UPROMPT_TSTSH12,(byte *)L"Format: Select the number of lbas for the second partition :" }, + {UPROMPT_TSTSH13,(byte *)L"Format: Bad input for partition values. Press return" }, + {UPROMPT_TSTSH14,(byte *)L"Format: Select the number of cyls for the first partition :" }, + {UPROMPT_TSTSH15,(byte *)L"Format: Select the number of cyls for the second partition :" }, + {UPROMPT_TSTSH16,(byte *)L"Format: Media partition failed. Press return" }, + {UPROMPT_TSTSH17,(byte *)L"Format: Press Y to format the volume " }, + {UPROMPT_TSTSH18,(byte *)L"Format: Format volume failed. Press return" }, +#else + {UPROMPT_TSTSH1,(byte *)"CMD> " }, + {UPROMPT_TSTSH2,(byte *)"Press Return " }, + {UPROMPT_TSTSH3,(byte *)"Enter the drive to format as A:, B: etc " }, + {UPROMPT_TSTSH4,(byte *)"Invalid drive selection to format, press return" }, + {UPROMPT_TSTSH5,(byte *)"Format - check media failed. Press return" }, + {UPROMPT_TSTSH6,(byte *)"Format: get media geometry failed. Press return" }, + {UPROMPT_TSTSH7,(byte *)"Format: Press Y to format media " }, + {UPROMPT_TSTSH8,(byte *)"Format: Media format failed. Press return" }, + {UPROMPT_TSTSH9,(byte *)"Format: Press Y to partition media " }, + {UPROMPT_TSTSH10,(byte *)"Format: Press Y to USE LBA formatting, N to use CHS " }, + {UPROMPT_TSTSH11,(byte *)"Format: Select the number of lbas for the first partition :" }, + {UPROMPT_TSTSH12,(byte *)"Format: Select the number of lbas for the second partition :" }, + {UPROMPT_TSTSH13,(byte *)"Format: Bad input for partition values. Press return" }, + {UPROMPT_TSTSH14,(byte *)"Format: Select the number of cyls for the first partition :" }, + {UPROMPT_TSTSH15,(byte *)"Format: Select the number of cyls for the second partition :" }, + {UPROMPT_TSTSH16,(byte *)"Format: Media partition failed. Press return" }, + {UPROMPT_TSTSH17,(byte *)"Format: Press Y to format the volume " }, + {UPROMPT_TSTSH18,(byte *)"Format: Format volume failed. Press return" }, +#endif +#endif /* (INCLUDE_TSTSH_STRINGS) */ +#if (INCLUDE_CS_UNICODE) + {UPROMPT_WINSPLSH,(byte *)L" for next, (P) for Previous, (Q) to run demo" }, + {0,(byte *)L"Unkown User Prompt" } +#else + {UPROMPT_WINSPLSH,(byte *)" for next, (P) for Previous, (Q) to run demo" }, + {0,(byte *)"Unkown User Prompt" } +#endif + }; + +KS_CONSTANT RTFS_STRING_TABLE string_table[] = { +#if (INCLUDE_CS_UNICODE) + {USTRING_SYS_NULL,(byte *)L"" }, +/* Note that USTRING_SYS_BADALIAS is not stored as a wide string. this + is processed as a string of 8 bit characters */ + {USTRING_SYS_BADALIAS,(byte *)"\\/:*?\"<>| ,;=+[]" }, + {USTRING_SYS_BADLFN,(byte *)L"\\/:*?\"<>|" }, +/* Note that USTRING_SYS_UCRESERVED_NAMES and USTRING_SYS_LCRESERVED_NAMES + are not stored as a wide string. they are processed as strings of 8 bit characters */ + {USTRING_SYS_UCRESERVED_NAMES,(byte *)"CON,PRN,NUL,AUX,LPT1,LPT2,LPT3,LPT4,COM1,COM2,COM3,COM4" }, + {USTRING_SYS_LCRESERVED_NAMES,(byte *)"con,prn,nul,aux,lpt1,lpt2,lpt3,lpt4,com1,com2,com3,com4" }, + {USTRING_SYS_VOLUME_LABEL,(byte *)L"VOLUMELABEL" }, /* 11 chars max (12th is NUL) */ + {USTRING_SYS_OEMNAME,(byte *)L"EBS" }, + {USTRING_SYS_TAB,(byte *)L" " }, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_SYS_FSFILENAME,(byte *)L"\\FAILSAFE" }, +#endif +#else + {USTRING_SYS_NULL,(byte *)"" }, + {USTRING_SYS_BADALIAS,(byte *)"\\/:*?\"<>| ,;=+[]" }, + {USTRING_SYS_BADLFN,(byte *)"\\/:*?\"<>|" }, + {USTRING_SYS_UCRESERVED_NAMES,(byte *)"CON,PRN,NUL,AUX,LPT1,LPT2,LPT3,LPT4,COM1,COM2,COM3,COM4" }, + {USTRING_SYS_LCRESERVED_NAMES,(byte *)"con,prn,nul,aux,lpt1,lpt2,lpt3,lpt4,com1,com2,com3,com4" }, + {USTRING_SYS_VOLUME_LABEL,(byte *)"VOLUMELABE" }, /* 11 chars max (12th is NUL) */ + {USTRING_SYS_OEMNAME,(byte *)"EBS" }, + {USTRING_SYS_TAB,(byte *)" " }, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_SYS_FSFILENAME,(byte *)"\\FAILSAFE" }, +#endif +#endif +#if (INCLUDE_CHKDSK_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_CHKDSK_01,(byte *)L"Error Accessing: " }, + {USTRING_CHKDSK_02,(byte *)L"Failed Allocating Core To Run" }, + {USTRING_CHKDSK_03,(byte *)L"Failed Scanning Disk Files" }, + {USTRING_CHKDSK_04,(byte *)L"Failed Scanning Fat" }, + {USTRING_CHKDSK_05,(byte *)L" Creating .CHK Files" }, + {USTRING_CHKDSK_06,(byte *)L" Failed Creating .CHK Files" }, + {USTRING_CHKDSK_07,(byte *)L" Use -f option to create .CHK files" }, + {USTRING_CHKDSK_08,(byte *)L"Failed Scanning Crossed Files" }, + {USTRING_CHKDSK_09,(byte *)L" Crossed Chains Were Found" }, + {USTRING_CHKDSK_11,(byte *)L" user files in this many directories " }, + {USTRING_CHKDSK_13,(byte *)L" Kbytes total disk space" }, + {USTRING_CHKDSK_15,(byte *)L" Kbytes in " }, + {USTRING_CHKDSK_16,(byte *)L" hidden files" }, + {USTRING_CHKDSK_18,(byte *)L" KBytes in " }, + {USTRING_CHKDSK_19,(byte *)L" directories" }, + {USTRING_CHKDSK_21,(byte *)L" KBytes in " }, + {USTRING_CHKDSK_22,(byte *)L" user files" }, + {USTRING_CHKDSK_24,(byte *)L" KBytes in bad sectors" }, + {USTRING_CHKDSK_26,(byte *)L" Free sectors available on disk" }, + {USTRING_CHKDSK_29,(byte *)L" Bytes Per Allocation Unit" }, + {USTRING_CHKDSK_30,(byte *)L" " }, + {USTRING_CHKDSK_32,(byte *)L" Total Allocation Units On Disk" }, + {USTRING_CHKDSK_35,(byte *)L" lost clusters found in " }, + {USTRING_CHKDSK_36,(byte *)L" lost chains" }, + {USTRING_CHKDSK_38,(byte *)L" bad long file name chains found" }, + {USTRING_CHKDSK_39,(byte *)L" and deleted" }, + {USTRING_CHKDSK_40,(byte *)L" that were not deleted" }, + {USTRING_CHKDSK_41,(byte *)L" Chains Crossed at Cluster" }, + {USTRING_CHKDSK_42,(byte *)L" " }, + {USTRING_CHKDSK_43,(byte *)L" Lost Chains" }, + {USTRING_CHKDSK_44,(byte *)L" A Lost Chain" }, + {USTRING_CHKDSK_45,(byte *)L"Path too deep , directory == " }, + {USTRING_CHKDSK_46,(byte *)L"Failed Scanning This Directory on LFN Pass -" }, + {USTRING_CHKDSK_47,(byte *)L"Failed Scanning This Directory -" }, + {USTRING_CHKDSK_48,(byte *)L"Failed Scanning This Directory -" }, + {USTRING_CHKDSK_49,(byte *)L" " }, + {USTRING_CHKDSK_50,(byte *)L"Failed Scanning This File " }, + {USTRING_CHKDSK_51,(byte *)L"Failed Scanning This Directory -" }, + {USTRING_CHKDSK_52,(byte *)L"Failed Writing This Adusted File: " }, + {USTRING_CHKDSK_53,(byte *)L"Size Adusted, This File: " }, + {USTRING_CHKDSK_54,(byte *)L"Size Needs Adusting This File: " }, + {USTRING_CHKDSK_55,(byte *)L"Chkdsk Giving up. Too Many Lost Chains" }, + {USTRING_CHKDSK_56,(byte *)L"Failed Scanning This Directory " }, + {USTRING_CHKDSK_57,(byte *)L"Failed Scanning This Directory " }, + {USTRING_CHKDSK_58,(byte *)L" " }, + {USTRING_CHKDSK_59,(byte *)L"Failed Scanning This File " }, + {USTRING_CHKDSK_60,(byte *)L"Failed Scanning This Directory " }, + {USTRING_CHKDSK_61,(byte *)L"Chkdsk gives up, too many crossed Files" }, + {USTRING_CHKDSK_62,(byte *)L"Chkdsk gives up, Too many crossed chains" }, +#else + {USTRING_CHKDSK_01,(byte *)"Error Accessing: " }, + {USTRING_CHKDSK_02,(byte *)"Failed Allocating Core To Run" }, + {USTRING_CHKDSK_03,(byte *)"Failed Scanning Disk Files" }, + {USTRING_CHKDSK_04,(byte *)"Failed Scanning Fat" }, + {USTRING_CHKDSK_05,(byte *)" Creating .CHK Files" }, + {USTRING_CHKDSK_06,(byte *)" Failed Creating .CHK Files" }, + {USTRING_CHKDSK_07,(byte *)" Use -f option to create .CHK files" }, + {USTRING_CHKDSK_08,(byte *)"Failed Scanning Crossed Files" }, + {USTRING_CHKDSK_09,(byte *)" Crossed Chains Were Found" }, + {USTRING_CHKDSK_11,(byte *)" user files in this many directories " }, + {USTRING_CHKDSK_13,(byte *)" Kbytes total disk space" }, + {USTRING_CHKDSK_15,(byte *)" Kbytes in " }, + {USTRING_CHKDSK_16,(byte *)" hidden files" }, + {USTRING_CHKDSK_18,(byte *)" KBytes in " }, + {USTRING_CHKDSK_19,(byte *)" directories" }, + {USTRING_CHKDSK_21,(byte *)" KBytes in " }, + {USTRING_CHKDSK_22,(byte *)" user files" }, + {USTRING_CHKDSK_24,(byte *)" KBytes in bad sectors" }, + {USTRING_CHKDSK_26,(byte *)" Free sectors available on disk" }, + {USTRING_CHKDSK_29,(byte *)" Bytes Per Allocation Unit" }, + {USTRING_CHKDSK_30,(byte *)" " }, + {USTRING_CHKDSK_32,(byte *)" Total Allocation Units On Disk" }, + {USTRING_CHKDSK_35,(byte *)" lost clusters found in " }, + {USTRING_CHKDSK_36,(byte *)" lost chains" }, + {USTRING_CHKDSK_38,(byte *)" bad long file name chains found" }, + {USTRING_CHKDSK_39,(byte *)" and deleted" }, + {USTRING_CHKDSK_40,(byte *)" that were not deleted" }, + {USTRING_CHKDSK_41,(byte *)" Chains Crossed at Cluster" }, + {USTRING_CHKDSK_42,(byte *)" " }, + {USTRING_CHKDSK_43,(byte *)" Lost Chains" }, + {USTRING_CHKDSK_44,(byte *)" A Lost Chain" }, + {USTRING_CHKDSK_45,(byte *)"Path too deep , directory == " }, + {USTRING_CHKDSK_46,(byte *)"Failed Scanning This Directory on LFN Pass -" }, + {USTRING_CHKDSK_47,(byte *)"Failed Scanning This Directory -" }, + {USTRING_CHKDSK_48,(byte *)"Failed Scanning This Directory -" }, + {USTRING_CHKDSK_49,(byte *)" " }, + {USTRING_CHKDSK_50,(byte *)"Failed Scanning This File " }, + {USTRING_CHKDSK_51,(byte *)"Failed Scanning This Directory -" }, + {USTRING_CHKDSK_52,(byte *)"Failed Writing This Adusted File: " }, + {USTRING_CHKDSK_53,(byte *)"Size Adusted, This File: " }, + {USTRING_CHKDSK_54,(byte *)"Size Needs Adusting This File: " }, + {USTRING_CHKDSK_55,(byte *)"Chkdsk Giving up. Too Many Lost Chains" }, + {USTRING_CHKDSK_56,(byte *)"Failed Scanning This Directory " }, + {USTRING_CHKDSK_57,(byte *)"Failed Scanning This Directory " }, + {USTRING_CHKDSK_58,(byte *)" " }, + {USTRING_CHKDSK_59,(byte *)"Failed Scanning This File " }, + {USTRING_CHKDSK_60,(byte *)"Failed Scanning This Directory " }, + {USTRING_CHKDSK_61,(byte *)"Chkdsk gives up, too many crossed Files" }, + {USTRING_CHKDSK_62,(byte *)"Chkdsk gives up, Too many crossed chains" }, +#endif +#endif /* (INCLUDE_CHKDSK_STRINGS) */ +#if (INCLUDE_CS_UNICODE) + {USTRING_CRITERR_02,(byte *)L"BAD_FORMAT" }, + {USTRING_CRITERR_03,(byte *)L"CRERR_NO_CARD" }, + {USTRING_CRITERR_04,(byte *)L"CRERR_BAD_CARD" }, + {USTRING_CRITERR_05,(byte *)L"CRERR_CHANGED_CARD" }, + {USTRING_CRITERR_06,(byte *)L"CRERR_CARD_FAILURE" }, + {USTRING_CRITERR_07,(byte *)L"Media status == " }, + {USTRING_CRITERR_08,(byte *)L"Volume == " }, + {USTRING_CRITERR_09,(byte *)L"Volume is dirty" }, + {USTRING_CRITERR_10,(byte *)L"Volume is clean" }, +#else + {USTRING_CRITERR_02,(byte *)"BAD_FORMAT" }, + {USTRING_CRITERR_03,(byte *)"CRERR_NO_CARD" }, + {USTRING_CRITERR_04,(byte *)"CRERR_BAD_CARD" }, + {USTRING_CRITERR_05,(byte *)"CRERR_CHANGED_CARD" }, + {USTRING_CRITERR_06,(byte *)"CRERR_CARD_FAILURE" }, + {USTRING_CRITERR_07,(byte *)"Media status == " }, + {USTRING_CRITERR_08,(byte *)"Volume == " }, + {USTRING_CRITERR_09,(byte *)"Volume is dirty" }, + {USTRING_CRITERR_10,(byte *)"Volume is clean" }, +#endif +#if (INCLUDE_FLDRVER_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_FLDRVER_01,(byte *)L"Formatting " }, + {USTRING_FLDRVER_02,(byte *)L" of " }, + {USTRING_FLDRVER_03,(byte *)L" total tracks " }, + {USTRING_FLDRVER_04,(byte *)L"Format failed resid != 0" }, + {USTRING_FLDRVER_06,(byte *)L"Abnormal Termination Error FLERR_ABN_TERM" }, + {USTRING_FLDRVER_07,(byte *)L"Floppy controller not responding FLERR_CHIP_HUNG " }, + {USTRING_FLDRVER_08,(byte *)L"Dma Error FLERR_DMA " }, + {USTRING_FLDRVER_09,(byte *)L"Error during format FLERR_FORMAT " }, + {USTRING_FLDRVER_10,(byte *)L"Invalid Interleave For Media Type FLERR_INVALID_INTERLEAVE" }, + {USTRING_FLDRVER_11,(byte *)L"Sector Not Found FLERR_IO_SECTOR" }, + {USTRING_FLDRVER_12,(byte *)L"Time out FLERR_IO_TMO " }, + {USTRING_FLDRVER_13,(byte *)L"Can not determine Media FLERR_MEDIA " }, + {USTRING_FLDRVER_14,(byte *)L"Error During Recal FLERR_RECAL " }, + {USTRING_FLDRVER_15,(byte *)L"Error resetting drive FLERR_RESET " }, + {USTRING_FLDRVER_16,(byte *)L"Error during seek FLERR_SEEK " }, + {USTRING_FLDRVER_17,(byte *)L"Error during specify FLERR_SPECIFY" }, + {USTRING_FLDRVER_18,(byte *)L"Unknown Drive type FLERR_UNK_DRIVE" }, + {USTRING_FLDRVER_19,(byte *)L"Unspecified floppy error" }, +#else + {USTRING_FLDRVER_01,(byte *)"Formatting " }, + {USTRING_FLDRVER_02,(byte *)" of " }, + {USTRING_FLDRVER_03,(byte *)" total tracks " }, + {USTRING_FLDRVER_04,(byte *)"Format failed resid != 0" }, + {USTRING_FLDRVER_06,(byte *)"Abnormal Termination Error FLERR_ABN_TERM" }, + {USTRING_FLDRVER_07,(byte *)"Floppy controller not responding FLERR_CHIP_HUNG " }, + {USTRING_FLDRVER_08,(byte *)"Dma Error FLERR_DMA " }, + {USTRING_FLDRVER_09,(byte *)"Error during format FLERR_FORMAT " }, + {USTRING_FLDRVER_10,(byte *)"Invalid Interleave For Media Type FLERR_INVALID_INTERLEAVE" }, + {USTRING_FLDRVER_11,(byte *)"Sector Not Found FLERR_IO_SECTOR" }, + {USTRING_FLDRVER_12,(byte *)"Time out FLERR_IO_TMO " }, + {USTRING_FLDRVER_13,(byte *)"Can not determine Media FLERR_MEDIA " }, + {USTRING_FLDRVER_14,(byte *)"Error During Recal FLERR_RECAL " }, + {USTRING_FLDRVER_15,(byte *)"Error resetting drive FLERR_RESET " }, + {USTRING_FLDRVER_16,(byte *)"Error during seek FLERR_SEEK " }, + {USTRING_FLDRVER_17,(byte *)"Error during specify FLERR_SPECIFY" }, + {USTRING_FLDRVER_18,(byte *)"Unknown Drive type FLERR_UNK_DRIVE" }, + {USTRING_FLDRVER_19,(byte *)"Unspecified floppy error" }, +#endif +#endif /* (INCLUDE_FLDRVER_STRINGS) */ +#if (INCLUDE_FLASHDRV_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_FLASHDRV_01,(byte *)L"DISK FULL" }, + {USTRING_FLASHDRV_02,(byte *)L"DATA ERROR" }, + {USTRING_FLASHDRV_03,(byte *)L"DEVICE RESOURCE ERROR" }, + {USTRING_FLASHDRV_04,(byte *)L"INTERNAL ERROR" }, + {USTRING_FLASHDRV_05,(byte *)L"Flash: Exception at line " }, + {USTRING_FLASHDRV_06,(byte *)L"RTFSYSErrorExit() called at line " }, + {USTRING_FLASHDRV_07,(byte *)L"Avail count messed up" }, + {USTRING_FLASHDRV_08,(byte *)L"*** adjusting EraseCount by " }, + {USTRING_FLASHDRV_09,(byte *)L"CopyBlock from " }, + {USTRING_FLASHDRV_10,(byte *)L" To " }, + {USTRING_FLASHDRV_11,(byte *)L"Replaced " }, + {USTRING_FLASHDRV_12,(byte *)L" Copied " }, + {USTRING_FLASHDRV_13,(byte *)L"Avail " }, + {USTRING_FLASHDRV_14,(byte *)L" Recovered " }, + {USTRING_FLASHDRV_15,(byte *)L"*** Wareleveling block " }, + {USTRING_FLASHDRV_16,(byte *)L"spare block not found" }, + {USTRING_FLASHDRV_17,(byte *)L"new spare block not found" }, + {USTRING_FLASHDRV_18,(byte *)L"Writing Sector " }, + {USTRING_FLASHDRV_19,(byte *)L" count " }, + {USTRING_FLASHDRV_20,(byte *)L"flash driver: ca not find a free sector" }, + {USTRING_FLASHDRV_21,(byte *)L"flash driver: ca not find sector to write" }, + {USTRING_FLASHDRV_22,(byte *)L"flash media too large" }, + {USTRING_FLASHDRV_23,(byte *)L"flash disk OK" }, + {USTRING_FLASHDRV_24,(byte *)L"Fixing 1" }, + {USTRING_FLASHDRV_25,(byte *)L"Fixing 2" }, + {USTRING_FLASHDRV_26,(byte *)L"Fixing 3" }, + {USTRING_FLASHDRV_27,(byte *)L"Invalid sector value in flash" }, + {USTRING_FLASHDRV_28,(byte *)L"Sector found twice" }, + {USTRING_FLASHDRV_29,(byte *)L"Invalid Sector status" }, + {USTRING_FLASHDRV_30,(byte *)L"Fixing 4" }, + {USTRING_FLASHDSK_01,(byte *)L"Flash device is formatted already." }, + {USTRING_FLASHDSK_02,(byte *)L"Call again to override and format." }, +#else + {USTRING_FLASHDRV_01,(byte *)"DISK FULL" }, + {USTRING_FLASHDRV_02,(byte *)"DATA ERROR" }, + {USTRING_FLASHDRV_03,(byte *)"DEVICE RESOURCE ERROR" }, + {USTRING_FLASHDRV_04,(byte *)"INTERNAL ERROR" }, + {USTRING_FLASHDRV_05,(byte *)"Flash: Exception at line " }, + {USTRING_FLASHDRV_06,(byte *)"RTFSYSErrorExit() called at line " }, + {USTRING_FLASHDRV_07,(byte *)"Avail count messed up" }, + {USTRING_FLASHDRV_08,(byte *)"*** adjusting EraseCount by " }, + {USTRING_FLASHDRV_09,(byte *)"CopyBlock from " }, + {USTRING_FLASHDRV_10,(byte *)" To " }, + {USTRING_FLASHDRV_11,(byte *)"Replaced " }, + {USTRING_FLASHDRV_12,(byte *)" Copied " }, + {USTRING_FLASHDRV_13,(byte *)"Avail " }, + {USTRING_FLASHDRV_14,(byte *)" Recovered " }, + {USTRING_FLASHDRV_15,(byte *)"*** Wareleveling block " }, + {USTRING_FLASHDRV_16,(byte *)"spare block not found" }, + {USTRING_FLASHDRV_17,(byte *)"new spare block not found" }, + {USTRING_FLASHDRV_18,(byte *)"Writing Sector " }, + {USTRING_FLASHDRV_19,(byte *)" count " }, + {USTRING_FLASHDRV_20,(byte *)"flash driver: ca not find a free sector" }, + {USTRING_FLASHDRV_21,(byte *)"flash driver: ca not find sector to write" }, + {USTRING_FLASHDRV_22,(byte *)"flash media too large" }, + {USTRING_FLASHDRV_23,(byte *)"flash disk OK" }, + {USTRING_FLASHDRV_24,(byte *)"Fixing 1" }, + {USTRING_FLASHDRV_25,(byte *)"Fixing 2" }, + {USTRING_FLASHDRV_26,(byte *)"Fixing 3" }, + {USTRING_FLASHDRV_27,(byte *)"Invalid sector value in flash" }, + {USTRING_FLASHDRV_28,(byte *)"Sector found twice" }, + {USTRING_FLASHDRV_29,(byte *)"Invalid Sector status" }, + {USTRING_FLASHDRV_30,(byte *)"Fixing 4" }, + {USTRING_FLASHDSK_01,(byte *)"Flash device is formatted already." }, + {USTRING_FLASHDSK_02,(byte *)"Call again to override and format." }, +#endif +#endif /* (INCLUDE_FLASHDRV_STRINGS) */ +#if (INCLUDE_FLASHEMU_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_FLASHEMU_01,(byte *)L"Curr > Base hit" }, + {USTRING_FLASHEMU_02,(byte *)L"Flash emu hit trap" }, + {USTRING_FLASHEMU_03,(byte *)L"flash emulator: called with file not open " }, + {USTRING_FLASHEMU_04,(byte *)L"MapWindow beyond block" }, + {USTRING_FLASHEMU_05,(byte *)L"EraseBlock: block not mapped" }, + {USTRING_FLASHEMU_06,(byte *)L"ProgramData: outside Window" }, + {USTRING_FLASHEMU_07,(byte *)L"attempt to unset bits in flash!" }, +#else + {USTRING_FLASHEMU_01,(byte *)"Curr > Base hit" }, + {USTRING_FLASHEMU_02,(byte *)"Flash emu hit trap" }, + {USTRING_FLASHEMU_03,(byte *)"flash emulator: called with file not open " }, + {USTRING_FLASHEMU_04,(byte *)"MapWindow beyond block" }, + {USTRING_FLASHEMU_05,(byte *)"EraseBlock: block not mapped" }, + {USTRING_FLASHEMU_06,(byte *)"ProgramData: outside Window" }, + {USTRING_FLASHEMU_07,(byte *)"attempt to unset bits in flash!" }, +#endif +#endif /* (INCLUDE_FLASHEMU_STRINGS) */ +#if (INCLUDE_RTFSINIT_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_RTFSINIT_01,(byte *)L"ERTFS Device List" }, + {USTRING_RTFSINIT_02,(byte *)L"=================" }, + {USTRING_RTFSINIT_03,(byte *)L"Name Logging Disabled" }, + {USTRING_RTFSINIT_04,(byte *)L" = " }, + {USTRING_RTFSINIT_05,(byte *)L"Autoformatting RAM Devices" }, + {USTRING_RTFSINIT_06,(byte *)L"==========================" }, + {USTRING_RTFSINIT_07,(byte *)L"Autoformatting Drive Id - " }, + {USTRING_RTFSINIT_08,(byte *)L" as Device: " }, + {USTRING_RTFSINIT_09,(byte *)L"Failed Autoformatting Drive Id - " }, + {USTRING_RTFSINIT_10,(byte *)L" = " }, + {USTRING_RTFSINIT_11,(byte *)L"NDRIVES is too small to mount all devices" }, + {USTRING_RTFSINIT_12,(byte *)L"Device name : " }, + {USTRING_RTFSINIT_13,(byte *)L" Is mounted on " }, +#else + {USTRING_RTFSINIT_01,(byte *)"ERTFS Device List" }, + {USTRING_RTFSINIT_02,(byte *)"=================" }, + {USTRING_RTFSINIT_03,(byte *)"Name Logging Disabled" }, + {USTRING_RTFSINIT_04,(byte *)" = " }, + {USTRING_RTFSINIT_05,(byte *)"Autoformatting RAM Devices" }, + {USTRING_RTFSINIT_06,(byte *)"==========================" }, + {USTRING_RTFSINIT_07,(byte *)"Autoformatting Drive Id - " }, + {USTRING_RTFSINIT_08,(byte *)" as Device: " }, + {USTRING_RTFSINIT_09,(byte *)"Failed Autoformatting Drive Id - " }, + {USTRING_RTFSINIT_10,(byte *)" = " }, + {USTRING_RTFSINIT_11,(byte *)"NDRIVES is too small to mount all devices" }, + {USTRING_RTFSINIT_12,(byte *)"Device name : " }, + {USTRING_RTFSINIT_13,(byte *)" Is mounted on " }, +#endif +#endif /* (INCLUDE_RTFSINIT_STRINGS) */ +#if (INCLUDE_IDEDRV_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_IDEDRV_01,(byte *)L"PIO Mode set succeed" }, + {USTRING_IDEDRV_02,(byte *)L"PIO Mode set failed" }, + {USTRING_IDEDRV_03,(byte *)L"Unknown ATAPI Command Issued" }, + {USTRING_IDEDRV_04,(byte *)L"Get LBA VTOC failed" }, + {USTRING_IDEDRV_05,(byte *)L"Read Audio Failed" }, +#else + {USTRING_IDEDRV_01,(byte *)"PIO Mode set succeed" }, + {USTRING_IDEDRV_02,(byte *)"PIO Mode set failed" }, + {USTRING_IDEDRV_03,(byte *)"Unknown ATAPI Command Issued" }, + {USTRING_IDEDRV_04,(byte *)"Get LBA VTOC failed" }, + {USTRING_IDEDRV_05,(byte *)"Read Audio Failed" }, +#endif +#endif /* (INCLUDE_IDEDRV_STRINGS) */ +#if (INCLUDE_PORTMAIN_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_PORTMAIN_01,(byte *)L"Spawn of main task failed. change priority ?" }, + {USTRING_PORTMAIN_02,(byte *)L"Spawn of main task failed." }, +#else + {USTRING_PORTMAIN_01,(byte *)"Spawn of main task failed. change priority ?" }, + {USTRING_PORTMAIN_02,(byte *)"Spawn of main task failed." }, +#endif +#endif /* (INCLUDE_PORTMAIN_STRINGS) */ +#if (INCLUDE_PORTKERN_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_PORTKERN_01,(byte *)L"Spawn of timer task failed. change priority " }, + {USTRING_PORTKERN_02,(byte *)L"Spawn of timer task failed. " }, + {USTRING_PORTKERN_03,(byte *)L"pc_report_error was called with error" }, + {USTRING_PORTKERN_04,(byte *)L"Exitting and restoring interrupt vectors" }, +#else + {USTRING_PORTKERN_01,(byte *)"Spawn of timer task failed. change priority " }, + {USTRING_PORTKERN_02,(byte *)"Spawn of timer task failed. " }, + {USTRING_PORTKERN_03,(byte *)"pc_report_error was called with error" }, + {USTRING_PORTKERN_04,(byte *)"Exitting and restoring interrupt vectors" }, +#endif +#endif /* (INCLUDE_PORTKERN_STRINGS) */ +#if (INCLUDE_RTFSDEM_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_RTFSDEM_01,(byte *)L"pc_ertfs_init failed" }, + {USTRING_RTFSDEM_07,(byte *)L"Invalid Drive number" }, + {USTRING_RTFSDEM_08,(byte *)L"Creating Subdirectory:" }, + {USTRING_RTFSDEM_09,(byte *)L"Creating Subdirectory " }, + {USTRING_RTFSDEM_10,(byte *)L"Creating Subdirectory " }, + {USTRING_RTFSDEM_11,(byte *)L"Removing Directory " }, + {USTRING_RTFSDEM_12,(byte *)L"-" }, + {USTRING_RTFSDEM_13,(byte *)L"Performing File io test" }, + {USTRING_RTFSDEM_14,(byte *)L"regress_error was called with error" }, + {USTRING_RTFSDEM_15,(byte *)L"Performing long file name test" }, + {USTRING_RTFSDEM_16,(byte *)L"Performing buffered file io test" }, + {USTRING_RTFSDEM_17,(byte *)L"Performing Large (4Gig)File io test" }, +#else + {USTRING_RTFSDEM_01,(byte *)"pc_ertfs_init failed" }, + {USTRING_RTFSDEM_07,(byte *)"Invalid Drive number" }, + {USTRING_RTFSDEM_08,(byte *)"Creating Subdirectory:" }, + {USTRING_RTFSDEM_09,(byte *)"Creating Subdirectory " }, + {USTRING_RTFSDEM_10,(byte *)"Creating Subdirectory " }, + {USTRING_RTFSDEM_11,(byte *)"Removing Directory " }, + {USTRING_RTFSDEM_12,(byte *)"-" }, + {USTRING_RTFSDEM_13,(byte *)"Performing File io test" }, + {USTRING_RTFSDEM_14,(byte *)"regress_error was called with error" }, + {USTRING_RTFSDEM_15,(byte *)"Performing long file name test" }, + {USTRING_RTFSDEM_16,(byte *)"Performing buffered file io test" }, + {USTRING_RTFSDEM_17,(byte *)"Performing Large (4Gig)File io test" }, +#endif +#endif /* (INCLUDE_RTFSDEM_STRINGS) */ +#if (INCLUDE_TSTSH_STRINGS) +#if (INCLUDE_CS_UNICODE) + {USTRING_TSTSH_01,(byte *)L"CD Memory init failed" }, + {USTRING_TSTSH_02,(byte *)L"Not implemented. See ide ioctl... " }, + {USTRING_TSTSH_03,(byte *)L"Set Default Drive Failed" }, + {USTRING_TSTSH_04,(byte *)L"Usage: DSKSELECT D: " }, + {USTRING_TSTSH_05,(byte *)L"No more random file slots " }, + {USTRING_TSTSH_06,(byte *)L"Cant open :" }, + {USTRING_TSTSH_07,(byte *)L"Usage: RNDOP D:PATH RECLEN" }, + {USTRING_TSTSH_08,(byte *)L"Cant find file" }, + {USTRING_TSTSH_09,(byte *)L"Close failed" }, + {USTRING_TSTSH_10,(byte *)L"Usage: CLOSE fd" }, + {USTRING_TSTSH_11,(byte *)L"Cant find file" }, + {USTRING_TSTSH_12,(byte *)L"Seek operation failed " }, + {USTRING_TSTSH_13,(byte *)L"Usage: SEEK fd recordno" }, + {USTRING_TSTSH_14,(byte *)L"Cant find file" }, + {USTRING_TSTSH_15,(byte *)L"Read operation failed " }, + {USTRING_TSTSH_16,(byte *)L"Usage: READ fd" }, + {USTRING_TSTSH_17,(byte *)L"Cant find file" }, + {USTRING_TSTSH_18,(byte *)L"Write operation failed " }, + {USTRING_TSTSH_19,(byte *)L"Usage: WRITE fd data " }, + {USTRING_TSTSH_20,(byte *)L"Usage: MKDIR D:PATH" }, + {USTRING_TSTSH_21,(byte *)L"Usage: RMDIR D:PATH" }, + {USTRING_TSTSH_22,(byte *)L"Usage: DELTREE D:PATH" }, + {USTRING_TSTSH_23,(byte *)L"deleting --> " }, + {USTRING_TSTSH_24,(byte *)L"Can not delete: " }, + {USTRING_TSTSH_25,(byte *)L"Usage: DELETE D:PATH" }, + {USTRING_TSTSH_26,(byte *)L"Usage: RENAME PATH NEWNAME" }, + {USTRING_TSTSH_27,(byte *)L"Set cwd failed" }, + {USTRING_TSTSH_28,(byte *)L"Get cwd failed" }, + {USTRING_TSTSH_29,(byte *)L"PWD Failed " }, + {USTRING_TSTSH_30,(byte *)L" " }, + {USTRING_TSTSH_31,(byte *)L" File(s) " }, + {USTRING_TSTSH_32,(byte *)L" Blocks Free " }, + {USTRING_TSTSH_33,(byte *)L"Cant open " }, + {USTRING_TSTSH_34,(byte *)L"Usage: CAT PATH" }, + {USTRING_TSTSH_35,(byte *)L"Usage: CHKDSK DRIVE: WRITECHAINS" }, + {USTRING_TSTSH_36,(byte *)L"Example:CHKDSK A: 1" }, + {USTRING_TSTSH_37,(byte *)L"Runs chkdsk on A: writes lost chains" }, + {USTRING_TSTSH_38,(byte *)L"Example:CHKDSK A: 0" }, + {USTRING_TSTSH_39,(byte *)L"Runs chkdsk on A: does not write lost chains" }, + {USTRING_TSTSH_40,(byte *)L"Cant open file: " }, + {USTRING_TSTSH_41,(byte *)L"Change size function failed" }, + {USTRING_TSTSH_42,(byte *)L"Usage: CHSIZE PATH NEWSIZE" }, + {USTRING_TSTSH_43,(byte *)L"Cant open file: " }, + {USTRING_TSTSH_44,(byte *)L"Cant open file" }, + {USTRING_TSTSH_45,(byte *)L"Write failure " }, + {USTRING_TSTSH_46,(byte *)L"Usage: COPY FROMPATH TOPATH" }, + {USTRING_TSTSH_47,(byte *)L"Cant open file: " }, + {USTRING_TSTSH_48,(byte *)L"Cant open file" }, + {USTRING_TSTSH_49,(byte *)L"Files are different" }, + {USTRING_TSTSH_50,(byte *)L"File: " }, + {USTRING_TSTSH_51,(byte *)L" And File: " }, + {USTRING_TSTSH_52,(byte *)L" are the same" }, + {USTRING_TSTSH_53,(byte *)L"File: " }, + {USTRING_TSTSH_54,(byte *)L" is larger than File: " }, + {USTRING_TSTSH_55,(byte *)L"Usage: DIFF PATH PATH " }, + {USTRING_TSTSH_56,(byte *)L"Cant open file" }, + {USTRING_TSTSH_57,(byte *)L"Write failure" }, + {USTRING_TSTSH_58,(byte *)L"Usage: FILLFILE PATH PATTERN NTIMES" }, + {USTRING_TSTSH_59,(byte *)L"MODE BITS :" }, + {USTRING_TSTSH_60,(byte *)L"S_IFDIR|" }, + {USTRING_TSTSH_61,(byte *)L"S_IFREG|" }, + {USTRING_TSTSH_62,(byte *)L"S_IWRITE|" }, + {USTRING_TSTSH_63,(byte *)L"S_IREAD" }, + {USTRING_TSTSH_65,(byte *)L"FSTAT failed" }, + {USTRING_TSTSH_66,(byte *)L"Usage: FSTAT D:PATH" }, + {USTRING_TSTSH_67,(byte *)L"Calling media format" }, + {USTRING_TSTSH_68,(byte *)L"The drive is contains this many logical blocks " }, + {USTRING_TSTSH_69,(byte *)L"This many logical blocks remain" }, + {USTRING_TSTSH_70,(byte *)L"The drive contains this many cylinders" }, + {USTRING_TSTSH_71,(byte *)L"There are this many cylinders remainng" }, + {USTRING_TSTSH_72,(byte *)L"Attributes: " }, + {USTRING_TSTSH_73,(byte *)L"ARDONLY|" }, + {USTRING_TSTSH_74,(byte *)L"AHIDDEN|" }, + {USTRING_TSTSH_75,(byte *)L"ASYSTEM|" }, + {USTRING_TSTSH_76,(byte *)L"AVOLUME|" }, + {USTRING_TSTSH_77,(byte *)L"ADIRENT|" }, + {USTRING_TSTSH_78,(byte *)L"ARCHIVE|" }, + {USTRING_TSTSH_79,(byte *)L"NORMAL FILE (No bits set)" }, + {USTRING_TSTSH_81,(byte *)L"get attributes failed" }, + {USTRING_TSTSH_82,(byte *)L"Usage: GETATTR D:PATH" }, + {USTRING_TSTSH_83,(byte *)L"Can not get attributes" }, + {USTRING_TSTSH_84,(byte *)L"RDONLY" }, + {USTRING_TSTSH_85,(byte *)L"HIDDEN" }, + {USTRING_TSTSH_86,(byte *)L"SYSTEM" }, + {USTRING_TSTSH_87,(byte *)L"ARCHIVE" }, + {USTRING_TSTSH_88,(byte *)L"NORMAL" }, + {USTRING_TSTSH_89,(byte *)L"Set attributes failed" }, + {USTRING_TSTSH_90,(byte *)L"Usage: SETATTR D:PATH RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL" }, + {USTRING_TSTSH_91,(byte *)L"Can not open disk " }, + {USTRING_TSTSH_92,(byte *)L"Usage: DSKOPEN D:" }, + {USTRING_TSTSH_93,(byte *)L"Usage: DSKCLOSE D: " }, + {USTRING_TSTSH_94,(byte *)L"cd_set_default failed" }, + {USTRING_TSTSH_95,(byte *)L"Usage: DSKSELECT D: " }, + {USTRING_TSTSH_96,(byte *)L"path error" }, + {USTRING_TSTSH_97,(byte *)L"Cant open File" }, + {USTRING_TSTSH_98,(byte *)L"Usage: CAT PATH" }, + {USTRING_TSTSH_99,(byte *)L"Cant open :" }, + {USTRING_TSTSH_100,(byte *)L"." }, + {USTRING_TSTSH_102,(byte *)L"Usage: READ PATH" }, + {USTRING_TSTSH_103,(byte *)L"Usage: COPY CDPATH OSPATH" }, + {USTRING_TSTSH_104,(byte *)L"Cant open " }, + {USTRING_TSTSH_105,(byte *)L"Cant creat " }, + {USTRING_TSTSH_106,(byte *)L"." }, + {USTRING_TSTSH_108,(byte *)L"Write error" }, + {USTRING_TSTSH_110,(byte *)L"Usage: REGRESSTEST D:" }, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_TSTSH_111, (byte *)L"Usage: FAILSAFEINIT D:"}, + {USTRING_TSTSH_112, (byte *)L"Usage: FAILSAFECOMMIT D:"}, + {USTRING_TSTSH_113,(byte *)L"Failsafe commit failed\n"}, + {USTRING_TSTSH_114,(byte *)L"Failsafe init failed\n"}, + {USTRING_TSTSH_115,(byte *)L"Failsafe already running on a drive\n"}, + {USTRING_TSTSH_116,(byte *)L"Command shell only supports one failsafe session\n"}, + {USTRING_TSTSH_117,(byte *)L"Usage: FAILSAFERESTORE D:"}, +#endif + {USTRING_TSTSH_118,(byte *)L"Failsafe disabled"}, + {USTRING_TSTSH_119,(byte *)L"Failsafe enabled with journaling "}, + {USTRING_TSTSH_120,(byte *)L"Failsafe enabled with no journaling "}, + {USTRING_TSTSH_121,(byte *)L"Total Number of Block Buffers "}, + {USTRING_TSTSH_122,(byte *)L"Pending writes on Block Buffers "}, + {USTRING_TSTSH_123,(byte *)L"Block Buffers available "}, + {USTRING_TSTSH_124,(byte *)L"Block Buffer Cache Hits "}, + {USTRING_TSTSH_125,(byte *)L"Block Buffer Cache Misses "}, + {USTRING_TSTSH_126,(byte *)L"Block Buffer Low Water "}, + {USTRING_TSTSH_127,(byte *)L"Block Allocation failures "}, + {USTRING_TSTSH_128,(byte *)L"Total Number of FAT Buffers "}, + {USTRING_TSTSH_129,(byte *)L"FAT Buffers never used "}, + {USTRING_TSTSH_130,(byte *)L"FAT Buffer Primary Cache Hits "}, + {USTRING_TSTSH_131,(byte *)L"FAT Buffer Secondary Cache Hits "}, + {USTRING_TSTSH_132,(byte *)L"FAT Buffer Secondary Cache Loads "}, + {USTRING_TSTSH_133,(byte *)L"FAT Buffer Secondary Cache Swaps "}, + {USTRING_TSTSH_134,(byte *)L"Pending writes on FAT Buffers "}, + {USTRING_TSTSH_135,(byte *)L"FAT Buffers available "}, + {USTRING_TSTSH_136,(byte *)L"pro_failsafe_status failed"}, + {USTRING_TSTSH_137,(byte *)L"USAGE: BUFFERSTAT D:"}, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_TSTSH_138,(byte *)L"FAILSAFE: Volume up to date, no restore required"}, + {USTRING_TSTSH_139,(byte *)L"FAILSAFE: no journal present"}, + {USTRING_TSTSH_140,(byte *)L"FAILSAFE: bad journal file present"}, + {USTRING_TSTSH_141,(byte *)L"FAILSAFE: journal file has checksum error"}, + {USTRING_TSTSH_142,(byte *)L"FAILSAFE: IO error during restore"}, + {USTRING_TSTSH_144,(byte *)L"FAILSAFE: Volume was restored sucessfully"}, + {USTRING_TSTSH_145,(byte *)L"FAILSAFE: Restore required"}, + {USTRING_TSTSH_146,(byte *)L"FAILSAFE: Restore unknown return code"}, +#endif + {USTRING_TSTSH_147,(byte *)L"Failsafe file blocks consumed "}, + {USTRING_TSTSH_148,(byte *)L"Failsafe file blocks still available "}, + + {USTRING_TSTSHHELP_01,(byte *)L"CAT PATH" }, + {USTRING_TSTSHHELP_02,(byte *)L"CHSIZE FILENAME NEWSIZE" }, + {USTRING_TSTSHHELP_03,(byte *)L"CD PATH or CD to display PWD " }, + {USTRING_TSTSHHELP_04,(byte *)L"CDCAT PATH" }, + {USTRING_TSTSHHELP_05,(byte *)L"CDCOPY PATH PATH" }, + {USTRING_TSTSHHELP_06,(byte *)L"CDREAD PATH" }, + {USTRING_TSTSHHELP_07,(byte *)L"CDCD PATH " }, + {USTRING_TSTSHHELP_08,(byte *)L"CDDIR PATH" }, + {USTRING_TSTSHHELP_09,(byte *)L"CDDSKCLOSE: D:" }, + {USTRING_TSTSHHELP_10,(byte *)L"CDDSKOPEN: D:" }, + {USTRING_TSTSHHELP_11,(byte *)L"CDDSKSEL D:" }, + {USTRING_TSTSHHELP_12,(byte *)L"CHKDSK D: <0,1> 1 is write lost chains" }, + {USTRING_TSTSHHELP_13,(byte *)L"CLOSE FDNO" }, + {USTRING_TSTSHHELP_14,(byte *)L"COPY PATH PATH" }, + {USTRING_TSTSHHELP_15,(byte *)L"DELETE PATH" }, + {USTRING_TSTSHHELP_16,(byte *)L"DELTREE PATH" }, + {USTRING_TSTSHHELP_17,(byte *)L"DEVINFO (Display Device Information)" }, + {USTRING_TSTSHHELP_18,(byte *)L"DIFF PATH PATH" }, + {USTRING_TSTSHHELP_19,(byte *)L"DIR PATH" }, + {USTRING_TSTSHHELP_20,(byte *)L"DSKSEL D:" }, + {USTRING_TSTSHHELP_21,(byte *)L"ECHO: [args]" }, + {USTRING_TSTSHHELP_22,(byte *)L"EJECT (ejects LS-120)" }, + {USTRING_TSTSHHELP_23,(byte *)L"FILLFILE PATH PATTERN NTIMES" }, + {USTRING_TSTSHHELP_24,(byte *)L"FORMAT (routine will prompt for arguments)" }, + {USTRING_TSTSHHELP_25,(byte *)L"GETATTR FILE" }, + {USTRING_TSTSHHELP_26,(byte *)L"HELP:" }, + {USTRING_TSTSHHELP_27,(byte *)L"LSTOPEN (lists all open file decscriptors) " }, + {USTRING_TSTSHHELP_28,(byte *)L"MKDIR PATH" }, + {USTRING_TSTSHHELP_29,(byte *)L"PCMCIAINT (Force a PCMCIA mgmt Interrupt)" }, + {USTRING_TSTSHHELP_30,(byte *)L"QUIT" }, + {USTRING_TSTSHHELP_31,(byte *)L"READ FDNO" }, + {USTRING_TSTSHHELP_32,(byte *)L"RENAME PATH NEWNAME" }, + {USTRING_TSTSHHELP_33,(byte *)L"RMDIR PATH" }, + {USTRING_TSTSHHELP_34,(byte *)L"RNDOP PATH RECLEN" }, + {USTRING_TSTSHHELP_35,(byte *)L"SEEK FDNO RECORD" }, + {USTRING_TSTSHHELP_36,(byte *)L"SETATTR D:PATH RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL" }, + {USTRING_TSTSHHELP_37,(byte *)L"STAT PATH" }, + {USTRING_TSTSHHELP_38,(byte *)L"WRITE FDNO QUOTED DATA" }, + {USTRING_TSTSHHELP_39,(byte *)L"REGRESSTEST D:"}, + {USTRING_TSTSHHELP_40,(byte *)L"MKHOSTDISK win9xpath"}, + {USTRING_TSTSHHELP_41 ,(byte *)L"MKROM"}, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_TSTSHHELP_42 ,(byte *)L"FAILSAFEINIT D:"}, + {USTRING_TSTSHHELP_43 ,(byte *)L"FAILSAFECOMMIT D:"}, + {USTRING_TSTSHHELP_44 ,(byte *)L"FAILSAFERESTORE D:"}, + {USTRING_TSTSHHELP_45 ,(byte *)L"BUFFERSTAT D:"}, + {USTRING_TSTSHHELP_46 ,(byte *)L"FAILSAFETEST D: (must be hostdisk)"}, +#endif /* (INCLUDE_FAILSAFE_CODE) */ + {USTRING_TSTSHCMD_01,(byte *)L"CAT" }, + {USTRING_TSTSHCMD_02,(byte *)L"CHSIZE" }, + {USTRING_TSTSHCMD_03,(byte *)L"CD" }, + {USTRING_TSTSHCMD_04,(byte *)L"CDCAT" }, + {USTRING_TSTSHCMD_05,(byte *)L"CDCOPY" }, + {USTRING_TSTSHCMD_06,(byte *)L"CDREAD" }, + {USTRING_TSTSHCMD_07,(byte *)L"CDCD" }, + {USTRING_TSTSHCMD_08,(byte *)L"CDDIR" }, + {USTRING_TSTSHCMD_09,(byte *)L"CDDSKCLOSE" }, + {USTRING_TSTSHCMD_10,(byte *)L"CDDSKOPEN" }, + {USTRING_TSTSHCMD_11,(byte *)L"CDDSKSEL" }, + {USTRING_TSTSHCMD_12,(byte *)L"CHKDSK" }, + {USTRING_TSTSHCMD_13,(byte *)L"CLOSE" }, + {USTRING_TSTSHCMD_14,(byte *)L"COPY" }, + {USTRING_TSTSHCMD_15,(byte *)L"DELETE" }, + {USTRING_TSTSHCMD_16,(byte *)L"DELTREE" }, + {USTRING_TSTSHCMD_17,(byte *)L"DEVINFO" }, + {USTRING_TSTSHCMD_18,(byte *)L"DIFF" }, + {USTRING_TSTSHCMD_19,(byte *)L"DIR" }, + {USTRING_TSTSHCMD_20,(byte *)L"DSKSEL" }, + {USTRING_TSTSHCMD_21,(byte *)L"ECHO" }, + {USTRING_TSTSHCMD_22,(byte *)L"EJECT" }, + {USTRING_TSTSHCMD_23,(byte *)L"FILLFILE" }, + {USTRING_TSTSHCMD_24,(byte *)L"FORMAT" }, + {USTRING_TSTSHCMD_25,(byte *)L"GETATTR" }, + {USTRING_TSTSHCMD_26,(byte *)L"HELP" }, + {USTRING_TSTSHCMD_27,(byte *)L"LSTOPEN" }, + {USTRING_TSTSHCMD_28,(byte *)L"MKDIR" }, + {USTRING_TSTSHCMD_29,(byte *)L"PCMCIAINT" }, + {USTRING_TSTSHCMD_30,(byte *)L"QUIT" }, + {USTRING_TSTSHCMD_31,(byte *)L"READ" }, + {USTRING_TSTSHCMD_32,(byte *)L"RENAME" }, + {USTRING_TSTSHCMD_33,(byte *)L"RMDIR" }, + {USTRING_TSTSHCMD_34,(byte *)L"RNDOP" }, + {USTRING_TSTSHCMD_35,(byte *)L"SEEK" }, + {USTRING_TSTSHCMD_36,(byte *)L"SETATTR" }, + {USTRING_TSTSHCMD_37,(byte *)L"STAT" }, + {USTRING_TSTSHCMD_38,(byte *)L"WRITE" }, + {USTRING_TSTSHCMD_39,(byte *)L"REGRESSTEST"}, + {USTRING_TSTSHCMD_40,(byte *)L"MKHOSTDISK"}, + {USTRING_TSTSHCMD_41,(byte *)L"MKROM"}, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_TSTSHCMD_42 ,(byte *)L"FAILSAFEINIT"}, + {USTRING_TSTSHCMD_43 ,(byte *)L"FAILSAFECOMMIT"}, + {USTRING_TSTSHCMD_44 ,(byte *)L"FAILSAFERESTORE"}, + {USTRING_TSTSHCMD_45 ,(byte *)L"BUFFERSTAT"}, + {USTRING_TSTSHCMD_46 ,(byte *)L"FAILSAFETEST"}, +#endif /* (INCLUDE_FAILSAFE_CODE) */ +#else + {USTRING_TSTSH_01,(byte *)"CD Memory init failed" }, + {USTRING_TSTSH_02,(byte *)"Not implemented. See ide ioctl... " }, + {USTRING_TSTSH_03,(byte *)"Set Default Drive Failed" }, + {USTRING_TSTSH_04,(byte *)"Usage: DSKSELECT D: " }, + {USTRING_TSTSH_05,(byte *)"No more random file slots " }, + {USTRING_TSTSH_06,(byte *)"Cant open :" }, + {USTRING_TSTSH_07,(byte *)"Usage: RNDOP D:PATH RECLEN" }, + {USTRING_TSTSH_08,(byte *)"Cant find file" }, + {USTRING_TSTSH_09,(byte *)"Close failed" }, + {USTRING_TSTSH_10,(byte *)"Usage: CLOSE fd" }, + {USTRING_TSTSH_11,(byte *)"Cant find file" }, + {USTRING_TSTSH_12,(byte *)"Seek operation failed " }, + {USTRING_TSTSH_13,(byte *)"Usage: SEEK fd recordno" }, + {USTRING_TSTSH_14,(byte *)"Cant find file" }, + {USTRING_TSTSH_15,(byte *)"Read operation failed " }, + {USTRING_TSTSH_16,(byte *)"Usage: READ fd" }, + {USTRING_TSTSH_17,(byte *)"Cant find file" }, + {USTRING_TSTSH_18,(byte *)"Write operation failed " }, + {USTRING_TSTSH_19,(byte *)"Usage: WRITE fd data " }, + {USTRING_TSTSH_20,(byte *)"Usage: MKDIR D:PATH" }, + {USTRING_TSTSH_21,(byte *)"Usage: RMDIR D:PATH" }, + {USTRING_TSTSH_22,(byte *)"Usage: DELTREE D:PATH" }, + {USTRING_TSTSH_23,(byte *)"deleting --> " }, + {USTRING_TSTSH_24,(byte *)"Can not delete: " }, + {USTRING_TSTSH_25,(byte *)"Usage: DELETE D:PATH" }, + {USTRING_TSTSH_26,(byte *)"Usage: RENAME PATH NEWNAME" }, + {USTRING_TSTSH_27,(byte *)"Set cwd failed" }, + {USTRING_TSTSH_28,(byte *)"Get cwd failed" }, + {USTRING_TSTSH_29,(byte *)"PWD Failed " }, + {USTRING_TSTSH_30,(byte *)" " }, + {USTRING_TSTSH_31,(byte *)" File(s) " }, + {USTRING_TSTSH_32,(byte *)" Blocks Free " }, + {USTRING_TSTSH_33,(byte *)"Cant open " }, + {USTRING_TSTSH_34,(byte *)"Usage: CAT PATH" }, + {USTRING_TSTSH_35,(byte *)"Usage: CHKDSK DRIVE: WRITECHAINS" }, + {USTRING_TSTSH_36,(byte *)"Example:CHKDSK A: 1" }, + {USTRING_TSTSH_37,(byte *)"Runs chkdsk on A: writes lost chains" }, + {USTRING_TSTSH_38,(byte *)"Example:CHKDSK A: 0" }, + {USTRING_TSTSH_39,(byte *)"Runs chkdsk on A: does not write lost chains" }, + {USTRING_TSTSH_40,(byte *)"Cant open file: " }, + {USTRING_TSTSH_41,(byte *)"Change size function failed" }, + {USTRING_TSTSH_42,(byte *)"Usage: CHSIZE PATH NEWSIZE" }, + {USTRING_TSTSH_43,(byte *)"Cant open file: " }, + {USTRING_TSTSH_44,(byte *)"Cant open file" }, + {USTRING_TSTSH_45,(byte *)"Write failure " }, + {USTRING_TSTSH_46,(byte *)"Usage: COPY FROMPATH TOPATH" }, + {USTRING_TSTSH_47,(byte *)"Cant open file: " }, + {USTRING_TSTSH_48,(byte *)"Cant open file" }, + {USTRING_TSTSH_49,(byte *)"Files are different" }, + {USTRING_TSTSH_50,(byte *)"File: " }, + {USTRING_TSTSH_51,(byte *)" And File: " }, + {USTRING_TSTSH_52,(byte *)" are the same" }, + {USTRING_TSTSH_53,(byte *)"File: " }, + {USTRING_TSTSH_54,(byte *)" is larger than File: " }, + {USTRING_TSTSH_55,(byte *)"Usage: DIFF PATH PATH " }, + {USTRING_TSTSH_56,(byte *)"Cant open file" }, + {USTRING_TSTSH_57,(byte *)"Write failure" }, + {USTRING_TSTSH_58,(byte *)"Usage: FILLFILE PATH PATTERN NTIMES" }, + {USTRING_TSTSH_59,(byte *)"MODE BITS :" }, + {USTRING_TSTSH_60,(byte *)"S_IFDIR|" }, + {USTRING_TSTSH_61,(byte *)"S_IFREG|" }, + {USTRING_TSTSH_62,(byte *)"S_IWRITE|" }, + {USTRING_TSTSH_63,(byte *)"S_IREAD" }, + {USTRING_TSTSH_65,(byte *)"FSTAT failed" }, + {USTRING_TSTSH_66,(byte *)"Usage: FSTAT D:PATH" }, + {USTRING_TSTSH_67,(byte *)"Calling media format" }, + {USTRING_TSTSH_68,(byte *)"The drive is contains this many logical blocks " }, + {USTRING_TSTSH_69,(byte *)"This many logical blocks remain" }, + {USTRING_TSTSH_70,(byte *)"The drive contains this many cylinders" }, + {USTRING_TSTSH_71,(byte *)"There are this many cylinders remainng" }, + {USTRING_TSTSH_72,(byte *)"Attributes: " }, + {USTRING_TSTSH_73,(byte *)"ARDONLY|" }, + {USTRING_TSTSH_74,(byte *)"AHIDDEN|" }, + {USTRING_TSTSH_75,(byte *)"ASYSTEM|" }, + {USTRING_TSTSH_76,(byte *)"AVOLUME|" }, + {USTRING_TSTSH_77,(byte *)"ADIRENT|" }, + {USTRING_TSTSH_78,(byte *)"ARCHIVE|" }, + {USTRING_TSTSH_79,(byte *)"NORMAL FILE (No bits set)" }, + {USTRING_TSTSH_81,(byte *)"get attributes failed" }, + {USTRING_TSTSH_82,(byte *)"Usage: GETATTR D:PATH" }, + {USTRING_TSTSH_83,(byte *)"Can not get attributes" }, + {USTRING_TSTSH_84,(byte *)"RDONLY" }, + {USTRING_TSTSH_85,(byte *)"HIDDEN" }, + {USTRING_TSTSH_86,(byte *)"SYSTEM" }, + {USTRING_TSTSH_87,(byte *)"ARCHIVE" }, + {USTRING_TSTSH_88,(byte *)"NORMAL" }, + {USTRING_TSTSH_89,(byte *)"Set attributes failed" }, + {USTRING_TSTSH_90,(byte *)"Usage: SETATTR D:PATH RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL" }, + {USTRING_TSTSH_91,(byte *)"Can not open disk " }, + {USTRING_TSTSH_92,(byte *)"Usage: DSKOPEN D:" }, + {USTRING_TSTSH_93,(byte *)"Usage: DSKCLOSE D: " }, + {USTRING_TSTSH_94,(byte *)"cd_set_default failed" }, + {USTRING_TSTSH_95,(byte *)"Usage: DSKSELECT D: " }, + {USTRING_TSTSH_96,(byte *)"path error" }, + {USTRING_TSTSH_97,(byte *)"Cant open File" }, + {USTRING_TSTSH_98,(byte *)"Usage: CAT PATH" }, + {USTRING_TSTSH_99,(byte *)"Cant open :" }, + {USTRING_TSTSH_100,(byte *)"." }, + {USTRING_TSTSH_102,(byte *)"Usage: READ PATH" }, + {USTRING_TSTSH_103,(byte *)"Usage: COPY CDPATH OSPATH" }, + {USTRING_TSTSH_104,(byte *)"Cant open " }, + {USTRING_TSTSH_105,(byte *)"Cant creat " }, + {USTRING_TSTSH_106,(byte *)"." }, + {USTRING_TSTSH_108,(byte *)"Write error" }, + {USTRING_TSTSH_110,(byte *)"Usage: REGRESSTEST D:" }, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_TSTSH_111,(byte *)"Usage: FAILSAFEINIT D:"}, + {USTRING_TSTSH_112,(byte *)"Usage: FAILSAFECOMMIT D:"}, + {USTRING_TSTSH_113,(byte *)"Failsafe commit failed\n"}, + {USTRING_TSTSH_114,(byte *)"Failsafe init failed\n"}, + {USTRING_TSTSH_115,(byte *)"Failsafe already running on a drive\n"}, + {USTRING_TSTSH_116,(byte *)"Command shell only supports one failsafe session\n"}, + {USTRING_TSTSH_117,(byte *)"Usage: FAILSAFERESTORE D:"}, +#endif + {USTRING_TSTSH_118,(byte *)"Failsafe disabled"}, + {USTRING_TSTSH_119,(byte *)"Failsafe enabled with journaling "}, + {USTRING_TSTSH_120,(byte *)"Failsafe enabled with no journaling"}, + {USTRING_TSTSH_121,(byte *)"Total Number of Block Buffers "}, + {USTRING_TSTSH_122,(byte *)"Pending writes on Block Buffers "}, + {USTRING_TSTSH_123,(byte *)"Block Buffers available "}, + {USTRING_TSTSH_124,(byte *)"Block Buffer Cache Hits "}, + {USTRING_TSTSH_125,(byte *)"Block Buffer Cache Misses "}, + {USTRING_TSTSH_126,(byte *)"Block Buffer Low Water "}, + {USTRING_TSTSH_127,(byte *)"Block Allocation failures "}, + {USTRING_TSTSH_128,(byte *)"Total Number of FAT Buffers "}, + {USTRING_TSTSH_129,(byte *)"FAT Buffers never used "}, + {USTRING_TSTSH_130,(byte *)"FAT Buffer Primary Cache Hits "}, + {USTRING_TSTSH_131,(byte *)"FAT Buffer Secondary Cache Hits "}, + {USTRING_TSTSH_132,(byte *)"FAT Buffer Secondary Cache Loads "}, + {USTRING_TSTSH_133,(byte *)"FAT Buffer Secondary Cache Swaps "}, + {USTRING_TSTSH_134,(byte *)"Pending writes on FAT Buffers "}, + {USTRING_TSTSH_135,(byte *)"FAT Buffers available "}, + {USTRING_TSTSH_136,(byte *)"pro_failsafe_status failed"}, + {USTRING_TSTSH_137,(byte *)"USAGE: BUFFERSTAT D:"}, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_TSTSH_138,(byte *)"FAILSAFE: Volume up to date, no restore required"}, + {USTRING_TSTSH_139,(byte *)"FAILSAFE: no journal present"}, + {USTRING_TSTSH_140,(byte *)"FAILSAFE: bad journal file present"}, + {USTRING_TSTSH_141,(byte *)"FAILSAFE: journal file has checksum error"}, + {USTRING_TSTSH_142,(byte *)"FAILSAFE: IO error during restore"}, + {USTRING_TSTSH_144,(byte *)"FAILSAFE: Volume was restored sucessfully"}, + {USTRING_TSTSH_145,(byte *)"FAILSAFE: Restore required"}, + {USTRING_TSTSH_146,(byte *)"FAILSAFE: Restore unknown return code"}, +#endif + {USTRING_TSTSH_147,(byte *)"Failsafe file blocks consumed "}, + {USTRING_TSTSH_148,(byte *)"Failsafe file blocks still available "}, + + {USTRING_TSTSHHELP_01,(byte *)"CAT PATH" }, + {USTRING_TSTSHHELP_02,(byte *)"CHSIZE FILENAME NEWSIZE" }, + {USTRING_TSTSHHELP_03,(byte *)"CD PATH or CD to display PWD " }, + {USTRING_TSTSHHELP_04,(byte *)"CDCAT PATH" }, + {USTRING_TSTSHHELP_05,(byte *)"CDCOPY PATH PATH" }, + {USTRING_TSTSHHELP_06,(byte *)"CDREAD PATH" }, + {USTRING_TSTSHHELP_07,(byte *)"CDCD PATH " }, + {USTRING_TSTSHHELP_08,(byte *)"CDDIR PATH" }, + {USTRING_TSTSHHELP_09,(byte *)"CDDSKCLOSE: D:" }, + {USTRING_TSTSHHELP_10,(byte *)"CDDSKOPEN: D:" }, + {USTRING_TSTSHHELP_11,(byte *)"CDDSKSEL D:" }, + {USTRING_TSTSHHELP_12,(byte *)"CHKDSK D: <0,1> 1 is write lost chains" }, + {USTRING_TSTSHHELP_13,(byte *)"CLOSE FDNO" }, + {USTRING_TSTSHHELP_14,(byte *)"COPY PATH PATH" }, + {USTRING_TSTSHHELP_15,(byte *)"DELETE PATH" }, + {USTRING_TSTSHHELP_16,(byte *)"DELTREE PATH" }, + {USTRING_TSTSHHELP_17,(byte *)"DEVINFO (Display Device Information)" }, + {USTRING_TSTSHHELP_18,(byte *)"DIFF PATH PATH" }, + {USTRING_TSTSHHELP_19,(byte *)"DIR PATH" }, + {USTRING_TSTSHHELP_20,(byte *)"DSKSEL D:" }, + {USTRING_TSTSHHELP_21,(byte *)"ECHO: [args]" }, + {USTRING_TSTSHHELP_22,(byte *)"EJECT (ejects LS-120)" }, + {USTRING_TSTSHHELP_23,(byte *)"FILLFILE PATH PATTERN NTIMES" }, + {USTRING_TSTSHHELP_24,(byte *)"FORMAT (routine will prompt for arguments)" }, + {USTRING_TSTSHHELP_25,(byte *)"GETATTR FILE" }, + {USTRING_TSTSHHELP_26,(byte *)"HELP:" }, + {USTRING_TSTSHHELP_27,(byte *)"LSTOPEN (lists all open file decscriptors) " }, + {USTRING_TSTSHHELP_28,(byte *)"MKDIR PATH" }, + {USTRING_TSTSHHELP_29,(byte *)"PCMCIAINT (Force a PCMCIA mgmt Interrupt)" }, + {USTRING_TSTSHHELP_30,(byte *)"QUIT" }, + {USTRING_TSTSHHELP_31,(byte *)"READ FDNO" }, + {USTRING_TSTSHHELP_32,(byte *)"RENAME PATH NEWNAME" }, + {USTRING_TSTSHHELP_33,(byte *)"RMDIR PATH" }, + {USTRING_TSTSHHELP_34,(byte *)"RNDOP PATH RECLEN" }, + {USTRING_TSTSHHELP_35,(byte *)"SEEK FDNO RECORD" }, + {USTRING_TSTSHHELP_36,(byte *)"SETATTR D:PATH RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL" }, + {USTRING_TSTSHHELP_37,(byte *)"STAT PATH" }, + {USTRING_TSTSHHELP_38,(byte *)"WRITE FDNO QUOTED DATA" }, + {USTRING_TSTSHHELP_39,(byte *)"REGRESSTEST D:"}, + {USTRING_TSTSHHELP_40,(byte *)"MKHOSTDISK win9xpath"}, + {USTRING_TSTSHHELP_41 ,(byte *)"MKROM"}, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_TSTSHHELP_42 ,(byte *)"FAILSAFEINIT D:"}, + {USTRING_TSTSHHELP_43 ,(byte *)"FAILSAFECOMMIT D:"}, + {USTRING_TSTSHHELP_44 ,(byte *)"FAILSAFERESTORE D:"}, + {USTRING_TSTSHHELP_45 ,(byte *)"BUFFERSTAT D:"}, + {USTRING_TSTSHHELP_46 ,(byte *)"FAILSAFETEST D: (must be hostdisk)"}, +#endif /* (INCLUDE_FAILSAFE_CODE) */ + {USTRING_TSTSHCMD_01,(byte *)"CAT" }, + {USTRING_TSTSHCMD_02,(byte *)"CHSIZE" }, + {USTRING_TSTSHCMD_03,(byte *)"CD" }, + {USTRING_TSTSHCMD_04,(byte *)"CDCAT" }, + {USTRING_TSTSHCMD_05,(byte *)"CDCOPY" }, + {USTRING_TSTSHCMD_06,(byte *)"CDREAD" }, + {USTRING_TSTSHCMD_07,(byte *)"CDCD" }, + {USTRING_TSTSHCMD_08,(byte *)"CDDIR" }, + {USTRING_TSTSHCMD_09,(byte *)"CDDSKCLOSE" }, + {USTRING_TSTSHCMD_10,(byte *)"CDDSKOPEN" }, + {USTRING_TSTSHCMD_11,(byte *)"CDDSKSEL" }, + {USTRING_TSTSHCMD_12,(byte *)"CHKDSK" }, + {USTRING_TSTSHCMD_13,(byte *)"CLOSE" }, + {USTRING_TSTSHCMD_14,(byte *)"COPY" }, + {USTRING_TSTSHCMD_15,(byte *)"DELETE" }, + {USTRING_TSTSHCMD_16,(byte *)"DELTREE" }, + {USTRING_TSTSHCMD_17,(byte *)"DEVINFO" }, + {USTRING_TSTSHCMD_18,(byte *)"DIFF" }, + {USTRING_TSTSHCMD_19,(byte *)"DIR" }, + {USTRING_TSTSHCMD_20,(byte *)"DSKSEL" }, + {USTRING_TSTSHCMD_21,(byte *)"ECHO" }, + {USTRING_TSTSHCMD_22,(byte *)"EJECT" }, + {USTRING_TSTSHCMD_23,(byte *)"FILLFILE" }, + {USTRING_TSTSHCMD_24,(byte *)"FORMAT" }, + {USTRING_TSTSHCMD_25,(byte *)"GETATTR" }, + {USTRING_TSTSHCMD_26,(byte *)"HELP" }, + {USTRING_TSTSHCMD_27,(byte *)"LSTOPEN" }, + {USTRING_TSTSHCMD_28,(byte *)"MKDIR" }, + {USTRING_TSTSHCMD_29,(byte *)"PCMCIAINT" }, + {USTRING_TSTSHCMD_30,(byte *)"QUIT" }, + {USTRING_TSTSHCMD_31,(byte *)"READ" }, + {USTRING_TSTSHCMD_32,(byte *)"RENAME" }, + {USTRING_TSTSHCMD_33,(byte *)"RMDIR" }, + {USTRING_TSTSHCMD_34,(byte *)"RNDOP" }, + {USTRING_TSTSHCMD_35,(byte *)"SEEK" }, + {USTRING_TSTSHCMD_36,(byte *)"SETATTR" }, + {USTRING_TSTSHCMD_37,(byte *)"STAT" }, + {USTRING_TSTSHCMD_38,(byte *)"WRITE" }, + {USTRING_TSTSHCMD_39,(byte *)"REGRESSTEST"}, + {USTRING_TSTSHCMD_40,(byte *)"MKHOSTDISK"}, + {USTRING_TSTSHCMD_41,(byte *)"MKROM"}, +#if (INCLUDE_FAILSAFE_CODE) + {USTRING_TSTSHCMD_42 ,(byte *)"FAILSAFEINIT"}, + {USTRING_TSTSHCMD_43 ,(byte *)"FAILSAFECOMMIT"}, + {USTRING_TSTSHCMD_44 ,(byte *)"FAILSAFERESTORE"}, + {USTRING_TSTSHCMD_45 ,(byte *)"BUFFERSTAT"}, + {USTRING_TSTSHCMD_46 ,(byte *)"FAILSAFETEST"}, +#endif /* (INCLUDE_FAILSAFE_CODE) */ + +#endif +#endif /* (INCLUDE_TSTSH_STRINGS) */ +#if (INCLUDE_CS_UNICODE) + {0,(byte *)L"Unkown User String" } +#else + {0,(byte *)"Unkown User String" } +#endif + }; + + + +static byte *rtfs_strtab_string(KS_CONSTANT RTFS_STRING_TABLE *ptable, int string_id) +{ + while (ptable->string_id != 0) + { + if (ptable->string_id == string_id) + break; + ptable++; + } + /* Return prompt. If lookup failed return Unkown User Prompt */ + return((byte *)ptable->string_value); +} + +byte *rtfs_strtab_user_prompt(int prompt_id) +{ + return(rtfs_strtab_string(prompt_table, prompt_id)); +} + +byte *rtfs_strtab_user_string(int string_id) +{ + return(rtfs_strtab_string(string_table, string_id)); +} + diff --git a/build/libraries/fatfs/ARM7/csunicod.c b/build/libraries/fatfs/ARM7/csunicod.c new file mode 100644 index 0000000..0cf842f --- /dev/null +++ b/build/libraries/fatfs/ARM7/csunicod.c @@ -0,0 +1,723 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 2002 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* UNICODE.C - Contains UNCICODE string manipulation and character conversion routines */ + + +#include + +#if (INCLUDE_CS_UNICODE) + +BOOLEAN _illegal_alias_char(byte ch); +#if (KS_LITTLE_ENDIAN) +#define BYTE_LOW 1 /* Numeric low byte of a character */ +#define BYTE_HIGH 0 /* Numeric high byte of a character */ +#else +#define BYTE_LOW 0 /* Numeric low byte of a character */ +#define BYTE_HIGH 1 /* Numeric high byte of a character */ +#endif + +void pc_ascii_byte2upper(byte *to, byte *from) +{ +byte c; + c = *from; + if ((c >= 'a') && (c <= 'z')) + c = (byte) ('A' + c - 'a'); + *to = c; +} + +void pc_unicode_byte2upper(byte *to, byte *from) /* __fn__*/ +{ +byte c; + if (*(from+BYTE_LOW)==0) + { + c = *(from+BYTE_HIGH); + if ((c >= 'a') && (c <= 'z')) + c = (byte) ('A' + c - 'a'); + *(to+BYTE_LOW)= 0; + *(to+BYTE_HIGH)= c; + } + else + { + *to++ = *from++; + *to = *from; + } +} + + +int unicode_ascii_index(byte *p, byte base) +{ +byte c[2]; +int index; + + pc_unicode_byte2upper(c, p); + if (c[BYTE_LOW]==0) + { + index = (int) (c[BYTE_HIGH] - base); + } + else + { + index = c[BYTE_HIGH]; + index <<= 8; + index += c[BYTE_LOW]; + } + return(index); +} + +int unicode_compare(byte *p1, byte *p2) +{ + if (*p1 == *p2 && *(p1+1) == *(p2+1)) + return(1); + else + return(0); +} + +int ascii_compare_nc(byte *p1, byte *p2) +{ +byte c,d; + if (*p1 == *p2) + return(1); + else + { + pc_ascii_byte2upper(&c, p1); + pc_ascii_byte2upper(&d, p2); + return(c == d); + } +} + +int unicode_compare_nc(byte *p1, byte *p2) +{ +byte cp1[2]; +byte cp2[2]; + pc_unicode_byte2upper(cp1,p1); + pc_unicode_byte2upper(cp2,p2); + if (unicode_compare(cp1, cp2)) + return(1); + else + return(0); +} + +byte *unicode_goto_eos(byte *p) +{ + while ((*p) || *(p+1)) p+=2; + return(p); +} + +void unicode_drno_to_letter(byte *p,int driveno) +{ + driveno += (int) 'A'; + *(p+BYTE_LOW) = 0; + *(p+BYTE_HIGH) = (byte) driveno; +} +int unicode_cmp_to_ascii_char(byte *p, byte c) +{ + if (*(p+BYTE_LOW) == 0) + return ( c ==*(p+BYTE_HIGH) ); + return(0); +} + +void unicode_assign_ascii_char(byte *p, byte c) +{ + *(p+BYTE_LOW) = 0; + *(p+BYTE_HIGH) = c; +} + +void map_ascii_to_unicode(byte *unicode_to, byte *ascii_from) +{ +byte *p; + + p = unicode_to; + while (*ascii_from) + { + *(p+BYTE_LOW) = 0; + *(p+BYTE_HIGH) = *ascii_from++; + p+= 2; + } + *p++ = 0; + *p++ = 0; +} + +void map_unicode_to_ascii(byte *to, byte *from) +{ + while (*from || *(from+1)) + { + if (*(from+BYTE_LOW) == 0) + *to++ = *(from+BYTE_HIGH); + else + *to++ = '_'; + from += 2; + } + *to = 0; +} + +void pc_ascii_strn2upper(byte *to, byte *from, int n) /* __fn__*/ +{ + int i; + byte c; + for (i = 0; i < n; i++) + { + c = *from++; + if ((c >= 'a') && (c <= 'z')) + c = (byte) ('A' + c - 'a'); + *to++ = c; + } +} + +void pc_ascii_str2upper(byte *to, byte *from) /* __fn__*/ +{ + byte c; + while(*from) + { + c = *from++; + if ((c >= 'a') && (c <= 'z')) + c = (byte) ('A' + c - 'a'); + *to++ = c; + } + *to = '\0'; +} + +byte * rtfs_cs_strcat(byte * targ, byte * src) /*__fn__*/ +{ +byte *p; + p = unicode_goto_eos(targ); + rtfs_cs_strcpy(p, src); + return targ; +} + + +int rtfs_cs_strcmp(byte * s1, byte * s2) +{ + word w1, w2; + + while (CS_OP_IS_NOT_EOS(s1) && CS_OP_IS_NOT_EOS(s2) && unicode_compare(s1, s2)) + { + s1 += 2; + s2 += 2; + } + + w1 = (word)*(s1+BYTE_HIGH); w1 = (word) w1 << 8; w1 = (word)w1 + (word) *(s1+BYTE_LOW); + w2 = (word)*(s2+BYTE_HIGH); w2 = (word) w2 << 8; w2 = (word)w2 + (word) *(s2+BYTE_LOW); + + if (w1 == w2) return 0; + else if (w1 < w2) return -1; + else return 1; +} + +int rtfs_cs_strcpy(byte * targ, byte * src) +{ +int loop_count = 0; + while (CS_OP_IS_NOT_EOS(src)) + { + *targ++ = *src++; + *targ++ = *src++; + loop_count++; + } + *targ++ = 0; + *targ = 0; + return (loop_count); +} + +/* return number of unicode chars in a string */ +int rtfs_cs_strlen(byte * string) /*__fn__*/ +{ +int len; + len = 0; + while (CS_OP_IS_NOT_EOS(string)) + { + len += 1; + string += 2; + } + return (len); +} + +BOOLEAN validate_filename(byte * name, byte * ext) +{ + int len; + byte *pa, *pu, uni_buffer[16], ascii_buffer[8]; + + RTFS_ARGSUSED_PVOID((void *) ext); + + /* Check short filenames to see if they are reserved names, The test + is done in ascii because we use this for aliases */ + len = 0; + pu = uni_buffer; pa = name; + /* Make a unicode string up to DOT or Zero */ + while (CS_OP_IS_NOT_EOS(pa)) + { + if (CS_OP_CMP_ASCII(pa,'.')) + break; + CS_OP_CP_CHR(pu, pa); + CS_OP_INC_PTR(pu); + CS_OP_INC_PTR(pa); + CS_OP_TERM_STRING(pu); + len += 1; + if (len > 4) + break; + } + if (len && len <= 4) + { + map_unicode_to_ascii(ascii_buffer, uni_buffer); + if(name_is_reserved(ascii_buffer)) return(FALSE); + } + + if (*(name+BYTE_LOW)==0) + if (*(name+BYTE_HIGH) == ' ') + return(FALSE); + len = 1; + name += 2; + while (CS_OP_IS_NOT_EOS(name)) + { + if (*(name+BYTE_LOW)==0) + if (_illegal_lfn_char(*(name+BYTE_HIGH))) + return(FALSE); + name += 2; + len += 1; + } + if(len > 255) return(FALSE); + return(TRUE); +} + +BOOLEAN pc_ascii_malias(byte *alias, byte *input_file, int try); + +/* UNICODE Version */ +BOOLEAN pc_cs_malias(byte *alias, byte *input_file, int try) /*__fn__*/ +{ + BLKBUFF *scratch; + BLKBUFF *scratch1; + byte *ascii_input_file; + byte *ascii_alias; + BOOLEAN ret_val = FALSE; + + /* We know UNICODE file names aren't legal aliases */ + if(try == -1) + return(FALSE); + + scratch = scratch1 = 0; + + scratch = pc_scratch_blk(); + scratch1 = pc_scratch_blk(); + if (!scratch || !scratch1) + goto ex_it; + ascii_input_file = scratch->data; + ascii_alias = scratch1->data; + + /* Map the unicode to ascii and create an ascii alias */ + map_unicode_to_ascii(ascii_input_file, input_file); + if (!pc_ascii_malias(ascii_alias, ascii_input_file, try)) + goto ex_it; + /* Now back to unicode */ + map_ascii_to_unicode(alias, ascii_alias); + ret_val = TRUE; +ex_it: + if (scratch) + pc_free_scratch_blk(scratch); + if (scratch1) + pc_free_scratch_blk(scratch1); + return(ret_val); +} + + +BOOLEAN pc_patcmp_8(byte *p, byte *pattern, BOOLEAN dowildcard) /* __fn__*/ +{ +byte ascii_pattern[9]; +int size = 8; + + map_unicode_to_ascii(ascii_pattern, pattern); + pattern = ascii_pattern; + /* Kludge. never match a deleted file */ + if (*p == PCDELETE) + return (FALSE); + else if (*pattern == PCDELETE) /* But E5 in the Pattern matches 0x5 */ + { + if (*p == 0x5) + { + size -= 1; + p++; + pattern++; + } + else + return (FALSE); + } + + while (size--) + { + if(dowildcard) + { + if (*pattern == '*') /* '*' matches the rest of the name */ + return (TRUE); + if (*pattern != '?' && !ascii_compare_nc(pattern,p)) + return (FALSE); + } + else + { + if (!ascii_compare_nc(pattern,p)) + return (FALSE); + } + p++; + pattern++; + } + return (TRUE); +} + +BOOLEAN pc_patcmp_3(byte *p, byte *pattern, BOOLEAN dowildcard) /* __fn__*/ +{ +byte ascii_pattern[9]; +int size = 3; + + map_unicode_to_ascii(ascii_pattern, pattern); + pattern = ascii_pattern; + + while (size--) + { + if(dowildcard) + { + if (*pattern == '*') /* '*' matches the rest of the name */ + return (TRUE); + if (*pattern != '?' && !ascii_compare_nc(pattern,p)) + return (FALSE); + } + else + { + if (!ascii_compare_nc(pattern,p)) + return (FALSE); + } + p++; + pattern++; + } + return (TRUE); +} + +void lfn_chr_to_unicode(byte *to, byte *fr) +{ +#if (KS_LITTLE_ENDIAN) + *to = *fr++; + *(to+1) = *fr; +#else /* make sure UNICODE str is in Intel byte order on disk */ + *(to+1) = *fr++; + *to = *fr; +#endif +} +void unicode_chr_to_lfn(byte *to, byte *fr) +{ +#if (KS_LITTLE_ENDIAN) + *to = *fr++; + *(to+1) = *fr; +#else /* make sure UNICODE str is in Intel byte order on disk */ + *(to+1) = *fr++; + *to = *fr; +#endif +} + +byte *pc_ascii_mfile(byte *to, byte *filename, byte *ext); + + +/* Version of MFILE that converts path from byte orientation to + native char set before returning. */ +byte *pc_cs_mfile(byte *to, byte *filename, byte *ext) +{ +byte temp_to[13]; + pc_ascii_mfile(temp_to, filename, ext); + map_ascii_to_unicode(to, temp_to); + return(to); +} + +/*************************************************************************** +PC_FILEPARSE - Parse a file xxx.yyy into filename/pathname + + Description + Take a file named XXX.YY and return SPACE padded NULL terminated + filename [XXX ] and fileext [YY ] components. If the name or ext are + less than [8,3] characters the name/ext is space filled and null termed. + If the name/ext is greater than [8,3] the name/ext is truncated. '.' + is used to seperate file from ext, the special cases of . and .. are + also handled. + Returns + Returns TRUE + + ****************************************************************************/ + /* UNICODE - Called by pc_malias not by others if vfat - Okay as ascii */ + /* UNICODE - pc_enum usage is probably incorrect */ + /* Take a string xxx[.yy] and put it into filename and fileext */ + /* Note: add a check legal later */ +BOOLEAN pc_ascii_fileparse(byte *filename, byte *fileext, byte *p) /* __fn__*/ +{ + int i; + + /* Defaults */ + rtfs_memset(filename, ' ', 8); + filename[8] = '\0'; + rtfs_memset(fileext, ' ', 3); + fileext[3] = '\0'; + + /* Special cases of . and .. */ + if (*p == '.') + { + *filename = '.'; + if (*(p+1) == '.') + { + *(++filename) = '.'; + return (TRUE); + } + else if (*(p + 1) == '\0') + return (TRUE); + else + return (FALSE); + } + + i = 0; + while (*p) + { + if (*p == '.') + { + p++; + break; + } + else + if (i++ < 8) + *filename++ = *p; + p++; + } + + i = 0; + while (*p) + { + if (i++ < 3) + *fileext++ = *p; + p++; + } + return (TRUE); +} + + +/*************************************************************************** + PC_MFILE - Build a file spec (xxx.yyy) from a file name and extension + + Description + Fill in to with a concatenation of file and ext. File and ext are + not assumed to be null terminated but must be blank filled to [8,3] + chars respectively. 'to' will be a null terminated string file.ext. + + ASCII character function only. Unicode not required + + Returns + A pointer to 'to'. +This is byte oriented. See pc_cs_mfile() for unicode oriented. +****************************************************************************/ +byte *pc_ascii_mfile(byte *to, byte *filename, byte *ext) +{ + byte *p; + int i; + byte *retval = to; + + p = filename; + i = 0; + while(*p) + { + if (*p == ' ') + break; + else + { + *to++ = *p++; + i++; + } + if (i == 8) + break; + } + if (p != filename) + { + p = ext; + if (*p && *p != ' ') + *to++ = '.'; + i = 0; + while(p && *p) + { + if (*p == ' ') + break; + + else + { + *to++ = *p++; + i++; + } + if (i == 3) + break; + } + } + *to = '\0'; + return (retval); +} +BOOLEAN pc_valid_ascii_sfn(byte *filename); + +BOOLEAN pc_ascii_malias(byte *alias, byte *input_file, int try) +{ + int n,i,s; + byte filename[9],fileext[4]; + + /* Fill filename[8] with spaces before we start. */ + rtfs_memset(filename, ' ', 8); + + /* See if no change necessary! */ + if(try == -1) + return(pc_valid_ascii_sfn((byte *)input_file)); + /* Process the ASCII alias name */ + while(*input_file=='.' || *input_file==' ') input_file++; + + /* find extension start */ + for(n=0,i=0; input_file[n]!=0; n++) /* i holds the position right */ + { /* after the last period */ + if(input_file[n]=='.') i=n+1; + } + if(i>0 && input_file[i]!=0){ + /* copy extension to fileext[] */ + for(n=i,s=0; input_file[n]!=0 && s<3; n++) + { + if(input_file[n]!=' ') + { + if(_illegal_alias_char(input_file[n])) + { + fileext[s++] = '_'; + } + else + fileext[s++] = input_file[n]; + } + } + fileext[s]=0;} else { i=512; fileext[0]=0; } /* null terminate */ + + /* copy file name to filename[], filtering out spaces, periods and + replacing characters illegal in alias names with '_' */ + for(n=0,s=0; n127) + { + filename[s++] = '_'; + } + else + filename[s++] = input_file[n]; + } + } + + for(;s<8;s++) /* pad with spaces */ + { + filename[s] = ' '; + } + filename[8]=0; /* null terminate filename[] */ + + pc_ascii_str2upper(filename,filename); + pc_ascii_str2upper(fileext,fileext); + + /* append (TEXT[])i to filename[] */ + for(n=7,s=try; s>0 && n>0; s/=10,n--) + { + filename[n] = (byte)(((byte)s%10)+'0'); + } + if(n==0 && s>0) + return(FALSE); + else + filename[n]='~'; + + /* copy filename[] to alias[], filtering out spaces */ + for(n=0,s=0; s<8; s++) + { + if(filename[s]!=' ') + alias[n++]=filename[s]; + } + if(fileext[0] != 0) + { + alias[n++]='.'; /* insert separating period */ + /* copy fileext[] to alias[] */ + for(s=0; fileext[s]!=0; s++,n++) + { + alias[n]=fileext[s]; + } + } + alias[n]=0; /* null terminate alias[] */ + + return(TRUE); +} + +/*************************************************************************** + PC_VALID_SFN - See if filename is a valid short file name + +Description + Determines validity of a short file name based on the following criteria: + - the file name must be between 0 and 8 characters + - the file extension must be between 0 and 3 characters + - the file name must not begin with a period + - it must not be a reserved DOS file name + - it must not contain any characters that are illegal within sfn's +Returns + TRUE if filename is a valid sfn, FALSE otherwise +****************************************************************************/ + +BOOLEAN pc_valid_ascii_sfn(byte *filename) +{ + int len,period_count,ext_start; + BOOLEAN badchar; + if(name_is_reserved(filename)) return(FALSE); + for(len=0,badchar=FALSE,period_count=0,ext_start=0; filename[len]!=0; len++) + { + if(_illegal_alias_char(filename[len])) badchar = TRUE; + if(filename[len] == '.') + { + ext_start = len+1; + period_count++; + } + } + + if( (filename[0] == ' ') || /* 1st char is a space */ + (len == 0) || /* no name */ + badchar || /* contains illegal chars */ + (period_count > 1) || /* contains more than one extension */ + ((len-ext_start)>3 && period_count>0) || /* extension is longer than 3 chars */ + (period_count==0 && len > 8) || /* name is longer than 8 chars */ + (ext_start > 9) || /* name is longer than 8 chars */ + (ext_start==1) ) return(FALSE); /* no name; 1st char is a period */ + + return(TRUE); +} + +byte print_buffer[132]; + +/* Take a string the may be either ASCI unicode '\0''C' or just asci 'C' + and return an asci printable version of the string */ +byte *unicode_make_printable(byte *p) +{ +int i; +byte c; +int is_unicode; + + is_unicode = 0; + + if (*(p+BYTE_LOW)!=0) /* Most likely it is ascii */ + { + rtfs_strcpy(print_buffer, p); + return(&print_buffer[0]); + + } + + i = 0; + for (;;) + { + if (*(p+BYTE_LOW)==0) + { + c = *(p+BYTE_HIGH); + if (c == 0) + break; + else + print_buffer[i++] = c; + } + p += 2; + + } + print_buffer[i] = 0; + return (&print_buffer[0]); +} +#endif + diff --git a/build/libraries/fatfs/ARM7/drdefault.c b/build/libraries/fatfs/ARM7/drdefault.c new file mode 100644 index 0000000..75e61c5 --- /dev/null +++ b/build/libraries/fatfs/ARM7/drdefault.c @@ -0,0 +1,158 @@ +/*---------------------------------------------------------------------------* + Project: CTR - for RTFS + File: drdefault.c + + 2006 Nintendo. + *---------------------------------------------------------------------------*/ + +#include +#include +#include +#include + +#if (RTFS_DEBUG_PRINT_ON == 1) + #if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + #define PRINTDEBUG osTPrintf + #else + #include + #define PRINTDEBUG vlink_dos_printf + #endif +#else + #define PRINTDEBUG i_no_print + static void i_no_print( const char *fmt, ... ); + static void i_no_print( const char *fmt, ... ){ return; } +#endif + + +/*---------------------------------------------------------------------------* + Name: defaultRtfsIo + + Description: 上位層からのセクタリード/ライト要求を受ける + + Arguments: driveno : ドライブ番号 + block : 開始ブロック番号 + buffer : + count : ブロック数 + reading : リード要求時にTRUE + + Returns: TRUE/FALSE + *---------------------------------------------------------------------------*/ +BOOL defaultRtfsIo( int driveno, dword block, void* buffer, word count, BOOLEAN reading) +{ + if( reading) { + PRINTDEBUG( "DEVCTL_IO_READ ... block:%x, count:%x -> buf:%x\n", block, count, buffer); + }else{ + PRINTDEBUG( "DEVCTL_IO_WRITE ... block:%x, count:%x <- buf:%x\n", block, count, buffer); + } + + return FALSE; +} + +/*---------------------------------------------------------------------------* + Name: defaultRtfsCtrl + + Description: 上位層からのコントロール要求を受ける + + Arguments: driveno : ドライブ番号 + opcode : 要求の種類 + pargs : + + Returns: + *---------------------------------------------------------------------------*/ +int defaultRtfsCtrl( int driveno, int opcode, void* pargs) +{ + DDRIVE *pdr; + DEV_GEOMETRY gc; + int heads, secptrack; + + pdr = pc_drno_to_drive_struct( driveno); + + switch( opcode) { + case DEVCTL_GET_GEOMETRY: + PRINTDEBUG( "DEVCTL_GET_GEOMETRY\n"); + rtfs_memset( &gc, (byte)0, sizeof(gc)); + + gc.dev_geometry_lbas = 0; + gc.dev_geometry_heads = 0; + gc.dev_geometry_cylinders = 0; + gc.dev_geometry_secptrack = 0; + /**/ + gc.fmt_parms_valid = FALSE; + OSAPI_CPUCOPY8( &gc, pargs, sizeof(gc)); + return( -1); //fix + + case DEVCTL_FORMAT: + PRINTDEBUG( "DEVCTL_FORMAT\n"); + break; + + case DEVCTL_REPORT_REMOVE: + pdr->drive_flags &= ~DRIVE_FLAGS_INSERTED; + break; + + case DEVCTL_CHECKSTATUS: + PRINTDEBUG( "DEVCTL_CHECKSTATUS\n"); + if (!(pdr->drive_flags & DRIVE_FLAGS_REMOVABLE)) { //リムーバブルでない場合 + return(DEVTEST_NOCHANGE); //fix + }else{ //リムーバブルの場合 + return(DEVTEST_NOMEDIA); //fix + } + + case DEVCTL_WARMSTART: + PRINTDEBUG( "DEVCTL_WARMSTART\n"); + pdr->drive_flags |= (/*DRIVE_FLAGS_VALID |*/ DRIVE_FLAGS_REMOVABLE); + pdr->partition_number = 0; + return( 0); //fix + + case DEVCTL_POWER_RESTORE: + PRINTDEBUG( "DEVCTL_POWER_RESTORE\n"); + break; + + case DEVCTL_POWER_LOSS: + PRINTDEBUG( "DEVCTL_POWER_LOSS\n"); + break; + + default: + PRINTDEBUG( "DEVCTL_unknown\n"); + break; + } + return( 0); +} + +/*---------------------------------------------------------------------------* + Name: defaultRtfsAttach + + Description: defaultドライバをドライブに割り当てる + + Arguments: driveno : ドライブ番号 + + Returns: + *---------------------------------------------------------------------------*/ +/* rtfs_detachでdefaultドライバが割り当てられるため、このAPIは必要ない*/ +/* +void defaultRtfsAttach( int driveno) +{ + BOOLEAN result; + DDRIVE pdr; + + pdr.dev_table_drive_io = defaultRtfsIo; + pdr.dev_table_perform_device_ioctl = defaultRtfsCtrl; + pdr.register_file_address = (dword) 0; // Not used + pdr.interrupt_number = 0; // Not used + pdr.drive_flags = 0; // DRIVE_FLAGS_VALID をセットしない + pdr.partition_number = 0; // Not used + pdr.pcmcia_slot_number = 0; // Not used + pdr.controller_number = 0; + pdr.logical_unit_number = 0; + + + //REMOVE扱い + pdr.drive_flags |= DRIVE_FLAGS_REMOVABLE; + pdr.drive_flags &= ~DRIVE_FLAGS_INSERTED; + + + result = rtfs_attach( driveno, &pdr, "DEFAULT"); //構造体がFSライブラリ側にコピーされる + if( !result) { + PRINTDEBUG( "fsEnableDevice failured\n"); + } +} +*/ diff --git a/build/libraries/fatfs/ARM7/drdefault.h b/build/libraries/fatfs/ARM7/drdefault.h new file mode 100644 index 0000000..549bb30 --- /dev/null +++ b/build/libraries/fatfs/ARM7/drdefault.h @@ -0,0 +1,11 @@ + +#ifndef __DRDEFAULT_H__ +#define __DRDEFAULT_H__ + + +void defaultRtfsAttach( int driveno); +BOOL defaultRtfsIo( int driveno, dword block, void* buffer, word count, BOOLEAN reading); +int defaultRtfsCtrl( int driveno, int opcode, void* pargs); + + +#endif /*__DRDEFAULT_H__*/ diff --git a/build/libraries/fatfs/ARM7/drfile.c b/build/libraries/fatfs/ARM7/drfile.c new file mode 100644 index 0000000..b8dcf3a --- /dev/null +++ b/build/libraries/fatfs/ARM7/drfile.c @@ -0,0 +1,295 @@ +/*---------------------------------------------------------------------------* + Project: CTR - for RTFS + File: drfile.c + + 2006 Nintendo. + *---------------------------------------------------------------------------*/ + +#include +#include +#include +#include + +#if (RTFS_DEBUG_PRINT_ON == 1) + #if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + #define PRINTDEBUG osTPrintf + #else + #include + #define PRINTDEBUG vlink_dos_printf + #endif +#else + #define PRINTDEBUG i_no_print + static void i_no_print( const char *fmt, ... ); + static void i_no_print( const char *fmt, ... ){ return; } +#endif + + +/*---------------------------------------------------------------------------* + global変数 + *---------------------------------------------------------------------------*/ +PCFD fileDescList[26] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +u32 file_capacity; +u32 file_adjusted_capacity; +u16 file_heads; +u16 file_secptrack; +u16 file_cylinders; + + +/*---------------------------------------------------------------------------* + extern変数 + *---------------------------------------------------------------------------*/ +extern int rtfs_first_stat_flag[26]; + + +/*---------------------------------------------------------------------------* + static関数 + *---------------------------------------------------------------------------*/ +static void file_get_CHS_params( u32 file_sector_num); + + +/*---------------------------------------------------------------------------* + Name: fileRtfsIo + + Description: 上位層からのセクタリード/ライト要求を受ける + + Arguments: driveno : ドライブ番号 + block : 開始ブロック番号 + buffer : + count : ブロック数 + reading : リード要求時にTRUE + + Returns: TRUE/FALSE + *---------------------------------------------------------------------------*/ +BOOL fileRtfsIo( int driveno, dword block, void* buffer, word count, BOOLEAN reading) +{ + int result; + unsigned long dmy; + + po_ulseek( fileDescList[driveno], block*512, &dmy, PSEEK_SET); + if( reading) { + PRINTDEBUG( "DEVCTL_IO_READ ... block:%x, count:%x -> buf:%x\n", block, count, buffer); + result = po_read( fileDescList[driveno], buffer, 512*count); + }else{ + PRINTDEBUG( "DEVCTL_IO_WRITE ... block:%x, count:%x <- buf:%x\n", block, count, buffer); + result = po_write( fileDescList[driveno], buffer, 512*count); + } + + if( result < 0) { + return FALSE; + }else{ + return TRUE; + } +} + +/*---------------------------------------------------------------------------* + Name: fileRtfsCtrl + + Description: 上位層からのコントロール要求を受ける + + Arguments: driveno : ドライブ番号 + opcode : 要求の種類 + pargs : + + Returns: + *---------------------------------------------------------------------------*/ +int fileRtfsCtrl( int driveno, int opcode, void* pargs) +{ + DDRIVE *pdr; + DEV_GEOMETRY gc; + int heads, secptrack; + unsigned long file_size, dmy; + + pdr = pc_drno_to_drive_struct( driveno); + + switch( opcode) { + case DEVCTL_GET_GEOMETRY: + PRINTDEBUG( "%s : DEVCTL_GET_GEOMETRY\n", __FUNCTION__); + rtfs_memset( &gc, (byte)0, sizeof(gc)); + + po_ulseek( fileDescList[driveno], 0, &dmy, PSEEK_SET); + po_ulseek( fileDescList[driveno], 0, &file_size, PSEEK_END); + po_ulseek( fileDescList[driveno], 0, &dmy, PSEEK_SET); + + file_capacity = file_size / 512; //セクタ単位にする + + file_get_CHS_params( file_capacity); + + PRINTDEBUG( "capacity : 0x%x\n", file_adjusted_capacity); + PRINTDEBUG( "heads : 0x%x\n", file_heads); + PRINTDEBUG( "cylinders : 0x%x\n", file_cylinders); + PRINTDEBUG( "secptrack : 0x%x\n", file_secptrack); + + gc.dev_geometry_lbas = file_adjusted_capacity; + gc.dev_geometry_heads = file_heads; + gc.dev_geometry_cylinders = file_cylinders; + gc.dev_geometry_secptrack = file_secptrack; + /**/ + gc.fmt_parms_valid = FALSE; + OSAPI_CPUCOPY8( &gc, pargs, sizeof(gc)); + return( 0); + + case DEVCTL_FORMAT: + { +/* u32 filebuf[512/4]; + + miCpuFill8( filebuf, 0, 512); + po_write( fileDescList[driveno], filebuf, 512);*/ + PRINTDEBUG( "%s : DEVCTL_FORMAT\n", __FUNCTION__); + return( 0); + } + + case DEVCTL_REPORT_REMOVE: + pdr->drive_flags &= ~DRIVE_FLAGS_INSERTED; + return( 0); + + case DEVCTL_CHECKSTATUS: + PRINTDEBUG( "%s : DEVCTL_CHECKSTATUS\n", __FUNCTION__); + if (!(pdr->drive_flags & DRIVE_FLAGS_REMOVABLE)) { //リムーバブルでない場合 + return(DEVTEST_NOCHANGE); //fix + }else{ //リムーバブルの場合 + if( rtfs_first_stat_flag[driveno]) { + rtfs_first_stat_flag[driveno] = 0; + PRINTDEBUG( "CHANGED!\n"); + pdr->drive_flags |= DRIVE_FLAGS_INSERTED; + return(DEVTEST_CHANGED); + } + pdr->drive_flags |= DRIVE_FLAGS_INSERTED; + return( DEVTEST_NOCHANGE); + } + + case DEVCTL_WARMSTART: + PRINTDEBUG( "%s : DEVCTL_WARMSTART\n", __FUNCTION__); + pdr->drive_flags |= (DRIVE_FLAGS_VALID | DRIVE_FLAGS_REMOVABLE); + return( 0); + + case DEVCTL_POWER_RESTORE: + PRINTDEBUG( "%s : DEVCTL_POWER_RESTORE\n", __FUNCTION__); + break; + + case DEVCTL_POWER_LOSS: + PRINTDEBUG( "%s : DEVCTL_POWER_LOSS\n", __FUNCTION__); + break; + + default: + PRINTDEBUG( "%s : DEVCTL_unknown\n", __FUNCTION__); + break; + } + return( 0); +} + + +/*SD File System Specification(仕様書)に基づいた値を出す*/ +static void file_get_CHS_params( u32 file_sector_num) +{ + int mbytes; + +// mbytes = (sdmc_current_spec.card_capacity / (1024 * 1024)) * 512; + mbytes = (file_sector_num >> 11); + + while( 1) { + if( mbytes <= 2) { + file_heads = 2; + file_secptrack = 16; + break; + } + if( mbytes <= 16) { + file_heads = 2; + file_secptrack = 32; + break; + } + if( mbytes <= 32) { + file_heads = 4; + file_secptrack = 32; + break; + } + if( mbytes <= 128) { + file_heads = 8; + file_secptrack = 32; + break; + } + if( mbytes <= 256) { + file_heads = 16; + file_secptrack = 32; + break; + } + if( mbytes <= 504) { + file_heads = 16; + file_secptrack = 63; + break; + } + if( mbytes <= 1008) { + file_heads = 32; + file_secptrack = 63; + break; + } + if( mbytes <= 2016) { + file_heads = 64; + file_secptrack = 63; + break; + } + if( mbytes <= 2048) { + file_heads = 128; + file_secptrack = 63; + break; + } + if( mbytes <= 4032) { + file_heads = 128; + file_secptrack = 63; + break; + } + if( mbytes <= 32768) { + file_heads = 255; + file_secptrack = 63; + break; + } + } + + /*シリンダ数を計算*/ + file_cylinders = (file_sector_num / (file_heads * file_secptrack)); + + /*memory_capacityを再計算してadjusted_memory_capacityに格納*/ + file_adjusted_capacity = file_cylinders * + (file_heads * file_secptrack); +} + + +/*---------------------------------------------------------------------------* + Name: fileRtfsAttach + + Description: fileドライバをドライブに割り当てる + + Arguments: driveno : ドライブ番号 + + Returns: + *---------------------------------------------------------------------------*/ +BOOL fileRtfsAttach( PCFD fileDesc, int driveno) +{ + BOOLEAN result; + DDRIVE pdr; + + pdr.dev_table_drive_io = fileRtfsIo; + pdr.dev_table_perform_device_ioctl = fileRtfsCtrl; + pdr.register_file_address = (dword) 0; /* Not used */ + pdr.interrupt_number = 0; /* Not used */ + pdr.drive_flags = DRIVE_FLAGS_VALID; + pdr.partition_number = 0; /* Not used */ + pdr.pcmcia_slot_number = 0; /* Not used */ + pdr.controller_number = 0; + pdr.logical_unit_number = 0; + + fileDescList[driveno] = fileDesc; /* file descripter セット */ + + result = rtfs_attach( driveno, &pdr, "FILE"); //構造体がFSライブラリ側にコピーされる + /**/ + if( !result) { + PRINTDEBUG( "fsEnableDevice failured\n"); + } + + return( result); +} + + diff --git a/build/libraries/fatfs/ARM7/portconf.h b/build/libraries/fatfs/ARM7/portconf.h new file mode 100644 index 0000000..92a615e --- /dev/null +++ b/build/libraries/fatfs/ARM7/portconf.h @@ -0,0 +1,50 @@ +/***************************************************************************** +*Filename: PORTCONF.H - RTFS porting layer tuning constants +* +* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc, 1993-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* Description: +* This file contains porting layer tuning constants for configuring RTFS. +* It is included by rtfsconf.h. +* +****************************************************************************/ + +#ifndef __PORTCONF__ +#define __PORTCONF__ 1 + +/* CPU Configuration section */ + +#define KS_LITTLE_ENDIAN 1 /* See porting reference guide for explanation */ +#define KS_LITTLE_ODD_PTR_OK 0 /* See porting reference guide for explanation */ +#define KS_CONSTANT const /* See porting reference guide for explanation */ +#define KS_FAR /* See porting reference guide for explanation */ + + +/* Compile time constants to control device inclusion + See the reference guide for an explanation +*/ + + +#define INCLUDE_SD 0 +#define INCLUDE_IDE 0 /* - Include the IDE driver */ +#define INCLUDE_PCMCIA 0 /* - Include the pcmcia driver */ +#define INCLUDE_PCMCIA_SRAM 0 /* - Include the pcmcia static ram card driver */ +#define INCLUDE_COMPACT_FLASH 0 /* - Support compact flash (requires IDE and PCMCIA) */ +#define INCLUDE_FLASH_FTL 0 /* - Include the linear flash driver */ +#define INCLUDE_ROMDISK 0 /* - Include the rom disk driver */ +#define INCLUDE_RAMDISK 0 /* - Include the rom disk driver */ +#define INCLUDE_MMCCARD 0 /* - Include the multi media flash card driver */ +#define INCLUDE_SMARTMEDIA 0 /* - Include the smart media flash card driver */ +#define INCLUDE_FLOPPY 0 /* - Include the floppy disk driver */ +#define INCLUDE_HOSTDISK 0 /* - Include the host disk disk simulator */ +#define INCLUDE_UDMA 0 /* - Include ultra dma support for the ide driver */ +#define INCLUDE_82365_PCMCTRL 0 /* - Include the 82365 pcmcia controller driver */ + + +#endif /* __PORTCONF__ */ diff --git a/build/libraries/fatfs/ARM7/portio.c b/build/libraries/fatfs/ARM7/portio.c new file mode 100644 index 0000000..1c2ff3d --- /dev/null +++ b/build/libraries/fatfs/ARM7/portio.c @@ -0,0 +1,418 @@ +/* +* portio.c +* +* ERTFS Porting layer peripheral input output functions. The user must port +* some or all of the routines in this file to his CPU environment according +* to the rules set forth in the ERTFS porting specification. +* +* Copyright EBS Inc , 1993-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* Generic implementation of IO porting layer +* Modify these routnes for your target environment. +* Note: Not all routines must be implemented. Only implement +* those routines that are required for your selected set of devices. +*/ + +#include +#include +#include + +/*--- ctr modified ---*/ +#include +#if (RTFS_DEBUG_PRINT_ON == 1) + #if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + #define PRINTDEBUG osTPrintf + #else + #include + #define PRINTDEBUG vlink_dos_printf + #endif +#else + #define PRINTDEBUG i_no_print + static void i_no_print( const char *fmt, ... ); + static void i_no_print( const char *fmt, ... ){ return; } +#endif +/*--------------------*/ + +/* All of the IO in and out functions in this file call one of these four + macros to write or read the apprpriate IO location. If t is convenient + to use this scheme and it will work for you then please modify these + macros to do the appropriate IO operation for the register. If this can't + be accomplished just modfy the appropriate routines manually. +*/ + +#define RTFS_OUTBYTE(ADDRESS,VALUE) +#define RTFS_INBYTE(ADDRESS) 0 +#define RTFS_OUTWORD(ADDRESS,VALUE) +#define RTFS_INWORD(ADDRESS) 0 +#define RTFS_OUTDWORD(ADDRESS,VALUE) + +/* The code below must be implemented for all targets */ + +/*rtfs_port_disable() and rtfs_port_enable() + Used by a few device drivers, floppy, flash chip and pcmctrl, + you may ignore it if not being called, but first check to see + if floppy, flash chip and pcmctrl are still the only drivers + using the functions */ + +/*This function must disable interrupts and return. */ +void rtfs_port_disable(void) +{ /* ctr modified */ + PRINTDEBUG( "fatfs : rtfs_port_disable) is called.\n"); + OSAPI_DISABLEINTR(); +} + +/*This function must re-disable interrupts that were disabled via a call to +rtfs_port_disable(). */ + +void rtfs_port_enable(void) +{ /* ctr modified */ + PRINTDEBUG( "rtfs_port_enable is called.\n"); + OSAPI_ENABLEINTR(); +} + +#if (INCLUDE_82365_PCMCTRL) +/* These routines are required only if the 82365 pcmcia controller driver + is included (pcmctrl.c) + phys82365_to_virtual(byte * * virt, dword phys) + write_82365_index_register(byte value) + write_82365_data_register(byte value) + read_82365_data_register(void) +*/ +/* This routine must take a physical linear 32 bit bus address passed in +the "phys" argument and convert it to an address adressable in the logical +space of the CPU, returning that value in "virt". Two sample methods are +provided, a flat version where the virtual address is simply the "phys" +address cast to a pointer and a second segmented version for X86 +segmented applications. +*/ +void phys82365_to_virtual(byte * * virt, dword phys) +{ + *virt = (byte *) phys; /* example for flat unmapped address space */ +} +/* +These routines write and read the 82365's index and data registers which, +in a standard PC environment, are located in IO space at address 0x3E0 and +0x3E1. Non PC architectures typically map these as memory mapped locations +somewhere high in memory such as 0xB10003E0 and 0xB10003E1. +*/ +void write_82365_index_register(byte value) +{ + RTFS_OUTBYTE(0x3e0,value); /* Defined for X86. Replace with your own */ +} + +void write_82365_data_register(byte value) +{ + RTFS_OUTBYTE(0x3e1,value); /* Defined for X86. Replace with your own */ +} + +byte read_82365_data_register(void) +{ +byte v; + v = RTFS_INBYTE(0x3e1); /* Defined for X86. Replace with your own */ + return (v); +} +#endif /* (INCLUDE_82365_PCMCTRL) */ + +/* +These routines are only required if INCLUDE_IDE is turned on (ide_drv.c) +byte ide_rd_status(dword register_file_address) +byte ide_rd_sector_count(dword register_file_address) +byte ide_rd_alt_status(dword register_file_address,int contiguous_io_mode) +byte ide_rd_error(dword register_file_address) +byte ide_rd_sector_number(dword register_file_address) +byte ide_rd_cyl_low(dword register_file_address) +byte ide_rd_cyl_high(dword register_file_address) +byte ide_rd_drive_head(dword register_file_address) +byte ide_rd_drive_address(dword register_file_address,int contiguous_io_mode) +void ide_wr_dig_out(dword register_file_address, int contiguous_io_mode, byte value); +void ide_wr_sector_count(dword register_file_address, byte value) +void ide_wr_sector_number(dword register_file_address, byte value) +void ide_wr_cyl_low(dword register_file_address, byte value) +void ide_wr_cyl_high(dword register_file_address, byte value) +void ide_wr_drive_head(dword register_file_address, byte value) +void ide_wr_command(dword register_file_address, byte value) +void ide_wr_feature(dword register_file_address, byte value) +unsigned long rtfs_port_bus_address(void * p) +void ide_insw(dword register_file_address, unsigned short *p, int nwords) +void ide_outsw(dword register_file_address, unsigned short *p, int nwords) + +These routines are only required if INCLUDE_IDE +and INCLUDE_UDMA are turned on + +dword rtfs_port_ide_bus_master_address(int controller_number) +byte ide_rd_udma_status(dword bus_master_address) +void ide_wr_udma_status(dword bus_master_address, byte value) +byte ide_rd_udma_command(dword bus_master_address) +void ide_wr_udma_command(dword bus_master_address, byte value) +void ide_wr_udma_address(dword bus_master_address, dword bus_address) +*/ + +#if (INCLUDE_IDE) +/* These routines are required only if using the IDE device driver */ +/* This function must return the byte in location 7 (IDE_OFF_STATUS) + of the ide register file at register_file_address */ +byte ide_rd_status(dword register_file_address) +{ + return(RTFS_INBYTE((word) (register_file_address + 7))); +} + +/* This function must return the word in location 0 (IDE_OFF_DATA) + of the ide register file at register_file_address */ +word ide_rd_data(dword register_file_address) +{ + return(RTFS_INWORD((word)register_file_address)); +} + + +/* This function must return the byte in location 2 (IDE_OFF_SECTOR_COUNT) + of the ide register file at register_file_address */ +byte ide_rd_sector_count(dword register_file_address) +{ + return(RTFS_INBYTE((word) (register_file_address + 2))); +} + +/* This function must return the byte in location 0x206 (IDE_OFF_ALT_STATUS) + of the ide register file at register_file_address If the value of the + argument contiguous_io_mode is 1 then the register must be 14 rather + than 0x206. +*/ + +byte ide_rd_alt_status(dword register_file_address, int contiguous_io_mode) +{ + if (contiguous_io_mode) + return(RTFS_INBYTE((word) (register_file_address + 14))); + else + return(RTFS_INBYTE((word) (register_file_address + 0x206))); +} + +/* This function must return the byte in location 1 (IDE_OFF_ERROR) + of the ide register file at register_file_address. +*/ +byte ide_rd_error(dword register_file_address) +{ + return(RTFS_INBYTE((word) (register_file_address + 1))); +} + +/* This function must return the byte in location 3 (IDE_OFF_SECTOR_NUMBER) + of the ide register file at register_file_address +*/ +byte ide_rd_sector_number(dword register_file_address) +{ + return(RTFS_INBYTE((word) (register_file_address + 3))); +} +/* This function must return the byte in location 4 (IDE_OFF_CYL_LOW) + of the ide register file. register_file_address +*/ +byte ide_rd_cyl_low(dword register_file_address) +{ + return(RTFS_INBYTE((word) (register_file_address + 4))); +} +/* This function must return the byte in location 5 (IDE_OFF_CYL_HIGH) + of the ide register file. register_file_address +*/ +byte ide_rd_cyl_high(dword register_file_address) +{ + return(RTFS_INBYTE((word) (register_file_address + 5))); +} + +/* This function must return the byte in location 6 (IDE_OFF_DRIVE_HEAD) + of the ide register file. register_file_address +*/ +byte ide_rd_drive_head(dword register_file_address) +{ + return(RTFS_INBYTE((word) (register_file_address + 6))); +} + +/* This function must return the byte in location 0x207 (IDE_OFF_DRIVE_ADDRESS) + of the ide register file at register_file_address. + If the value of the argument contiguous_io_mode is 1 then the register + must be 15 rather than 0x207. +*/ + +byte ide_rd_drive_address(dword register_file_address,int contiguous_io_mode) +{ + if (contiguous_io_mode) + return(RTFS_INBYTE((word) (register_file_address + 15))); + else + return(RTFS_INBYTE((word) (register_file_address + 0x207))); +} + +/* This function must place the byte in value at location + 0x206 (IDE_OFF_ALT_STATUS) of the ide register file at + register_file_address. If the value of the argument contiguous_io_mode + is 1 then the register must be 14 rather than 0x206. +*/ + + +void ide_wr_dig_out(dword register_file_address, int contiguous_io_mode, byte value) +{ + if (contiguous_io_mode) + RTFS_OUTBYTE((word) (register_file_address + 14), value); + else + RTFS_OUTBYTE((word) (register_file_address + 0x206), value); +} + +/* This function must place the word in location 0 (IDE_OFF_DATA) + of the ide register file at register_file_address */ +void ide_wr_data(dword register_file_address, word value) +{ + RTFS_OUTWORD((word)register_file_address, value); +} + +/* This function must place the byte in value at location + 2 (IDE_OFF_SECTOR_COUNT) of the ide register file at + register_file_address +*/ +void ide_wr_sector_count(dword register_file_address, byte value) +{ + RTFS_OUTBYTE((word) (register_file_address + 2), value); +} + +/* This function must place the byte in value at location + 3 (IDE_OFF_SECTOR_NUMBER) of the ide register file at + register_file_address +*/ +void ide_wr_sector_number(dword register_file_address, byte value) +{ + RTFS_OUTBYTE((word) (register_file_address + 3), value); +} + +/* This function must place the byte in value at location + 4 (IDE_OFF_CYL_LOW) of the ide register file at + register_file_address +*/ +void ide_wr_cyl_low(dword register_file_address, byte value) +{ + RTFS_OUTBYTE((word) (register_file_address + 4), value); +} +/* This function must place the byte in value at location + 5 (IDE_OFF_CYL_HIGH) of the ide register file at register_file_address +*/ +void ide_wr_cyl_high(dword register_file_address, byte value) +{ + RTFS_OUTBYTE((word) (register_file_address + 5), value); +} + +/* This function must place the byte in value at location + 6 (IDE_OFF_DRIVE_HEAD) of the ide register file at register_file_address +*/ +void ide_wr_drive_head(dword register_file_address, byte value) +{ + RTFS_OUTBYTE((word) (register_file_address + 6), value); +} +/* This function must place the byte in value at location + 7 (IDE_OFF_COMMAND) of the ide register file at register_file_address */ +void ide_wr_command(dword register_file_address, byte value) +{ + RTFS_OUTBYTE((word) (register_file_address + 7), value); +} + +/* This function must place the byte in value at location + 1 (IDE_OFF_FEATURE) of the ide register file at register_file_address +*/ +void ide_wr_feature(dword register_file_address, byte value) +{ + RTFS_OUTBYTE((word) (register_file_address + 1), value); +} + + +/* This function must read nwords 16 bit values from the data + register at offset 0 of the the ide register file and place + them in succesive memory locations starting at p. Since large + blocks of data are transferred from the drive in this way this + routine should be optimized. On x86 based systems the repinsw + instruction should be used, on non x86 platforms the loop should + be as tight as possible. +*/ + +void ide_insw(dword register_file_address, unsigned short *p, int nwords) +{ + while (nwords--) + *p++ = (word)RTFS_INWORD(register_file_address); +} +/* + This function must write nwords 16 bit values to the data + register at offset 0 of the the ide register file. The data is + taken from succesive memory locations starting at p. Since large + blocks of data are transferred from the drive in this way this + routine should be optimized. On x86 based systems the repoutsw + instruction should be used, on non x86 platforms the loop should + be as tight as possible. +*/ +void ide_outsw(dword register_file_address, unsigned short *p, int nwords) +{ + while (nwords--) + RTFS_OUTWORD(register_file_address, *p++); +} + +#if (INCLUDE_UDMA) +/* These routines are required only if using an ultra-dma controller. */ +/* This function must determine if the specified controller is a PCI bus + mastering IDE controller and if so return the location of the + control and status region for that controller. If it is not a bus + bus master controller it should return zero. +*/ +dword rtfs_port_ide_bus_master_address(int controller_number) +{ + RTFS_ARGSUSED_INT(controller_number); + return (0); /* Must be implemented. 0 return val disables UDMA */ +} + +/* This function must read the status byte value at location + 2 the bus master control region +*/ +byte ide_rd_udma_status(dword bus_master_address) +{ +byte value; + value = RTFS_INBYTE((word) (bus_master_address + 2)); + return (value); +} + +/* This function must write the byte value to location + 2 of the bus master control region +*/ +void ide_wr_udma_status(dword bus_master_address, byte value) +{ + RTFS_OUTBYTE((word) (bus_master_address + 2), value); +} + +/* This function must read the command byte value at location + 0 of the bus master control region +*/ +byte ide_rd_udma_command(dword bus_master_address) +{ +byte value; + value = RTFS_INBYTE((word) bus_master_address); + return (value); +} +/* This function must write the byte value to location + 0 of the bus master control region +*/ +void ide_wr_udma_command(dword bus_master_address, byte value) +{ + RTFS_OUTBYTE((word) bus_master_address, value); +} +/* This function must write the byte dword to location + 4 of the bus master control region +*/ +void ide_wr_udma_address(dword bus_master_address, dword bus_address) +{ + RTFS_OUTDWORD((word)(bus_master_address+4), bus_address); +} + + +/* This function must take a logical pointer and convert it to an + unsigned long representation of its address on the system bus */ +unsigned long rtfs_port_bus_address(void * p) +{ +dword laddress; + laddress = (dword) p; + return(laddress); +} + +#endif /* (INCLUDE_UDMA) */ +#endif /* (INCLUDE_IDE) */ + diff --git a/build/libraries/fatfs/ARM7/portkern.c b/build/libraries/fatfs/ARM7/portkern.c new file mode 100644 index 0000000..77bff8b --- /dev/null +++ b/build/libraries/fatfs/ARM7/portkern.c @@ -0,0 +1,636 @@ +/* +* portrtfs.c +* +* ERTFS Porting layer. The user must port some or all of the routines +* in this file to his RTOS and hardware environment according to the +* rules set forth in the ERTFS porting specification. +* +* Copyright EBS Inc , 1993-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* Generic RTOS porting layer template. +* +* This file contains the RTOS/KERNEL porting layer as described in +* the porting guide. If you are porting to a real time operating +* system environment you must modify most of the routines in this +* file for your RTOS. If you are not porting to an RTOS but are rather +* bringing ERTFS up in polling mode then read the following section. +* +* +* If you are using POLLED mode then this file is actually quite portable. +* Several reference ports are provided for x86 environments. To port +* it to a non supported environment you must provide the following +* functions and macros. +* unsigned long rtfs_port_get_ticks(); +* #define MILLISECONDS_PER_TICK +* +* +* And depending on what drivers are required one or more of the following +* +* void hook_82365_pcmcia_interrupt(int irq) +* void hook_ide_interrupt(int irq, int controller_number) +* void hook_ide_interrupt(int irq, int controller_number) +* void hook_floppy_interrupt(int irq) +* +*/ + +#include /* ctr modified */ +//#include /* ctr modified */ +//#include +#include +#include /* For included devices */ + +/*--- ctr modified ---*/ +#include +#if (RTFS_DEBUG_PRINT_ON == 1) + #if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + #define PRINTDEBUG osTPrintf + #else + #include + #define PRINTDEBUG vlink_dos_printf + #endif +#else + #define PRINTDEBUG i_sdmc_no_print + static void i_sdmc_no_print( const char *fmt, ... ); + static void i_sdmc_no_print( const char *fmt, ... ){ return; } +#endif +/*--------------------*/ + +/* This routine takes no arguments and returns an unsigned long. The routine + must return a tick count from the system clock. The macro named + MILLISECONDS_PER_TICK must be defined in such a way so that it returns + the rate at which the tick increases in milliseconds per tick */ +/* This routine is declared as static to emphasize that its use is local + to the portkern.c file only */ + +#if (TARGET_OS_CTR == 1) +#define MILLISECONDS_PER_TICK 1 /* Change this to your environment */ +#else +#define MILLISECONDS_PER_TICK 33520 /* Change this to your environment */ +#endif + +static dword rtfs_port_get_ticks(void) +{ +#if (TARGET_OS_CTR == 1) + SYSTIM systim; /* ctr modified */ + systim = get_tim( &systim); + PRINTDEBUG( "fatfs : rtfs_port_get_ticks(%d).\n", systim); + return( (dword)(systim)); /* Return the real periodic clock tick value here */ +#else + OSTick systim; + systim = OS_GetTick(); + return( (dword)(systim)); +#endif +} + +/* +This routine takes no arguments and returns an unsigned long. The routine +must allocate and initialize a Mutex, setting it to the "not owned" state. It +must return an unsigned long value that will be used as a handle. ERTFS will +not interpret the value of the return value. The handle will only used as +an argument to the rtfs_port_claim_mutex() and rtfs_port_release_mutex() +calls. The handle may be used as an index into a table or it may be cast +internally to an RTOS specific pointer. If the mutex allocation function +fails this routine must return 0 and the ERTFS calling function will return +failure. +*/ +/*メモ:apicnfig.cの定義「NDRIVES」の数 + 1個ぶんの回数呼ばれる*/ +dword rtfs_port_alloc_mutex(void) +{ /* ctr modified */ + /* POLLED mode does not require mutexes */ + /* Otherwise implement it */ +#if (TARGET_OS_CTR == 1) +// ID mytskid; + T_CMTX cmtx; + ER_ID mtxid; + +// get_tid( &mytskid); + + // setup mutex generate info. + cmtx.mtxatr = TA_INHERIT; // set attribution : inherit priority + cmtx.ceilpri= TMAX_MPRI; // set max priority : no effect when mtxatr is without TA_CEILING + + mtxid = acre_mtx( &cmtx); //mutexの生成 + if( mtxid < 0) { + PRINTDEBUG("fatfs : ERROR! Cannot create new mutex.\n"); + return 0; + } +// PRINTDEBUG( "fatfs : task[%d] create new mutex(%d).\n", mytskid, mtxid); + return( (dword)(mtxid)); +#else + OSMutex* my_mtx; + my_mtx = (OSMutex*)OS_Alloc( sizeof( OSMutex)); + if( my_mtx == NULL) { + return 0; + } + OS_InitMutex( my_mtx); + return( (dword)my_mtx); +#endif +} + +/* This routine takes as an argument a mutex handle that was returned by + rtfs_port_alloc_mutex(). If the mutex is already claimed it must wait for + it to be released and then claim the mutex and return. +*/ + +void rtfs_port_claim_mutex(dword handle) +{ /* ctr modified */ + /* POLLED mode does not require mutexes */ + /* Otherwise implement it */ +#if ( TARGET_OS_CTR == 1) +// ID mytskid; + ER ercd; + +// ercd = get_tid( &mytskid); + ercd = loc_mtx( handle); //mutexのロック + if( ercd == E_OK) { +// PRINTDEBUG( "fatfs : task[%d] lock mutex(%d).\n", mytskid, handle); + } +#else + OS_LockMutex( (OSMutex*)handle); +#endif +} + +/* This routine takes as an argument a mutex handle that was returned by +rtfs_port_alloc_mutex() that was previously claimed by a call to +rtfs_port_claim_mutex(). It must release the handle and cause a caller +blocked in rtfs_port_claim_mutex() for that same handle to unblock. +*/ + +void rtfs_port_release_mutex(dword handle) +{ /* ctr modified */ + /* POLLED mode does not require mutexes */ + /* Otherwise implement it */ +#if ( TARGET_OS_CTR == 1) +// ID mytskid; + ER ercd; + +// ercd = get_tid( &mytskid); + ercd = unl_mtx( handle); //mutexのリリース + if( ercd == E_OK) { +// PRINTDEBUG( "fatfs : task[%d] release mutex(%d).\n", mytskid, handle); + } +#else + OS_UnlockMutex( (OSMutex*)handle); + OS_Free( (OSMutex*)handle); +#endif +} + +/* Note: Currently ERTFS only requires these functions if the floppy disk device +driver is being used or if the IDE device driver is being used in interrupt +mode rather than polled mode. Additional user supplied device drivers may +opt to use these calls or they may implement their own native signalling +method at the implementor's discretion. If you are not using the floppy +disk driver or the IDE driver in interrupt mode please skip this section. + +The signalling code is abstracted into four functions that must be modified +by the user to support the target RTOS. The required functions are: +rtfs_port_alloc_signal(), rtfs_port_clear_signal(), rtfs_port_test_signal() + and rtfs_port_set_signal(). The requirements of each of these functions is +defined below. +*/ + + +/* This routine takes no arguments and returns an unsigned long. The routine +must allocate and initialize a signalling device (typically a counting +semaphore) and set it to the "not signalled" state. It must return an +unsigned long value that will be used as a handle. ERTFS will +not interpret the value of the return value. The handle will only used as +an argument to the rtfs_port_clear_signal(), rtfs_port_test_signal() +and rtfs_port_set_signal() calls. +Only required for the supplied floppy disk and ide device driver if the +ide driver is running in interrupt mode. Otherwise leave this function as +it is, it will not be used. */ + +dword rtfs_port_alloc_signal(void) +{ + /* Polled mode has only one global signal */ + /* Otherwise implement it */ + PRINTDEBUG( "not implemented function ( rtfs_port_alloc_signal) is called\n"); + return(1); +} + +/* This routine takes as an argument a handle that was returned by +rtfs_port_alloc_signal(). It must place the signal in an unsignalled state +such that a subsequant call to rtfs_port_test_signal() will not return +success until rtfs_port_set_signal() has been called. This clear function +is neccessary since it is possible although unlikely that an interrupt +service routine could call rtfs_port_set_signal() after the intended call +to rtfs_port_test_signal() timed out. A typical implementation of this +function for a counting semaphore is to set the count value to zero or +to poll it until it returns failure. */ +int polled_signal = 0; /* We only need one signal for polled mode since it + is single threaded */ +void rtfs_port_clear_signal(dword handle) +{ + /* Polled mode has only one global signal */ + /* Otherwise implement clear of the signal specified by handle */ + PRINTDEBUG( "not implemented function ( rtfs_port_clear_signal) is called\n"); + RTFS_ARGSUSED_PVOID((void *) handle); + polled_signal = 0; +} + +/* This routine takes as an argument a handle that was returned by +rtfs_port_alloc_signal() and a timeout value in milliseconds. It must +block until timeout milliseconds have elapsed or rtfs_port_set_signal() +has been called. If the test succeeds must return 0, if it times out it +must return a non-zero value. +Only required for the supplied floppy disk and ide device driver if the +ide driver is running in interrupt mode. Otherwise leave this function as +it is, it will not be used. */ + + +int rtfs_port_test_signal(dword handle, int timeout) +{ + PRINTDEBUG( "not implemented function ( rtfs_port_test_signal) is called\n"); +#if (!POLLED_MODE) + /* Implement timed test of the signal specified by handle here + otherwise use the polled mode code provided below */ + return (-1); +#else + /* This will work in polled mode */ +dword last_time, end_time, curr_time; + /* Polled mode signal wait with timeout. This is portable but the + user must implement the routine rtfs_port_get_ticks(void) + and the macro MILLISECONDS_PER_TICK. */ + + /* polled mode only ever uses one signal at a time so we + ignore the handle argument */ + RTFS_ARGSUSED_PVOID((void *) handle); + if (timeout) + { + /* Covert milliseconds to ticks. If 0 set it to 2 ticks */ + timeout = timeout/MILLISECONDS_PER_TICK; + if (!timeout) + timeout = 2; + last_time = end_time = 0; // keep compiler happy + curr_time = rtfs_port_get_ticks(); + end_time = curr_time + (dword) timeout; + // Check for a wrap + if (end_time < curr_time) + { + end_time = (dword) 0xffffffffL; + } + last_time = curr_time; + do + { + // See if we timed out + curr_time = rtfs_port_get_ticks(); + if (curr_time > end_time) + break; + // if wrap or clearing of clock then start count over + if (curr_time < last_time) + end_time = curr_time + (dword) timeout; + last_time = curr_time; + } while (polled_signal == 0); + } + if (polled_signal) + { + polled_signal -= 1; + return(0); + } + else + { + return(-1); + } +#endif +} +/* +This routine takes as an argument a handle that was returned by +rtfs_port_alloc_signal(). It must set the signal such that a subsequant +call to rtfs_port_test_signal() or a call currently blocked in +rtfs_port_test_signal() will return success. +This routine is always called from the device driver interrupt service +routine while the processor is executing in the interrupt context. +Only required for the supplied floppy disk and ide device driver if the +ide driver is running in interrupt mode. Otherwise leave this function as +it is, it will not be used. */ + + +void rtfs_port_set_signal(dword handle) +{ + /* Implement set of the signal specified by handle here */ + /* This simple implemetation is for polled mode */ + PRINTDEBUG( "not implemented function ( rtfs_port_set_signal) is called\n"); + RTFS_ARGSUSED_PVOID((void *) handle); + polled_signal += 1; +} + +/* +This routine takes as an argument a sleeptime value in milliseconds. It +must not return to the caller until at least sleeptime milliseconds have +elapsed. In a mutitasking environment this call should yield the task cpu. +*/ + +void rtfs_port_sleep(int sleeptime) +{ + /* Implement simple task sleep call here */ + + /* This simple implemetation is for polled mode */ + /* Call the signalling system to timeout. This will in effect sleep */ +#if (TARGET_OS_CTR == 1) + dly_tsk( (RELTIM)sleeptime); +#else + OS_Sleep( (u32)sleeptime); +#endif + PRINTDEBUG( "fatfs : port_sleep (time : %d). \n", sleeptime); + rtfs_port_clear_signal(0); + rtfs_port_test_signal(0, sleeptime); +} + +/* This routine takes no arguments and returns an unsigned long. The routine +must return an unsigned long value that will later be passed to +rtfs_port_elapsed_check() to test if a given number of milliseconds or +more have elapsed. A typical implementation of this routine would read the +system tick counter and return it as an unsigned long. ERTFS makes no +assumptions about the value that is returned. +*/ + +dword rtfs_port_elapsed_zero(void) +{ + PRINTDEBUG( "fatfs : rtfs_port_elapsed_zero\n"); + return(rtfs_port_get_ticks()); +} + +/* This routine takes as arguments an unsigned long value that was returned by +a previous call to rtfs_port_elapsed_zero() and a timeout value in +milliseconds. If "timeout" milliseconds have not elapsed it should return +0. If "timeout" milliseconds have elapsed it should return 1. A typical +implementation of this routine would read the system tick counter, subtract +the zero value, scale the difference to milliseconds and compare that to +timeout. If the scaled difference is greater or equal to timeout it should +return 1, if less than timeout it should return 0. +*/ + +int rtfs_port_elapsed_check(dword zero_val, int timeout) +{ + dword curr_time; + + PRINTDEBUG( "not implemented function ( rtfs_port_elapsed_check) is called\n"); + + timeout = timeout/MILLISECONDS_PER_TICK; + if (!timeout) + timeout = 2; + + curr_time = rtfs_port_get_ticks(); + if ( (curr_time - zero_val) > (dword) timeout) + return(1); + else + return(0); +} + +/* +This function must return an unsigned long number that is unique to the +currently executing task such that each time this function is called from +the same task it returns this same unique number. A typical implementation +of this function would get address of the current task control block, cast +it to a long, and return it. +*/ + +dword rtfs_port_get_taskid(void) +{ /* ctr modified */ + /* For an RTOS environment return a dword that is unique to this + task. For example the address of its task control block */ + /* Otherwise always return 1 For POLLOS */ +#if (TARGET_OS_CTR == 1) + ID tskid; + get_tid( &tskid); + return( (dword)(tskid)); +#else + OSThread* my_tsk; + my_tsk = OS_GetCurrentThread(); + return( (dword)my_tsk); +#endif +} + +/* +This routine requires porting if you wish to use the interactive test shell. +It must provide a line of input from the terminal driver. The line must be + NULL terminated and it must not contain ending newline or carriage return +characters. +*/ + +void rtfs_port_tm_gets(byte *buffer) +{ +/* Use Get's or some other console input function. If you have no console + input function then leave it blank */ +/* gets(buffer); */ +} + +/* +This routine requires porting if you wish to display messages to your console +This routine must print a line of text from the supplied buffer to +the console. The output routine must not issue a carriage or linefeed +command unless the text is terminated with the appropriate control +character (\n or \r). +*/ + +void rtfs_port_puts(byte *buffer) +{ + PRINTDEBUG( "%s\n", buffer); //ctr modified +/* Use cputs or some other console output function. If you have no console + output function then leave it blank */ +/* cputs(buffer); */ +} + +/* +This function must exit the RTOS session and return to the user prompt. It +is only necessary when an RTOS is running inside a shell environment like +Windows. This function must be implemented if you are using the ERTFS command library +in an environment that will ever exit, for example if ERTFS is running with +an RTOS in a Dos/Windows or Unix environment you should put a call to your +RTOS's exit code. +Note: Most embedded systems never exit. If your application never exits then + please skip this section. +*/ + +void rtfs_port_exit(void) +{ +/* Exit your application here. If you never exit then leave it blank. */ +/* exit(0); */ + PRINTDEBUG( "not implemented function ( rtfs_port_exit) is called\n"); +} +/* + When the system needs to date stamp a file it will call this routine + to get the current time and date. YOU must modify the shipped routine + to support your hardware's time and date routines. +*/ + +DATESTR *pc_getsysdate(DATESTR * pd) +{ + word year; /* relative to 1980 */ + word month; /* 1 - 12 */ + word day; /* 1 - 31 */ + word hour; + word minute; + word sec; /* Note: seconds are 2 second/per. ie 3 == 6 seconds */ +#if (TARGET_OS_CTR == 1) + RtcDate rtfs_base_date; //ctr modified + RtcTime rtfs_base_time; //ctr modified +#endif + PRINTDEBUG( "not implemented function ( pc_getsysdate) is called\n"); + +#if (0) + /* This code will work if yoiu have ANSI time functions. otherwise get + rid of it and look below where the time is wired to 3/28/8. + You may modify that code to work in your environment. */ + struct tm *timeptr; + time_t timer; + + time(&timer); + timeptr = localtime(&timer); + + hour = (word) timeptr->tm_hour; + minute = (word) timeptr->tm_min; + sec = (word) (timeptr->tm_sec/2); + /* Date comes back relative to 1900 (eg 93). The pc wants it relative to + 1980. so subtract 80 */ + year = (word) (timeptr->tm_year-80); + month = (word) (timeptr->tm_mon+1); + day = (word) timeptr->tm_mday; +#else +#if (TARGET_OS_CTR == 1) + /* This code is useless but very generic */ + /* Hardwire for now */ + /* 7:37:28 PM */ +// hour = 19; +// minute = 37; +// sec = 14; + /* 3-28-88 */ +// year = 8; /* relative to 1980 */ +// month = 3; /* 1 - 12 */ +// day = 28; /* 1 - 31 */ + + /*----- ctr modified -----*/ + rtcGetDateTimeSync( &rtfs_base_date, &rtfs_base_time); + + hour = rtfs_base_time.hour; + minute = rtfs_base_time.minute; + sec = rtfs_base_time.second / 2; + year = rtfs_base_date.year - 1980; + month = rtfs_base_date.month; + day = rtfs_base_date.day; + /*------------------------*/ +#else + +#endif +#endif + pd->time = (word) ( (hour << 11) | (minute << 5) | sec); + pd->date = (word) ( (year << 9) | (month << 5) | day); + + return (pd); +} +/*--- ctr modified ---*/ +/*SDK_WEAK_SYMBOL BOOL rtcGetDateTimeSync(RtcDate *pDate, RtcTime *pTime) +{ + pTime->hour = 0; + pTime->minute = 0; + pTime->second = 0; + pDate->year = 2007; + pDate->month = 1; + pDate->day = 1; + return( FALSE); +}*/ +/*--------------------*/ + +/* This routine must establish an interrupt handler that will call the +plain 'C' routine void mgmt_isr(void) when the the chip's management +interrupt event occurs. The value of the argument 'irq' is the interrupt +number that was put into the 82365 management interrupt selection register +and is between 0 and 15. This is controlled by the constant +"MGMT_INTERRUPT" which is defined in pcmctrl.c +*/ +#if (INCLUDE_82365_PCMCTRL) + +/* The routine mgmt_isr(0) must execute when the PCMCIA controller interrupts + this a sample interrupt service routne that will do this, modify it + to match your system's interrupt behavior */ +void mgmt_isr(int); +void pcmcia_isr() +{ + PRINTDEBUG( "not implemented function ( pcmcia_isr) is called\n"); + mgmt_isr(0); +} + +/* This routine must "hook" the interrupt so the above code is jumped to + when the PCMCIA controller interrupts */ +void hook_82365_pcmcia_interrupt(int irq) +{ + PRINTDEBUG( "not implemented function ( hook_82365_pcmcia_interrupt) is called\n"); +} + +#endif /* (INCLUDE_82365_PCMCTRL) */ + +#if (INCLUDE_IDE) +/* The routine ide_isr(controller_number) must execute when the IDE + controller associated with that contoller number interrupts. + these are sample interrupt service routines that will do this, modify + them to match your system's interrupt behavior */ + +void ide_isr(int); +/* This is a sample interrupt service routine for controller 0 */ +void ide_isr_0() +{ + PRINTDEBUG( "not implemented function ( ide_isr_0) is called\n"); + ide_isr(0); +} + +/* This is a sample interrupt service routine for controller 1 */ +void ide_isr_1() +{ + PRINTDEBUG( "not implemented function ( ide_isr_1) is called\n"); + ide_isr(1); +} + + +/* hook_ide_interrupt() is called with the interrupt number in the argument +irq taken from the user's setting of pdr->interrupt_number in pc_ertfs_init(). +Controller number is taken from the pdr->controller_number field as set in +pc_ertfs_init() by the user. Hook_ide_interrupt() must establish an interrupt + handler such that the plain 'C' function "void ide_isr(int controller_number)" +is called when the IDE interrupt occurs. The argument to ide_isr() must be +the controller number that was passed to hook_ide_interrupt(), this value +is typically zero for single controller system. + +hook_ide_interrupt()は、pc_ertfs_init()内で定義されたpdr->interrupt_number +番号を引数(irq)としてコールされる。 +controller_numberはpc_ertfs_init()内でpdr->controller_numberとして定義され +たもの。 +*/ + +void hook_ide_interrupt(int irq, int controller_number) +{ + PRINTDEBUG( "not implemented function ( hook_ide_interrupt) is called\n"); +} + +#endif /* (INCLUDE_IDE) */ + +/* This routine is called by the floppy disk device driver. It must +establish an interrupt handler such that the plain 'C' function void +floppy_isr(void) is called when the floppy disk interrupt occurs. +The value in "irq" is always 6, this is the PC's standard mapping of +the floppy interrupt. If this is not correct for your system just ignore +the irq argument. +*/ +#if (INCLUDE_FLOPPY) +/* This is a sample floppy disk interrupt handler. Modify it to match + your target system */ +void floppy_isr(int); + +void floppy_interrupt() +{ + PRINTDEBUG( "not implemented function ( floppy_interrupt) is called\n"); + floppy_isr(0); +} + +void hook_floppy_interrupt(int irq) +{ + PRINTDEBUG( "not implemented function ( hook_floppy_interrupt) is called\n"); +} + +#endif /* (INCLUDE_FLOPPY) */ + + + diff --git a/build/libraries/fatfs/ARM7/portmain.c b/build/libraries/fatfs/ARM7/portmain.c new file mode 100644 index 0000000..167e838 --- /dev/null +++ b/build/libraries/fatfs/ARM7/portmain.c @@ -0,0 +1,52 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright Peter Van Oudenaren , 1993 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* +* Implementation for the generic porting layer +*/ +/* +***************************************************************************** + Portmain.c - Main entry point RTOS specific + + This file contains the main entry point for the executable. It is + required only if building a stand alone application like our demo. + Several examples of entry point are provided for different operating + systems. The only requirement of the startup module is to call + rtfs_app_entry() from within the contents of a thread. (or of the + main thread for OS's like DOS or UNIX. (see rtfsdemo.c for rtfs_app_entry) + + Note: All that is required to use ERTFS is to call pc_rtfs_init() before + using the API This file is required only if building the RTFS DEMO + program as a stand alone application. + If you wish to use the RTFS DEMO program from your existing framework + from your existing simply call rtfs_app_entry(). + If you wish to use the RTFS library from your existing framework + from your existing simply call pc_rtfs_init() and then make calls + to the ERTFS API. + +*****************************************************************************/ + +#include + +// ******************************************************************** +// THIS TASK IS THE MAIN PROGRAM +// ******************************************************************** + +void rtfs_app_entry(void); +/* +void main() +{ + rtfs_app_entry(); +} +*/ +int main() +{ + rtfs_app_entry(); + + return( 0); +} diff --git a/build/libraries/fatfs/ARM7/prapipro.c b/build/libraries/fatfs/ARM7/prapipro.c new file mode 100644 index 0000000..65b6c7a --- /dev/null +++ b/build/libraries/fatfs/ARM7/prapipro.c @@ -0,0 +1,309 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* APIPRO.C - Contains user api level source code for ERTFS-Pro functions. + + The following routines are included: + + pro_buffer_status - Return disk, failsafe and buffer status. + pro_assign_buffer_pool - Assign a private buffer pool to a drive. +*/ + +#include + +/* pro_buffer_status - Return disk and failsafe status. +* +* Summary: +* BOOLEAN pro_buffer_status(byte *drive_name, struct pro_buffer_stats *pstat) +* +* Description: +* +* This routine returns status information about failsafe, the block buffer +* pools and the fat buffer pools. +* The information is returned to the user in the structure of type +* pro_buffer_stats that must be passed to the routine. +* +* If the routine succeeds it will return 0 and the following fields in +* pstat will be poulated: +* +* int failsafe_mode - Failsafe operating mode. +* 0 - Failsafe disabled +* 1 - Failsafe enabled +* dword failsafe_blocks_used +* If failsafe is enabled this the number of blocks currently +* journalled in the FailSafe file. If FailSafe is disabled this +* value is zero. +* dword failsafe_blocks_free +* If failsafe is enabled this the number of blocks still available +* in the FailSafe file for use. If FailSafe is disabled this +* value is zero. +* dword total_block_buffers; +* Total number of directory buffers available. +* dword block_buffers_pending +* Total number of directory blocks scheduled to write but not yet written +* If directory writeback mode is not enabled this value will be zero. +* dword block_buffers_available; +* Number of directory blocks still available to hold pending writes. +* If directory writeback mode is not enabled this value will be zero. +* dword block_buffers_cache_hits; +* Number of block reads so far that were in the cache when the request +* was made. +* dword block_buffers_cache_misses; +* Number of block reads so far that were not in the cache when the request +* was made. +* dword block_buffers_low +* The low water mark or lowest number of block buffers that have been +* available for allocation so far. +* If directory writeback mode is not enabled this value will be zero. +* dword block_buffers_fail +* The number of block allocation failures that have occured due to +* insufficient buffer pool space +* If directory writeback mode is not enabled this value will be zero. +* dword block_buffers_cache_hits; +* Number of block reads so far that were in the cache when the request +* was made. +* dword block_buffers_cache_misses; +* Number of block reads so far that were not in the cache when the request +* was made. +* dword fat_buffer_primary_cache_hits +* Number of fat block accesses so far that were in the fat primary cache +* when the request was made. +* dword fat_buffer_secondary_cache_hits +* Number of fat block accesses so far that were in the fat secondary cache +* when the request was made. +* dword fat_buffer_cache_misses; +* Number of fat block accesses so far that were in the fat secondary cache +* when the request was made. +* dword total_fat_buffers +* Total number of fat directory buffers for this drive. This value is +* determined by the value assigned to prtfs_cfg->cfg_FAT_BUFFER_SIZE[driveno] in apiconfig.c +* dword fat_buffers_pending +* Total number of fat blocks scheduled to write but not yet written +* If FAT writeback mode is not enabled this value will be zero. +* dword fat_buffers_available +* Number of fat blocks still available to store pending writes. +* If FAT writeback mode is not enabled this value will be zero. +* dword fat_buffers_free +* Number of fat buffer blocks that have never been used. If this value +* is non-zero it means no fat swapping has occured. +* dword fat_buffer_cache_loads +* Number of fat block reads so far that were not in the cache when +* the request was made. +* dword fat_buffer_cache_swaps; +* Number of fat block swaps so far. Blocks that were written because they +* contained changed data but the buffer was required in order to load +* another block. +* +* Returns: +* TRUE - Success +* FALSE - Failure +* If FALSE is return errno will be set to one of the following. +* +* PEINVALIDDRIVEID - Drive argument invalid +*/ + +BOOLEAN pro_buffer_status(byte *drive_name, struct pro_buffer_stats *pstat) +{ +#if (INCLUDE_FAILSAFE_CODE) +FAILSAFECONTEXT *pfscntxt; +#endif +int driveno; +DDRIVE *pdr; +FATBUFF *pfblk; + driveno = pc_parse_raw_drive(drive_name); + if (driveno < 0) + { +inval: + rtfs_set_errno(PEINVALIDDRIVEID); + return(FALSE); + } + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr || !(pdr->drive_flags&DRIVE_FLAGS_VALID) ) + goto inval; + + OS_CLAIM_LOGDRIVE(driveno) /* pro_buffer_status Register drive in use */ + rtfs_set_errno(0); +#if (INCLUDE_FAILSAFE_CODE) + pfscntxt = (FAILSAFECONTEXT *) pdr->pfscntxt; + if (!pfscntxt) + { + pstat->failsafe_mode = 0; + pstat->failsafe_blocks_used = 0; + pstat->failsafe_blocks_free = 0; + } + else if (pfscntxt->configuration_flags & FS_MODE_JOURNALING) + { + pstat->failsafe_mode = 1; + pstat->failsafe_blocks_used = pfscntxt->total_blocks_mapped; + pstat->failsafe_blocks_free = pfscntxt->num_remap_blocks - + pfscntxt->total_blocks_mapped; + } + else + { + pstat->failsafe_mode = 2; + pstat->failsafe_blocks_used = pfscntxt->total_blocks_mapped; + pstat->failsafe_blocks_free = pfscntxt->num_remap_blocks - + pfscntxt->total_blocks_mapped; + } +#else + pstat->failsafe_mode = 0; + pstat->failsafe_blocks_used = 0; + pstat->failsafe_blocks_free = 0; +#endif + + OS_CLAIM_FSCRITICAL() + /* Critical since block buffers may be shared among drives */ + pstat->total_block_buffers = pdr->pbuffcntxt->num_blocks; + pstat->block_buffers_pending = 0; +/* for (pblk = pdr->pbuffcntxt->ppopulated_blocks; pblk; pblk = pblk->pnext) */ +/* { */ +/* if (pblk->block_state == DIRBLOCK_COMMITTED) */ +/* pstat->block_buffers_pending += 1; */ +/* } */ + pstat->block_buffers_available = pstat->total_block_buffers - + pstat->block_buffers_pending; + pstat->block_buffers_cache_hits = pdr->pbuffcntxt->stat_cache_hits; + pstat->block_buffers_cache_misses = pdr->pbuffcntxt->stat_cache_misses; + pstat->block_buffers_low = pdr->pbuffcntxt->low_water; + pstat->block_buffers_fail = pdr->pbuffcntxt->num_alloc_failures; + OS_RELEASE_FSCRITICAL() + + pstat->total_fat_buffers + = pdr->fatcontext.num_blocks; + pstat->fat_buffer_primary_cache_hits + = pdr->fatcontext.stat_primary_cache_hits; + pstat->fat_buffer_secondary_cache_hits + = pdr->fatcontext.stat_secondary_cache_hits; + pstat->fat_buffer_cache_loads + = pdr->fatcontext.stat_secondary_cache_loads; + pstat->fat_buffer_cache_swaps + = pdr->fatcontext.stat_secondary_cache_swaps; + + pstat->fat_buffers_free = 0; + for (pfblk = pdr->fatcontext.pfree_blocks;pfblk;pfblk=pfblk->pnext) + pstat->fat_buffers_free += 1; + pstat->fat_buffers_pending = 0; + for (pfblk = pdr->fatcontext.pcommitted_blocks;pfblk;pfblk=pfblk->pnext) + pstat->fat_buffers_pending += 1; + pstat->fat_buffers_available + = pstat->total_fat_buffers - pstat->fat_buffers_pending; + OS_RELEASE_LOGDRIVE(driveno) /* pro_buffer_status release */ + return(TRUE); +} + +/* pro_assign_buffer_pool - Assign a private buffer pool to a drive. +* +* Summary: +* BOOLEAN pro_assign_buffer_pool(byte *drive_name, +* int block_hashtable_size, +* BLKBUFF **block_hash_table, +* int block_buffer_pool_size, +* BLKBUFF *block_buffer_pool_data) +* +* Description: +* +* This routine provides a way to assign a private block +* buffer pool to a named drive. Normally block buffer space +* is shared among all drives in the system, but this function +* allows the caller to assign block buffers that are private to +* the drive. +* +* This function may improve performance by reducing the amount +* of buffer swapping that occurs when multiple drives compete for +* buffers in the common pool. +* +* It is advisable to assign a private buffer pool to a drive +* if writeback block buffering mode is being used. Failure to do so +* may result in block allocation failures occuring when using other +* drives because blocks are being consumed by pending writes. +* +* +* Inputs: +* drive_name - Null terminated drive designator, for example "A:" +* block_buffer_context - User supplied memory for the block buffer context +* block that will be assigned to the drive to be used +* as a private buffer pool management structure. +* This must be a pointer to a structure of type +* BLKBUFFCNTXT that must remain valid for the whole +* session and may not be deallocated. +* block_hashtable_size - You must set this to the size of the block hash +* table. Each entry in the table takes up 4 bytes. +* This value must be a power of two. +* block_hash_table - User supplied memory for the block hash table. +* This must be a pointer to an array of data of type +* BLKBUF *, containing block_hashtable_size elements. +* It must remain valid for the whole session and must +* not be deallocated. +* block_buffer_pool_size-You must set this to the size of the block buffer +* pool. Each entry in buffer pool requires +* approximately 540 bytes. +* block_buffer_pool_data - User supplied memory for the block buffer pool. +* This must be a pointer to an array of data of type +* BLKBUF, containing block_buffer_pool_size elements. +* It must remain valid for the whole session and must +* not be deallocated. +* +* Returns: +* TRUE - Success +* FALSE - Error +* +* If it returns FALSE errno will be set to one of the following: +* +* PEINVALIDPARMS - Invalid parameters either no context block was passed +* or the fields were not initialized correctly. +* PEINVALIDDRIVEID - Invalid drive +* An ERTFS system error +* +*/ + +BOOLEAN pro_assign_buffers(byte *drivename, + BLKBUFFCNTXT *block_buffer_context, + int block_hashtable_size, + BLKBUFF **block_hash_table, + int block_buffer_pool_size, + BLKBUFF *block_buffer_pool_data) +{ +DDRIVE *pdrive; +int driveno; + + if (!pc_parsedrive( &driveno, drivename)) + return (-1); + OS_CLAIM_LOGDRIVE(driveno) /* pro_assign_buffers register drive in use */ + if ( (block_hashtable_size & 0x3) || + (block_buffer_pool_size < 2) || + (!block_hash_table) || + (!block_buffer_pool_data) || + (!block_buffer_context) ) + { + goto bad_args; + } + /* Initialize the block buffer pool */ + if (!pc_initialize_block_pool( + block_buffer_context, + block_buffer_pool_size, + block_buffer_pool_data, + block_hashtable_size, + block_hash_table)) + { +bad_args: + OS_RELEASE_LOGDRIVE(driveno) /* pro_assign_buffers release */ + rtfs_set_errno(PEINVALIDPARMS); + return (-1); + } + + /* Assign a private block buffer pool to the drive */ + pdrive = pc_drno_to_drive_struct(driveno); + /* Make sure no blocks are buffered for this drive */ + pc_free_all_blk(pdrive); + pdrive->pbuffcntxt = block_buffer_context; + + OS_RELEASE_LOGDRIVE(driveno) + return(0); +} + + diff --git a/build/libraries/fatfs/ARM7/prblock.c b/build/libraries/fatfs/ARM7/prblock.c new file mode 100644 index 0000000..b73f62c --- /dev/null +++ b/build/libraries/fatfs/ARM7/prblock.c @@ -0,0 +1,1188 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* PRBLOCK.C - ERTFS-PRO Directory block buffering routines */ + +#include + + +static void pc_add_blk(BLKBUFFCNTXT *pbuffcntxt, BLKBUFF *pinblk); +static void pc_release_blk(BLKBUFFCNTXT *pbuffcntxt, BLKBUFF *pinblk); +static BLKBUFF *pc_allocate_blk(DDRIVE *pdrive, BLKBUFFCNTXT *pbuffcntxt); + + +/* Debugging tools to be removed in he final product */ +#define DEBUG_BLOCK_CODE 0 +#define DEBUG_FAT_CODE 0 +void debug_check_blocks(BLKBUFFCNTXT *pbuffcntxt, int numblocks, char *where); +void debug_check_fat(FATBUFFCNTXT *pfatbuffcntxt, char *where); +void debug_break(char *where, char *message); +#if (DEBUG_BLOCK_CODE) +#define DEBUG_CHECK_BLOCKS(X,Y,Z) debug_check_blocks(X,Y,X); +#else +#define DEBUG_CHECK_BLOCKS(X,Y,Z) +#endif +#if (DEBUG_FAT_CODE) +#define DEBUG_CHECK_FAT(X,Y) debug_check_fat(X,Y); +#else +#define DEBUG_CHECK_FAT(X,Y) +#endif + +#if (!INCLUDE_FAILSAFE_CODE) + +BOOLEAN block_devio_read(DDRIVE *pdrive, dword blockno, byte * buf) +{ + return(devio_read(pdrive->driveno, blockno, buf, (int) 1, FALSE)); +} + +BOOLEAN block_devio_write(BLKBUFF *pblk) +{ +/* dword blockno; + blockno = pblk->blockno;*/ + return(devio_write(pblk->pdrive->driveno,pblk->blockno, pblk->data, (int) 1, FALSE)); +} + +BOOLEAN fat_devio_write(DDRIVE *pdrive, FATBUFF *pblk, int fatnumber) +{ +dword blockno; + if (fatnumber) + blockno = pblk->fat_blockno+pdrive->secpfat; + else + blockno = pblk->fat_blockno; + return(devio_write(pdrive->driveno,blockno, pblk->fat_data, (int) 1, FALSE)); +} + +BOOLEAN fat_devio_read(DDRIVE *pdrive, dword blockno, byte *fat_data) +{ + return(devio_read(pdrive->driveno, blockno, fat_data, (int) 1, FALSE)); +} +#else +/* Replacement routines provided by failsafe package */ +BOOLEAN block_devio_read(DDRIVE *pdrive, dword blockno, byte * buf); +BOOLEAN block_devio_write(BLKBUFF *pblk); +BOOLEAN fat_devio_write(DDRIVE *pdrive, FATBUFF *pblk, int fatnumber); +BOOLEAN fat_devio_read(DDRIVE *pdrive, dword blockno, byte *fat_data); +#endif +/***************************************************************************** + pc_release_buf - Unlock a block buffer. + +Description + Give back a buffer to the system buffer pool so that it may + be re-used. If was_err is TRUE this means that the data in the + buffer is invalid so discard the buffer from the buffer pool. + + Returns + Nothing + +***************************************************************************/ + +void pc_release_buf(BLKBUFF *pblk) +{ + if (!pblk) + return; + DEBUG_CHECK_BLOCKS(pblk->pdrive->pbuffcntxt, pblk->pdrive->pbuffcntxt->num_blocks, "Release") +#if (DEBUG_BLOCK_CODE) + if (!pblk->pdrive->mount_valid) + debug_break("release buf", "Mount not valid"); + if (pblk->block_state != DIRBLOCK_COMMITTED && pblk->block_state != DIRBLOCK_UNCOMMITTED) + debug_break("release buf", "releasing buffer not in use list"); +#endif + + if (pblk->block_state != DIRBLOCK_COMMITTED && pblk->block_state != DIRBLOCK_UNCOMMITTED) + return; + OS_CLAIM_FSCRITICAL() + if (pblk->use_count) + { + pblk->use_count -= 1; + if (!pblk->use_count) + pblk->pdrive->pbuffcntxt->num_free += 1; + } + OS_RELEASE_FSCRITICAL() +} + +/***************************************************************************** + pc_discard_buf - Put a buffer back on the free list. + +Description + Check if a buffer is in the buffer pool, unlink it from the + buffer pool if it is. + Put the buffer on the free list. + + Returns + Nothing + +***************************************************************************/ + +void pc_discard_buf(BLKBUFF *pblk) +{ +BLKBUFFCNTXT *pbuffcntxt; + + if (!pblk) + return; + /*note:pblk->pdrive must not be 0. if so, use pc_free_scratch_buffer() */ +#if (DEBUG_BLOCK_CODE) + if (pblk->block_state == DIRBLOCK_FREE) + { + printf("Warning: discarding a free buffer \n"); + } + if (pblk->block_state == DIRBLOCK_COMMITTED) + { /* Could come from a disk free call */ + printf("Warning discarding a committed buffer \n"); + } +#endif + if (pblk->block_state == DIRBLOCK_FREE) + return; + + OS_CLAIM_FSCRITICAL() + pbuffcntxt = pblk->pdrive->pbuffcntxt; + DEBUG_CHECK_BLOCKS(pbuffcntxt, pbuffcntxt->num_blocks, "Discard 1") + if (pblk->block_state == DIRBLOCK_COMMITTED || pblk->block_state == DIRBLOCK_UNCOMMITTED) + { + /* Release it from the buffer pool */ + pc_release_blk(pbuffcntxt, pblk); +#if (DEBUG_BLOCK_CODE) + if (pblk->pnext && pblk->pnext->pprev != pblk) + debug_break("discard buf", "Buffer and populated pool inconsistent"); + if (pblk->pprev && pblk->pprev->pnext != pblk) + debug_break("discard buf", "Buffer and populated pool inconsistent"); +#endif + /* Unlink it from the populated pool double check link integrity */ + if (pblk->pnext && pblk->pnext->pprev == pblk) + pblk->pnext->pprev = pblk->pprev; + if (pblk->pprev && pblk->pprev->pnext == pblk) + pblk->pprev->pnext = pblk->pnext; + if (pbuffcntxt->ppopulated_blocks == pblk) + pbuffcntxt->ppopulated_blocks = pblk->pnext; + } + pblk->block_state = DIRBLOCK_FREE; + pblk->pnext = pbuffcntxt->pfree_blocks; + pbuffcntxt->num_free += 1; + pbuffcntxt->pfree_blocks = pblk; + OS_RELEASE_FSCRITICAL() + DEBUG_CHECK_BLOCKS(pbuffcntxt, pbuffcntxt->num_blocks, "Discard 2") +} + +/**************************************************************************** + PC_READ_BLK - Allocate and read a BLKBUFF, or get it from the buffer pool. + +Description + Use pdrive and blockno to determine what block to read. Read the block + or get it from the buffer pool and return the buffer. + + Note: After reading, you own the buffer. You must release it by + calling pc_release_buff() or pc_discard_buff() before it may be + used for other blocks. + + Returns + Returns a valid pointer or NULL if block not found and not readable. + +*****************************************************************************/ + +BLKBUFF *pc_read_blk(DDRIVE *pdrive, dword blockno) /*__fn__*/ +{ +BLKBUFF *pblk; +BLKBUFFCNTXT *pbuffcntxt; + + if ( !pdrive || (blockno >= pdrive->numsecs) ) + return(0); + OS_CLAIM_FSCRITICAL() + DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 1") + /* TRy to find it in the buffer pool */ + pblk = pc_find_blk(pdrive, blockno); + if (pblk) + { + pdrive->pbuffcntxt->stat_cache_hits += 1; + DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 2") + pblk->use_count += 1; + OS_RELEASE_FSCRITICAL() + } + else + { + pdrive->pbuffcntxt->stat_cache_misses += 1; + /* Allocate, read, stitch into the populated list, and the buffer pool */ + pblk = pc_allocate_blk(pdrive, pdrive->pbuffcntxt); + OS_RELEASE_FSCRITICAL() + if (pblk) + { + if (block_devio_read(pdrive, blockno, pblk->data)) + { + pbuffcntxt = pdrive->pbuffcntxt; + pblk->use_count = 1; + pblk->block_state = DIRBLOCK_UNCOMMITTED; + pblk->blockno = blockno; + pblk->pdrive = pdrive; + /* Put in front of populated list (it's new) */ + OS_CLAIM_FSCRITICAL() + pblk->pprev = 0; + pblk->pnext = pbuffcntxt->ppopulated_blocks; + if (pbuffcntxt->ppopulated_blocks) + pbuffcntxt->ppopulated_blocks->pprev = pblk; + pbuffcntxt->ppopulated_blocks = pblk; + pc_add_blk(pbuffcntxt, pblk); /* Add it to the buffer pool */ + DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 3") + OS_RELEASE_FSCRITICAL() + } + else + { + /* set errno to IO error unless devio set PEDEVICE */ + if (!get_errno()) + rtfs_set_errno(PEIOERRORREADBLOCK); /* pc_read_blk device read error */ + pc_discard_buf(pblk); + DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 4") + pblk = 0; + } + } + } + DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Read 5") + return(pblk); +} +/*************************************************************************** + PC_SCRATCH_BLK - Return a block for scratch purposes. +Description + Use the block buffer pool as a heap of 512 byte memory locations + When done call void pc_free_scratch_buf(pblk) to clean up + + Returns + Returns a blkbuf if one is available or NULL +****************************************************************************/ +/* Allocate a scratch buffer from the shared (amongst drives) buffer pool */ +BLKBUFF *pc_scratch_blk(void) /*__fn__*/ +{ +BLKBUFF *pblk; + OS_CLAIM_FSCRITICAL() + pblk = pc_allocate_blk(0, &prtfs_cfg->buffcntxt); + OS_RELEASE_FSCRITICAL() + return (pblk); +} + +/* Free a scratch buffer to the shared (amongst drives) buffer pool */ +void pc_free_scratch_blk(BLKBUFF *pblk) +{ + BLKBUFFCNTXT *pbuffcntxt; + OS_CLAIM_FSCRITICAL() + pbuffcntxt = &prtfs_cfg->buffcntxt; + pblk->pnext = pbuffcntxt->pfree_blocks; + pbuffcntxt->pfree_blocks = pblk; + pblk->block_state = DIRBLOCK_FREE; + pbuffcntxt->num_free += 1; + OS_RELEASE_FSCRITICAL() +} + + +/*************************************************************************** + PC_INIT_BLK - Zero a BLKBUFF and add it to the buffer pool +Description + Allocate and zero a BLKBUFF and add it to the to the buffer pool. + + Note: After initializing you own the buffer. You must release it by + calling pc_release_buff() or pc_discard_buf() before it may be used + for other blocks. + + Returns + Returns a valid pointer or NULL if no core. + +****************************************************************************/ + +BLKBUFF *pc_init_blk(DDRIVE *pdrive, dword blockno) /*__fn__*/ +{ +BLKBUFF *pblk; +BLKBUFFCNTXT *pbuffcntxt; + + if ( !pdrive || (blockno >= pdrive->numsecs) ) + return(0); + /* Find it in the buffer pool */ + OS_CLAIM_FSCRITICAL() + DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Init 1") + pblk = pc_find_blk(pdrive, blockno); + if (pblk) + { + pblk->use_count += 1; + OS_RELEASE_FSCRITICAL() + } + else + { + /* Allocate, read, stitch into the populated list, and the buffer pool */ + DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Init 2") + pblk = pc_allocate_blk(pdrive, pdrive->pbuffcntxt); + + OS_RELEASE_FSCRITICAL() + if (pblk) + { + DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks-1, "Init 2_a") + pbuffcntxt = pdrive->pbuffcntxt; + pblk->use_count = 1; + pblk->block_state = DIRBLOCK_UNCOMMITTED; + pblk->blockno = blockno; + pblk->pdrive = pdrive; + /* Put in front of populated list (it's new) */ + OS_CLAIM_FSCRITICAL() + pblk->pprev = 0; + pblk->pnext = pbuffcntxt->ppopulated_blocks; + if (pbuffcntxt->ppopulated_blocks) + pbuffcntxt->ppopulated_blocks->pprev = pblk; + pbuffcntxt->ppopulated_blocks = pblk; + pc_add_blk(pbuffcntxt, pblk); /* Add it to the buffer pool */ + OS_RELEASE_FSCRITICAL() + DEBUG_CHECK_BLOCKS(pdrive->pbuffcntxt, pdrive->pbuffcntxt->num_blocks, "Init 3") + } + } + if (pblk) + rtfs_memset(pblk->data, (byte) 0, 512); + return(pblk); +} + +/**************************************************************************** + PC_FREE_ALL_BLK - Release all buffers associated with a drive +Description + Use pdrive to find all buffers in the buffer pool associated with the + drive. Mark them as unused, called by dsk_close. + If any are locked, print a debug message in debug mode to warn the + programmer. + + Returns + Nothing +****************************************************************************/ +void pc_free_all_blk(DDRIVE *pdrive) /*__fn__*/ +{ +BLKBUFFCNTXT *pbuffcntxt; +BLKBUFF *pblk; +BOOLEAN deleting; + + if (!pdrive) + return; + pbuffcntxt = pdrive->pbuffcntxt; + DEBUG_CHECK_BLOCKS(pbuffcntxt, pbuffcntxt->num_blocks, "Free all 1") + do + { + deleting = FALSE; + OS_CLAIM_FSCRITICAL() + pblk = pbuffcntxt->ppopulated_blocks; + while (pblk) + { + if (pblk->pdrive == pdrive) + { + OS_RELEASE_FSCRITICAL() + pc_discard_buf(pblk); + OS_CLAIM_FSCRITICAL() + deleting = TRUE; + break; + } + else + pblk = pblk->pnext; + } + OS_RELEASE_FSCRITICAL() + } while (deleting); + DEBUG_CHECK_BLOCKS(pbuffcntxt, pbuffcntxt->num_blocks, "Free all 2") +} + +/*************************************************************************** + PC_WRITE_BLK - Flush a BLKBUFF to disk. +Description + Use pdrive and blockno information in pblk to flush its data buffer + to disk. + + Returns + Returns TRUE if the write succeeded. +****************************************************************************/ + +/* Write */ +BOOLEAN pc_write_blk(BLKBUFF *pblk) /*__fn__*/ +{ + if (!block_devio_write(pblk)) + { + /* set errno to IO error unless devio set PEDEVICE */ + if (!get_errno()) + rtfs_set_errno(PEIOERRORWRITEBLOCK); /* pc_write_blk device write error */ + return(FALSE); + } + else + return(TRUE); +} + +/* Add a block to the block buffer pool */ +static void pc_add_blk(BLKBUFFCNTXT *pbuffcntxt, BLKBUFF *pinblk) +{ +int hash_index; + hash_index = (int) (pinblk->blockno&pbuffcntxt->hash_mask); + pinblk->pnext2 = *(pbuffcntxt->blk_hash_tbl+hash_index); + *(pbuffcntxt->blk_hash_tbl+hash_index) = pinblk; +} +/* Remove a block from the block buffer pool */ +static void pc_release_blk(BLKBUFFCNTXT *pbuffcntxt, BLKBUFF *pinblk) +{ +int hash_index; +BLKBUFF *pblk; + + hash_index = (int) (pinblk->blockno&pbuffcntxt->hash_mask); + pblk = *(pbuffcntxt->blk_hash_tbl+hash_index); + if (pblk == pinblk) + *(pbuffcntxt->blk_hash_tbl+hash_index) = pinblk->pnext2; + else + { + while (pblk) + { + if (pblk->pnext2==pinblk) + { + pblk->pnext2 = pinblk->pnext2; + break; + } + pblk = pblk->pnext2; + } + } +} +/* Find a block in the block buffer pool */ +BLKBUFF *pc_find_blk(DDRIVE *pdrive, dword blockno) +{ +BLKBUFFCNTXT *pbuffcntxt; +int hash_index; +BLKBUFF *pblk; + + pbuffcntxt = pdrive->pbuffcntxt; + hash_index = (int) (blockno&pbuffcntxt->hash_mask); + pblk = *(pbuffcntxt->blk_hash_tbl+hash_index); + while (pblk) + { + if (pblk->blockno == blockno && pblk->pdrive == pdrive) + { + if (pblk->block_state == DIRBLOCK_UNCOMMITTED) + { /* Put in front of populated list (it's the last touched) */ + /* Unlink it from the populated pool */ + if (pbuffcntxt->ppopulated_blocks != pblk) + { /* Since we aren't the root we know pprev is good */ + pblk->pprev->pnext = pblk->pnext; /* Unlink. */ + if (pblk->pnext) + pblk->pnext->pprev = pblk->pprev; + pblk->pprev = 0; /* link in front*/ + pblk->pnext = pbuffcntxt->ppopulated_blocks; + if (pbuffcntxt->ppopulated_blocks) + pbuffcntxt->ppopulated_blocks->pprev = pblk; + pbuffcntxt->ppopulated_blocks = pblk; + } + } + return(pblk); + } + pblk=pblk->pnext2; + } + return(0); +} +/* Allocate a block or re-use an un-committed one */ +static BLKBUFF *pc_allocate_blk(DDRIVE *pdrive, BLKBUFFCNTXT *pbuffcntxt) +{ +BLKBUFF *pblk, *pblkscan; +int num_free; + /* Note: pdrive may be NULL, do not dereference the pointer */ + pblk = 0; + if (pbuffcntxt->pfree_blocks) + { + pblk = pbuffcntxt->pfree_blocks; + pbuffcntxt->pfree_blocks = pblk->pnext; + } + else if (pbuffcntxt->ppopulated_blocks) + { + /* Find the oldest UNCOMMITED block (deepest into the list) */ + pblkscan = pbuffcntxt->ppopulated_blocks; + num_free = 0; + while (pblkscan) + { + if (pblkscan->block_state == DIRBLOCK_UNCOMMITTED && !pblkscan->use_count) + { + pblk = pblkscan; + num_free += 1; + } + pblkscan = pblkscan->pnext; + } + pbuffcntxt->num_free = num_free; + if (pblk) + { + pc_release_blk(pbuffcntxt, pblk); /* Remove it from buffer pool */ + /* Unlink it from the populated pool */ + if (pblk->pnext) + pblk->pnext->pprev = pblk->pprev; + if (pblk->pprev) + pblk->pprev->pnext = pblk->pnext; + if (pbuffcntxt->ppopulated_blocks == pblk) + pbuffcntxt->ppopulated_blocks = pblk->pnext; + } + } + if (pblk) + { /* Put in a known state */ + pbuffcntxt->num_free -= 1; + if (pbuffcntxt->num_free < pbuffcntxt->low_water) + pbuffcntxt->low_water = pbuffcntxt->num_free; + pblk->use_count = 0; + pblk->block_state = DIRBLOCK_ALLOCATED; + pblk->pdrive = pdrive; + } + else + { + pbuffcntxt->num_alloc_failures += 1; + rtfs_set_errno(PERESOURCEBLOCK); /* pc_allocate_blk out of resources */ + } + return(pblk); +} +/* Tomo */ +/* Traverse a cluster chain and make sure that all blocks in the cluster + chain are flushed from the buffer pool. This is required when deleting + a directory since it is possible, although unlikely, that blocks used in + the directory may be used in a file. This may cause the buffered + block to be different from on-disk block. + Called by pc_rmnode +*/ +void pc_flush_chain_blk(DDRIVE *pdrive, CLUSTERTYPE cluster) +{ +int i; +dword blockno; +BLKBUFF *pblk; + + if ( cluster < 2 || cluster > pdrive->maxfindex) + return; + while (cluster != 0xffffffff) /* End of chain */ + { + blockno = pc_cl2sector(pdrive, cluster); + if (blockno) + { + for (i = 0; i < pdrive->secpalloc; i++, blockno++) + { + /* Find it in the buffer pool */ + OS_CLAIM_FSCRITICAL() + pblk = pc_find_blk(pdrive, blockno); + if (pblk) + pblk->use_count += 1; + OS_RELEASE_FSCRITICAL() + if (pblk) + { + pc_discard_buf(pblk); + } + } + } + /* Consult the fat for the next cluster */ + cluster = FATOP(pdrive)->fatop_clnext(pdrive, cluster); + if (cluster == 0) /* clnext detected error */ + break; + } +} + +/* Initialize and populate a block buffer context structure */ +BOOLEAN pc_initialize_block_pool(BLKBUFFCNTXT *pbuffcntxt, int nblkbuffs, + BLKBUFF *pmem_block_pool, int blk_hashtble_size, BLKBUFF **pblock_hash_table) +{ +int i; +dword t; +BLKBUFF *pblk; + rtfs_memset(pbuffcntxt,(byte) 0, sizeof(BLKBUFFCNTXT)); + rtfs_memset(pblock_hash_table,(byte) 0, sizeof(BLKBUFF *)*blk_hashtble_size); + pblk = pmem_block_pool; + for (i = 0; i < (nblkbuffs-1); i++, pblk++) + { + rtfs_memset(pblk,(byte) 0, sizeof(BLKBUFF)); + pblk->pnext = pblk+1; + } + rtfs_memset(pblk,(byte) 0, sizeof(BLKBUFF)); + /* pblk->pnext = 0; accomplished by memset */ + pbuffcntxt->pfree_blocks = pmem_block_pool; + pbuffcntxt->hash_size = blk_hashtble_size; + /* Take size and subtract 1 to get the mask */ + t = (dword) blk_hashtble_size; + pbuffcntxt->hash_mask = t-1; + pbuffcntxt->blk_hash_tbl = pblock_hash_table; + pbuffcntxt->num_blocks = nblkbuffs; + pbuffcntxt->num_free = nblkbuffs; + pbuffcntxt->low_water = nblkbuffs; + pbuffcntxt->num_alloc_failures = 0; + return(TRUE); +} + +FATBUFF *pc_find_fat_blk(FATBUFFCNTXT *pfatbuffcntxt, dword blockno); +static void pc_commit_fat_blk(FATBUFFCNTXT *pfatbuffcntxt, FATBUFF *pblk); +void pc_commit_fat_table(FATBUFFCNTXT *pfatbuffcntxt); +void pc_sort_committed_blocks(FATBUFFCNTXT *pfatbuffcntxt); + + +BOOLEAN pc_flush_fat_blocks(DDRIVE *pdrive) +{ +FATBUFFCNTXT *pfatbuffcntxt; +FATBUFF *pblk, *plast; +int hash_index; +dword b; + + pfatbuffcntxt = &pdrive->fatcontext; + DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 1") + /* Make sure the primary cache is is sync with secondary */ + pc_commit_fat_table(pfatbuffcntxt); + DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 2") + if (!pfatbuffcntxt->pcommitted_blocks) + return(TRUE); + /* Sort the blocks in ascending order */ + DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 3") + pc_sort_committed_blocks(pfatbuffcntxt); + DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 4") + pblk = pfatbuffcntxt->pcommitted_blocks; + while (pblk) + { + if (!fat_devio_write(pdrive,pblk,0)) + { + /* set errno to PEDEVICE unless devio set errno */ + if (!get_errno()) + rtfs_set_errno(PEIOERRORWRITEFAT); /* flush_fat device write error */ +io_error: + pc_report_error(PCERR_FAT_FLUSH); + return(FALSE); + } + else + pblk = pblk->pnext; + } + if (pdrive->numfats >= 2) + { /* Write the second copy */ + pblk = pfatbuffcntxt->pcommitted_blocks; + while (pblk) + { + if (!fat_devio_write(pdrive,pblk,1)) + { + /* set errno to PEDEVICE unless devio set errno */ + if (!get_errno()) + rtfs_set_errno(PEIOERRORWRITEFAT); /* flush_fat device write error */ + goto io_error; + } + else + pblk = pblk->pnext; + } + } + /* Set all entries uncommitted and put them in front of uncommitted list */ + DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 5") + plast = 0; + pblk = pfatbuffcntxt->pcommitted_blocks; + while (pblk) + { + pblk->fat_block_state = FATBLOCK_UNCOMMITTED; + + /* Clear from the primary cache if needed */ + hash_index = (int) (pblk->fat_blockno&pfatbuffcntxt->hash_mask); + b = *(pfatbuffcntxt->mapped_blocks+hash_index); + if ( (b & 0x0ffffffful) == pblk->fat_blockno) + { + b &= 0x3ffffffful; /* Clear 0x40000000ul and 0x80000000ul*/ + *(pfatbuffcntxt->mapped_blocks+hash_index) = b; + } + plast = pblk; + pblk = pblk->pnext; + } + if (pfatbuffcntxt->puncommitted_blocks) + pfatbuffcntxt->puncommitted_blocks->pprev = plast; + plast->pnext = pfatbuffcntxt->puncommitted_blocks; + /* pfatbuffcntxt->pcommitted_blocks->pprev = 0; - this is already true*/ + pfatbuffcntxt->puncommitted_blocks = pfatbuffcntxt->pcommitted_blocks; + pfatbuffcntxt->pcommitted_blocks = 0; + DEBUG_CHECK_FAT(pfatbuffcntxt, "Flush 6") + return(TRUE); +} + +byte *pc_map_fat_block(DDRIVE *pdrive, dword blockno, dword usage_flags) +{ +int hash_index, temp_hash_index; +FATBUFFCNTXT *pfatbuffcntxt; +FATBUFF *pblk, *pblkscan; +dword b; + pfatbuffcntxt = &pdrive->fatcontext; + DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 1") + hash_index = (int) (blockno&pfatbuffcntxt->hash_mask); + b = *(pfatbuffcntxt->mapped_blocks+hash_index); + if ( (b & 0x0ffffffful) == blockno) + { + pfatbuffcntxt->stat_primary_cache_hits += 1; +#if (INCLUDE_FAILSAFE_CODE) + /* If in the primary cache and the new flag is write but the old + flag was not write then we will need to journal the block */ + if ((usage_flags & 0x80000000ul) && (!(b & 0x80000000ul))) + if (pro_failsafe_journal_full(pdrive)) + return(0); /* Failsafe sets errno */ +#endif + if (usage_flags) /* bit 31 0x80000000ul is write bit 29 0x20000000ul is lock */ + *(pfatbuffcntxt->mapped_blocks+hash_index) |= usage_flags; + return (*(pfatbuffcntxt->mapped_data+hash_index)); + } + else + { /* Swap out of the primary cache */ + if ( (b & 0xc0000000ul) == 0x80000000ul) + { /* Move to commit list if state changed from not_for_write to for_write */ + pblk = pc_find_fat_blk(pfatbuffcntxt, (b & 0x0ffffffful)); + if (pblk) /* This should not fail */ + pc_commit_fat_blk(pfatbuffcntxt, pblk); + DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 2") + *(pfatbuffcntxt->mapped_blocks+hash_index) = 0; + } + /* See if blockno is in the secondary cache */ + pblk = pc_find_fat_blk(pfatbuffcntxt, blockno); + if (pblk) + { + pfatbuffcntxt->stat_secondary_cache_hits += 1; + DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 3") + b = blockno; + if (pblk->fat_block_state == FATBLOCK_COMMITTED) + { + b |= 0x40000000ul; + } +#if (INCLUDE_FAILSAFE_CODE) + else + { + /* If in the primary cache but uncommitted, if it will + switch to committed now then we will have to journal the block */ + if (usage_flags & 0x80000000ul) + { + if (pro_failsafe_journal_full(pdrive)) + return(0); /* Failsafe sets errno */ + } + } +#endif + if (usage_flags) /* bit 31 0x80000000ul is write bit 29 0x20000000ul is lock */ + b |= usage_flags; + *(pfatbuffcntxt->mapped_blocks+hash_index) = b; + *(pfatbuffcntxt->mapped_data+hash_index) = pblk->fat_data; + return(pblk->fat_data); + } + /* Not in secondary. Are there any free ? */ + if (!pfatbuffcntxt->pfree_blocks) + { + /* Make sure commit state changes in the fat table are up to date */ + /* Since hhere may committed blocks in the primary, but not secondary */ + pc_commit_fat_table(pfatbuffcntxt); + DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 4") + if (!pfatbuffcntxt->puncommitted_blocks) + { /* No uncommitted blocks left */ + /* Write the oldest committed block to disk */ + /* So we can re-use the buffer space for another block */ + pblk = pfatbuffcntxt->pcommitted_blocks; + while (pblk && pblk->pnext) /* Go to end of list */ + { + pblk = pblk->pnext; + } + if (pblk) + { /* Found one */ + pfatbuffcntxt->stat_secondary_cache_swaps += 1; + if (!fat_devio_write(pdrive,pblk,0)) + { +swap_write_error: + if (!get_errno()) + rtfs_set_errno(PEIOERRORWRITEFAT); /* pc_map_fat_block device write error */ + return(0); + } + if (pdrive->numfats >= 2) + { /* Write the second copy */ + if (!fat_devio_write(pdrive,pblk,1)) + goto swap_write_error; + } + /* Remove it from committed list */ + if (pblk->pnext) + pblk->pnext->pprev = pblk->pprev; + if (pblk->pprev) + pblk->pprev->pnext = pblk->pnext; + if (pblk == pfatbuffcntxt->pcommitted_blocks) + pfatbuffcntxt->pcommitted_blocks = pblk->pnext; + pfatbuffcntxt->stat_secondary_cache_swaps += 1; + } + } + else + { + /* Find the oldest UNCOMMITED block (deepest into the list) */ + pblk = pfatbuffcntxt->puncommitted_blocks; + while (pblk && pblk->pnext) /* Go to end of list */ + { + pblk = pblk->pnext; + } + if (pblk) + { /* Found one */ + /* Remove it from uncommitted list */ + if (pblk->pnext) + pblk->pnext->pprev = pblk->pprev; + if (pblk->pprev) + pblk->pprev->pnext = pblk->pnext; + if (pblk == pfatbuffcntxt->puncommitted_blocks) + pfatbuffcntxt->puncommitted_blocks = pblk->pnext; + } + } + if (pblk) /* Came from either the committed or uncommited list */ + { + /* Clear from the primary cache if needed */ + temp_hash_index = (int) (pblk->fat_blockno&pfatbuffcntxt->hash_mask); + b = *(pfatbuffcntxt->mapped_blocks+temp_hash_index); + if ( (b & 0x0ffffffful) == pblk->fat_blockno) + *(pfatbuffcntxt->mapped_blocks+temp_hash_index) = 0; + /* Now get it out of the hash table */ + if (pblk == *(pfatbuffcntxt->fat_blk_hash_tbl+temp_hash_index)) + *(pfatbuffcntxt->fat_blk_hash_tbl+temp_hash_index) = pblk->pnext2; + else + { + pblkscan = *(pfatbuffcntxt->fat_blk_hash_tbl+temp_hash_index); + while (pblkscan) + { + if (pblkscan->pnext2==pblk) + { + pblkscan->pnext2 = pblk->pnext2; + break; + } + pblkscan = pblkscan->pnext2; + } + } + /* Put it on the free list */ + pblk->pnext = pfatbuffcntxt->pfree_blocks; + pfatbuffcntxt->pfree_blocks = pblk; + DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 5") + } + } + else + { /* the free list already had a block available for us */ + pfatbuffcntxt->stat_secondary_cache_loads += 1; + } + pblk = pfatbuffcntxt->pfree_blocks; + if (!pblk) + { + /* No blocks available. We tried to flush the secondary and + that didn't come up with any so we're completely out */ + rtfs_set_errno(PERESOURCEFATBLOCK); + return(0); + } + else if (fat_devio_read(pdrive, blockno, pblk->fat_data)) + { /* The read worked. Unhook from the free list */ + pfatbuffcntxt->stat_secondary_cache_loads += 1; + DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 6") + pfatbuffcntxt->pfree_blocks = pblk->pnext; + pblk->fat_block_state = FATBLOCK_UNCOMMITTED; + pblk->fat_blockno = blockno; + /* Move to the front of the list so we don't swap too soon */ + if (pfatbuffcntxt->puncommitted_blocks) + pfatbuffcntxt->puncommitted_blocks->pprev = pblk; + pblk->pprev = 0; + pblk->pnext = pfatbuffcntxt->puncommitted_blocks; + pfatbuffcntxt->puncommitted_blocks = pblk; + /* Insert in the hash table */ + pblk->pnext2 = *(pfatbuffcntxt->fat_blk_hash_tbl+hash_index); + *(pfatbuffcntxt->fat_blk_hash_tbl+hash_index) = pblk; + if (usage_flags) /* bit 31 0x80000000ul is write bit 29 0x20000000ul is lock */ + blockno |= usage_flags; + *(pfatbuffcntxt->mapped_blocks+hash_index) = blockno; + *(pfatbuffcntxt->mapped_data+hash_index) = pblk->fat_data; + DEBUG_CHECK_FAT(pfatbuffcntxt, "Map 7") +#if (INCLUDE_FAILSAFE_CODE) + /* We are coming from the FREE state, but write is + set so journal the buffer */ + if (usage_flags & 0x80000000ul) + { + if (pro_failsafe_journal_full(pdrive)) + return(0); /* Failsafe sets errno */ + } +#endif + return(pblk->fat_data); + } + else + { + if (!get_errno()) + rtfs_set_errno(PEIOERRORREADFAT); /* pc_map_fat_block device write error */ + return(0); + } + } +} + +BOOLEAN pc_initialize_fat_block_pool(FATBUFFCNTXT *pfatbuffcntxt, + int fat_buffer_size, FATBUFF *pfat_buffers, + int fat_hashtbl_size, FATBUFF **pfat_hash_table, + byte **pfat_primary_cache, dword *pfat_primary_index) +{ +dword t; + + pfatbuffcntxt->pfat_buffers = pfat_buffers; /* save the address of buffer pool */ + pfatbuffcntxt->mapped_data = pfat_primary_cache; + pfatbuffcntxt->mapped_blocks = pfat_primary_index; + pfatbuffcntxt->fat_blk_hash_tbl = pfat_hash_table; + pfatbuffcntxt->hash_size = fat_hashtbl_size; + /* Take size and subtract 1 to get the mask */ + t = (dword) fat_hashtbl_size; + pfatbuffcntxt->hash_mask = t-1; + pfatbuffcntxt->num_blocks = fat_buffer_size; + /* Now clear it to the NO cached state */ + pc_free_all_fat_blocks(pfatbuffcntxt); + return(TRUE); +} + +/* Reset it fat buffer cache */ +void pc_free_all_fat_blocks(FATBUFFCNTXT *pfatbuffcntxt) +{ +int i; +FATBUFF *pblk; + + /* Clear stats */ + pfatbuffcntxt->stat_primary_cache_hits = + pfatbuffcntxt->stat_secondary_cache_hits = + pfatbuffcntxt->stat_secondary_cache_loads = + pfatbuffcntxt->stat_secondary_cache_swaps = 0; + + /* Clear hash tables */ + rtfs_memset(pfatbuffcntxt->fat_blk_hash_tbl,0, sizeof(FATBUFF *)*pfatbuffcntxt->hash_size); + rtfs_memset(pfatbuffcntxt->mapped_data,(byte) 0, sizeof(byte *)*pfatbuffcntxt->hash_size); + rtfs_memset(pfatbuffcntxt->mapped_blocks,(byte) 0, sizeof(dword)*pfatbuffcntxt->hash_size); + /* Reset block lists */ + pfatbuffcntxt->puncommitted_blocks = 0; + pfatbuffcntxt->pcommitted_blocks = 0; + /* Regenerate the free list */ + pfatbuffcntxt->pfree_blocks = pfatbuffcntxt->pfat_buffers; + pblk = pfatbuffcntxt->pfat_buffers; + for (i = 0; i < (pfatbuffcntxt->num_blocks-1); i++, pblk++) + { + rtfs_memset(pblk,(byte) 0, sizeof(FATBUFF)); + pblk->pnext = pblk+1; + } + rtfs_memset(pblk,(byte) 0, sizeof(FATBUFF)); + pblk->pnext = 0; +} + +FATBUFF *pc_find_fat_blk(FATBUFFCNTXT *pfatbuffcntxt, dword blockno) +{ +int hash_index; +FATBUFF *pblk; + + hash_index = (int) (blockno&pfatbuffcntxt->hash_mask); + pblk = *(pfatbuffcntxt->fat_blk_hash_tbl+hash_index); + while (pblk) + { + if (pblk->fat_blockno == blockno) + return(pblk); + else + pblk=pblk->pnext2; + } + return(0); +} + +static void pc_commit_fat_blk(FATBUFFCNTXT *pfatbuffcntxt, FATBUFF *pblk) +{ +#if (DEBUG_FAT_CODE) + if (pblk->fat_block_state != FATBLOCK_UNCOMMITTED) + debug_break("commit fat block", "Not un-committed"); +#endif + /* Remove from the uncommitted list */ + if (pblk->pnext) + pblk->pnext->pprev = pblk->pprev; + if (pblk->pprev) + pblk->pprev->pnext = pblk->pnext; + if (pfatbuffcntxt->puncommitted_blocks == pblk) + pfatbuffcntxt->puncommitted_blocks = pblk->pnext; + + /* Put on the committed list */ + pblk->fat_block_state = FATBLOCK_COMMITTED; + pblk->pprev = 0; + if (pfatbuffcntxt->pcommitted_blocks) + pfatbuffcntxt->pcommitted_blocks->pprev = pblk; + pblk->pnext = pfatbuffcntxt->pcommitted_blocks; + pfatbuffcntxt->pcommitted_blocks = pblk; +} + +void pc_commit_fat_table(FATBUFFCNTXT *pfatbuffcntxt) +{ +dword b; +int i; +FATBUFF *pblk; + for (i = 0; i < pfatbuffcntxt->hash_size; i++) + { + b = *(pfatbuffcntxt->mapped_blocks+i); + if ( b && ((b & 0xc0000000ul) == 0x80000000ul) ) + { /* It changed to committed */ + /* Move from uncommitted list to committed list */ + pblk = pc_find_fat_blk(pfatbuffcntxt, (b & 0x0ffffffful)); + if (pblk) /* This should not fail */ + pc_commit_fat_blk(pfatbuffcntxt, pblk); + b |= 0x40000000ul; /* set commited flag in primary */ + *(pfatbuffcntxt->mapped_blocks+i) = b; + } + } +} +void pc_sort_committed_blocks(FATBUFFCNTXT *pfatbuffcntxt) +{ +FATBUFF *pblk, *pprev, *psorted_list, *psort, *pblk_source_scan; + + /* The first element is the root of the sorted list, we begin from the + next element scanning foreward and inserting in sorted order */ + psorted_list = pfatbuffcntxt->pcommitted_blocks; /* pfatbuffcntxt->pcommitted_blocks is guaranteed not null */ + + pblk_source_scan = pfatbuffcntxt->pcommitted_blocks->pnext; + if (pblk_source_scan) + { + psorted_list->pnext = 0; + psorted_list->pprev = 0; + pblk_source_scan->pprev = 0; + } + while (pblk_source_scan) + { + pblk = pblk_source_scan; + pblk_source_scan = pblk_source_scan->pnext; + + pblk->pnext = 0; + + pprev = 0; + psort = psorted_list; + /* Find the first block in the sorted list with blockno > the new entry */ + while (psort && psort->fat_blockno < pblk->fat_blockno) + { + pprev = psort; + psort = psort->pnext; + } + if (pprev) + { /* Insert the new one after pprev */ + if (pprev->pnext) + pprev->pnext->pprev = pblk; + pblk->pprev = pprev; + pblk->pnext = pprev->pnext; + pprev->pnext = pblk; + } + else + { /* We are the smallest */ + if (psorted_list) + psorted_list->pprev = pblk; + pblk->pprev = 0; + pblk->pnext = psorted_list; + psorted_list = pblk; + } + } + pfatbuffcntxt->pcommitted_blocks = psorted_list; +} + +#if (DEBUG_BLOCK_CODE || DEBUG_FAT_CODE) +void debug_break(char *where, char *message) +{ + printf("%s: %s\n", where, message); +} +#endif + +#if (DEBUG_FAT_CODE) +void debug_check_fat(FATBUFFCNTXT *pfatbuffcntxt, char *where) +{ +FATBUFF *pblk; +FATBUFF *pblk_prev; +int nb = 0; +int numblocks; + + numblocks = pfatbuffcntxt->num_blocks; + pblk = pfatbuffcntxt->pfree_blocks; + while (pblk) + { + nb += 1; + if (nb > numblocks) + debug_break(where, "Bad Fat freelist"); + pblk = pblk->pnext; + } + pblk = pfatbuffcntxt->pcommitted_blocks; + if (pblk && pblk->pprev) + debug_break(where, "Bad fat committed root"); + while (pblk) + { + nb += 1; + if (nb > numblocks) + debug_break(where, "Bad fat committed list"); + if (pblk->fat_block_state != FATBLOCK_COMMITTED) + debug_break(where, "Uncommitted blokc on committed list"); + pblk = pblk->pnext; + } + if (pfatbuffcntxt->pcommitted_blocks) + { + pblk_prev = pblk = pfatbuffcntxt->pcommitted_blocks; + pblk = pblk->pnext; + while (pblk) + { + if (pblk->pprev != pblk_prev) + debug_break(where, "Bad link in committed list"); + pblk_prev = pblk; + pblk = pblk->pnext; + } + } + pblk = pfatbuffcntxt->puncommitted_blocks; + if (pblk && pblk->pprev) + debug_break(where, "Bad fat uncommitted root"); + while (pblk) + { + nb += 1; + if (nb > numblocks) + debug_break(where, "Bad fat uncommitted list"); + if (pblk->fat_block_state != FATBLOCK_UNCOMMITTED) + debug_break(where, "Uncommitted blokc on committed list"); + pblk = pblk->pnext; + } + if (pfatbuffcntxt->puncommitted_blocks) + { + pblk_prev = pblk = pfatbuffcntxt->puncommitted_blocks; + pblk = pblk->pnext; + while (pblk) + { + if (pblk->pprev != pblk_prev) + debug_break(where, "Bad link in uncommitted list"); + pblk_prev = pblk; + pblk = pblk->pnext; + } + } + + if (nb != numblocks) + debug_break(where, "Fat Leak"); + +} +#endif /* DEBUG_FAT_CODE */ + +#if (DEBUG_BLOCK_CODE) +void debug_check_blocks(BLKBUFFCNTXT *pbuffcntxt, int numblocks, char *where) +{ +BLKBUFF *pblk; +BLKBUFF *pblk_prev; +int nb = 0; +int i; + pblk = pbuffcntxt->pfree_blocks; + while (pblk) + { + nb += 1; + if (nb > numblocks) + debug_break(where, "Bad freelist"); + pblk = pblk->pnext; + } + pblk = pbuffcntxt->ppopulated_blocks; + if (pblk && pblk->pprev) + debug_break(where, "Bad populated root"); + while (pblk) + { + nb += 1; + if (nb > numblocks) + debug_break(where, "Bad populated list"); + pblk = pblk->pnext; + } + if (nb != numblocks) + debug_break(where, "Leak"); + + if (pbuffcntxt->ppopulated_blocks) + { + pblk_prev = pblk = pbuffcntxt->ppopulated_blocks; + pblk = pblk->pnext; + while (pblk) + { + if (pblk->pprev != pblk_prev) + debug_break(where, "Bad link in populated list"); + pblk_prev = pblk; + pblk = pblk->pnext; + } + } + for (i = 0; i < pbuffcntxt->hash_size; i++) + { + nb = 0; + pblk = *(pbuffcntxt->blk_hash_tbl+i); + while (pblk) + { + if (i != (int) (pblk->blockno&pbuffcntxt->hash_mask)) + debug_break(where, "Block in wrong hash slot"); + + nb += 1; + if (nb > numblocks) + debug_break(where, "Loop in hash table"); + pblk = pblk->pnext2; + } + } +} +#endif /* (DEBUG_BLOCK_CODE) */ + + + diff --git a/build/libraries/fatfs/ARM7/prfsapi.c b/build/libraries/fatfs/ARM7/prfsapi.c new file mode 100644 index 0000000..139b296 --- /dev/null +++ b/build/libraries/fatfs/ARM7/prfsapi.c @@ -0,0 +1,484 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* PRFSAPI.C - Contains user api level source code for ERTFS-Pro + failsafe functions. + + The following routines are included: + pro_failsafe_init - Initiate a failsafe session + pro_failsafe_commit - Commit failsafe buffers to disk + pro_failsafe_restore - Check or restore from the failsafe journal + pro_failsafe_auto_init - Automatically enable failsafe for a device at boot + pro_failsafe_shutdown - Close or abort FailSafe mode +*/ + +#include + +#if (INCLUDE_FAILSAFE_CODE) + +#define INCLUDE_FAILSAFE_AUTO_INIT 1 + +/* pro_failsafe_init - Initiate a failsafe session +* +* Summary: +* BOOLEAN pro_failsafe_init(byte *drive_name, +* FAILSAFECONTEXT *pfscntxt, +* int configuration_flags) +* +* Description: +* +* This routine checks the user supplied parameters, initializes the +* failsafe context structure and places the drive in failsafe mode. +* Once the device is in failsafe mode directory block and FAT block +* writes will be held in a journal file pro_failsafe_commit() is called. +* +* Inputs: +* drive_name - Null terminated drive designator, for example "A:" +* pfscntxt - The address of a block of data that will be used as a +* context block for failsafe. This block must remain valid for the whole +* session and may not be deallocated. +* journal_size - If this is non-zero a journal file is created containing +* this many blocks. If this value is zero the journal file will be created +* using default size calculations. The default size is calculated to be the +* size of the File Allocation table plus +* value in blocks +* configuration_flags - You can optionally enable these features by oring +* these values into the configuration_flags field. +* FS_MODE_AUTORESTORE- If removable media is reinserted and the +* journal file indicates that restore is needed, +* automatically restore and continue. +* If this flag is not set when this condition occurs +* the mount of the volume fails and the errno is set +* to PEFSRESTORENEEDED, the application must then +* handle this errno setting and call the pro_failsafe_restore +* FS_MODE_AUTORECOVER- If AUTORESTORE is set and the restore fails, ignore +* ignore the error and continue. Attempt to create +* the journal file. If AUTORECOVER is not set the +* disk mount fails, with errno set to PEFSRESTOREERROR. +* FS_MODE_AUTOCOMMIT- If AUTOCOMMIT is enabled the FailSafe commit operation +* will be performed automatically be ERTFS at the +* completion of each API call. With AUTOCOMMIT enabled +* the FailSafe operation is transparent to the user +* and it is not necessary to call pro_failsafe_commit(). +* +* +* Additional inputs - Several fields in the FailSafe context block may be +* initialized before pro_failsafe_init() is called. user_journal_size may +* be set under certain special circumstances. blockmap_freelist and +* blockmap_size should be initialized under most circumstance. +* +* user_journal_size - This field in the context block may be assigned with +* a value that prescribes the size of the FailSafe file in blocks. If this +* field is left at zero the FailSafe file will be sized according to the +* algorithm described in the compile time configuration section. +* +* Blockmaps - FailSafe will utilize an optional block map cache if you +* provide it with the necessary ram resources. Even though FailSafe will +* execute properly if no block mapping is provided its performance will be +* significantly diminished. It is therefore highly recommended that you +* enable block mapping. If the number of blocks written to the FailSafe +* file exceeds the number of block map elements FailSafe performance will +* be reduced drastically. Each caching structure requires approximately +* 32 bytes. In the examples provided with ERTFS we provide 128 mapping +* structures. +* +* blockmap_freelist - This field in the context block may be assigned +* a pointer to an array of structures of type FSBLOCKMAP. for example: +* context.blockmap_freelist = &blockmap_array[0]; +* +* blockmap_size - This field may be filled in with the number of elements +* contained in the array assigned to blockmap_freelist. +* +* Returns: +* TRUE - Success +* FALSE - Error +* +* If it returns FALSE errno will be set to one of the following: +* +* PEFSREINIT - Failsafe already initialized +* PEINVALIDPARMS - Invalid parameters either no context block was passed +* the journal size parameter is too small +* PEINVALIDDRIVEID - Invalid drive +* An ERTFS system error +* +*/ + +BOOLEAN pro_failsafe_init(byte *drivename, FAILSAFECONTEXT *pfscntxt) +{ +DDRIVE *pdrive; +int driveno; +BOOLEAN ret_val; + + if (!pc_parsedrive( &driveno, drivename)) + return (FALSE); + + OS_CLAIM_LOGDRIVE(driveno) /* pro_failsafe_init register drive in use */ + pdrive = pc_drno_to_drive_struct(driveno); + if (pdrive->pfscntxt) + { + rtfs_set_errno(PEFSREINIT); + OS_RELEASE_LOGDRIVE(driveno) + return(FALSE); + } + + /* If the drive is open now, flush all buffers and close the disk. + When the drive is reopened it will re-open in failsafe mode */ + if (pdrive->mount_valid) + { + /* Flush any FAT buffers */ + if (!FATOP(pdrive)->fatop_flushfat(driveno)) + { + OS_RELEASE_LOGDRIVE(driveno) + return(FALSE); + } + /* Flush any directory buffers */ + /*HEREHERE - Need an API */ + pc_dskfree(driveno); + } + ret_val = pro_failsafe_init_internal(pdrive, pfscntxt); + + OS_RELEASE_LOGDRIVE(driveno) + return(ret_val); +} + +/* pro_failsafe_commit - Commit failsafe buffers to disk +* +* Summary: +* int pro_failsafe_commit(FAILSAFECONTEXT *pfscntxt) +* +* Description: +* +* This routine updates the disk from the in-memory fat and block buffers. +* If FS_MODE_JOURNALING is enabled the journal file is completed before the +* buffer flush operation begins. +* +* When pro_failsafe_commit completes succesfully, the changes made to +* the volume structure since the previous succesful call to +* pro_failsafe_commit are guaranteed to be committed to disk. If +* the disk volume was correct when failsafe was started it will +* still be correct. +* +* If pro_failsafe_commit is unsuccessful, those changes made to +* the volume structure since the last call to pro_failsafe_commit +* may be partially committed to disk. This causes inconsistencies such as +* lost cluster chains and incorrect file lengtths. If JOURNALING is +* enabled, pro_failsafe_restore may be called to restore the disk volume +* back to it's state after the last successful call to pro_failsafe_init. +* +* +* Returns: +* TRUE - Success +* FALSE - Failure +* If FALSE is returned errno will be set to one of the following. +* +* PEINVALIDDRIVEID - Drive argument invalid +* PENOINIT - pro_failsafe_init must be called first +* PEJOURNALOPENFAIL - journal file or vram init call failed +* PEIOERRORREAD - Error reading FAT or buffer area +* PEIOERRORWRITEJOURNAL - Error writing journal file or NVRAM section +* PEIOERRORREADJOURNAL - Error writing journal file or NVRAM section +* PEIOERRORWRITEFAT - Error writing fat area +* PEIOERRORWRITEBLOCK - Error writing directory area +* PEINTERNAL - internal error, only occurs if ERTFS is not configured. +* PEIOERRORREAD - Error reading FAT or buffer area +* PEIOERRORWRITEJOURNAL - Error writing journal file or NVRAM section +* PEIOERRORWRITEFAT - Error writing fat area +* PEIOERRORWRITEBLOCK - Error writing directory area +* An ERTFS system error +*/ + +BOOLEAN pro_failsafe_commit(byte *path) /* __apifn__ */ +{ +FAILSAFECONTEXT *pfscntxt; +int driveno; +BOOLEAN ret_val; +DDRIVE *pdr; + + driveno = check_drive_name_mount(path); + if (driveno < 0) + return(FALSE); /* Check_drive set errno */ + pdr = pc_drno_to_drive_struct(driveno); + rtfs_set_errno(0); + pfscntxt = (FAILSAFECONTEXT *) pdr->pfscntxt; + if (!pfscntxt || !(pfscntxt->configuration_flags & FS_MODE_JOURNALING)) + { + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + rtfs_set_errno(PENOINIT); + return (FALSE); + } + /* Flush any directory buffers */ + if (pro_failsafe_commit_internal(pfscntxt) == 0) + ret_val = TRUE; + else + ret_val = FALSE; + release_drive_mount(driveno);/* Release lock, unmount if aborted */ + return (ret_val); +} + +/* pro_failsafe_restore - Check or restore from the failsafe journal +* +* Summary: +* int pro_failsafe_restore(byte *drive_name, +* FAILSAFECONTEXT *fscntxt, +* BOOLEAN dorestore, +* BOOLEAN doclear) +* +* Description: +* +* This routine tests the status of the failsafe journal file and returns the +* status of the drive. If dorestore is requested and restore is required +* the disk volume is restored from the journal file. +* If doclear Failsafe will clear any errors in the failsafe file +* +* Note: pro_failsafe_restore() requires a FAILSAFE CONTEXT structure to +* operate. If Failsafe is already initialized it will already have +* this context block, otherwise +* +* Returns: +* FS_STATUS_OK - No restore required +* FS_STATUS_NO_JOURNAL - No journal file present +* FS_STATUS_BAD_JOURNAL - Journal present but has invalid fields +* FS_STATUS_BAD_CHECKSUM - Journal data doesn't match stored checksum. +* FS_STATUS_IO_ERROR - IO error during restore +* FS_STATUS_RESTORED - Restore was required and completed +* FS_STATUS_MUST_RESTORE - Disk must be restored from the journal file +* because flushing of FAT and disk blocks was +* interrupted +* FS_STATUS_NO_INIT - Failsafe init must be called or a context +* structure must be provided +* +* Example: +* int s; +* s = pro_failsafe_restore("A:", 0, FALSE); +* if (s==FS_STATUS_OK) printf("No restore required\n"); +* else if (s==FS_STATUS_OK) printf("No restore needed\n"); +* else if (s==FS_STATUS_NO_JOURNAL) printf("No journal file present\n"); +* else if (s==FS_STATUS_BAD_JOURNAL) printf("Journal contains bad data\n"); +* else if (s==FS_STATUS_BAD_CHECKSUM) printf("Journal contains bad data\n"); +* else if (s==FS_STATUS_OUT_OF_DATE) printf("Journal out of sync\n"); +* else if (s==FS_STATUS_NO_INIT) printf("Failsafe not initialized\n"); +* else if (s==FS_STATUS_IO_ERROR) printf("IO error accessing journal\n"); +* else if (s==FS_STATUS_MUST_RESTORE) +* { +* printf("Restoring disk from the journal file\n"); +* s = pro_failsafe_restore("A:", 0, TRUE); +* if (s==FS_STATUS_RESTORED) +* printf("Restore was completed sucesfully\n"); +* else if (s==FS_STATUS_BAD_CHECKSUM) +* printf("Journal file corrupted\n"); +* else if (s==FS_STATUS_IO_ERROR) +* printf("IO error during restore\n"); +* } +* } +* +*/ + +int pro_failsafe_restore(byte *drive_name, FAILSAFECONTEXT *fscntxt, BOOLEAN dorestore, BOOLEAN doclear) +{ +int ret_val,driveno; +FAILSAFECONTEXT *savedcontext; +DDRIVE *pdr; + + savedcontext = 0; + /* Get the drive and make sure it has a context block */ + if (!pc_parsedrive( &driveno, drive_name)) + return(FS_STATUS_NO_INIT); + + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr) + return(FS_STATUS_NO_INIT); + + if (!pdr->pfscntxt && !fscntxt) + return(FS_STATUS_NO_INIT); + + OS_CLAIM_LOGDRIVE(driveno) /* check_drive_name_mount Register drive in use */ + pc_dskfree(driveno); /* make sure buffers are free */ + savedcontext = (FAILSAFECONTEXT *)pdr->pfscntxt; /* Kill failsafe */ + pdr->pfscntxt = 0; + OS_RELEASE_LOGDRIVE(driveno) /* check_drive_name_mount release */ + + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(drive_name); + if (driveno < 0) + { + ret_val = -1; /* check_drive set errno */ + } + else + { + if (fscntxt) + { + rtfs_memset((void *) fscntxt, 0, sizeof(*fscntxt)); + fscntxt->pdrive = pdr; + pdr->pfscntxt = (void *) fscntxt; + } + else + pdr->pfscntxt = (void *) savedcontext; + ret_val = failsafe_restore_internal((FAILSAFECONTEXT *) pdr->pfscntxt, dorestore, doclear); + if (dorestore || doclear) + pc_dskfree(driveno); + } + pdr->pfscntxt = (void *) savedcontext; + OS_RELEASE_LOGDRIVE(driveno) /* pro+failsafe_restore, release claim by check_drive_name_mount() */ + return(ret_val); +} + +/* pro_failsafe_auto_init - Automatically enable failsafe for a device at boot +* time +* +* The user must modify this code if he wishes to auto configure failsafe +* features for a given device. +* +* +* Summary: +* BOOLEAN pro_failsafe_auto_init(DDRIVE *pdrive) +* +* Description: +* +* If in pc_ertfs_init() the DRIVE_FLAGS_FAILSAFE bit is set in +* pdr->drive_flags then this routine is called +* to automatically enable Failsafe mode for that drive. +* If failsafe is initialized this way there is no need to call +* pro_failsafe_init() from the API layer. +* +* Note: pro_failsafe_auto_init() is implemented inside the file +* prapifs.c. +* To use this feature, INCLUDE_FAILSAFE_AUTO_INIT must be set to +* 1 in the source file, the routine should be modified to fit +* your needs and then be recompiled. By default the +* INCLUDE_FAILSAFE_AUTO_INIT is set to zero. +* +* +* +* Inputs: +* pdrive - A pointer to the drive structure for the device. +* +* Returns: +* TRUE - (default) If the user returns TRUE, ERTFS will allow mounts +* to occur on the drive. +* FALSE - If the user returns FALSE, ERTFS will not allow mounts +* to occur on the drive. +* +*/ + +#if (INCLUDE_FAILSAFE_AUTO_INIT) +FAILSAFECONTEXT auto_fscontext; +#define AUTO_BLOCKMAPSIZE 128 +FSBLOCKMAP auto_failsafe_blockmap_array[AUTO_BLOCKMAPSIZE]; +#endif + +BOOLEAN pro_failsafe_auto_init(DDRIVE *pdrive) +{ +#if (!INCLUDE_FAILSAFE_AUTO_INIT) + return(FALSE); +#else + /* In this example we set up failsafe mode for one device + This code may be modified to enable failsafe on multiple + devices and to modify FailSafe settings according to the + documentation provided for pro_failsafe_init(). + */ + if (auto_fscontext.pdrive) /* Only one device supported int this */ + return (FALSE); /* Example. Add more contexts */ + + rtfs_memset((void *) &auto_fscontext, 0, sizeof(auto_fscontext)); + /* Run fully automated */ + auto_fscontext.configuration_flags = +// (FS_MODE_AUTORESTORE|FS_MODE_AUTORECOVER/*|FS_MODE_AUTOCOMMIT*/); + (FS_MODE_AUTORESTORE|FS_MODE_AUTORECOVER|FS_MODE_AUTOCOMMIT); // ctr modified + auto_fscontext.blockmap_size = AUTO_BLOCKMAPSIZE; + auto_fscontext.blockmap_freelist = &auto_failsafe_blockmap_array[0]; + /* Our parameters are correct so we know that the routine will not + fail but to demonstrate the principle we return FALSE if the + call does fail. */ + if (!pro_failsafe_init_internal(pdrive, &auto_fscontext)) + return(FALSE); + return(TRUE); +#endif /* (INCLUDE_FAILSAFE_AUTO_INIT) */ +} + +/* pro_failsafe_shutdown - Close or abort FailSafe mode +* +* Summary: +* BOOLEAN pro_failsafe_shutdown(byte *drive_name, BOOLEAN abort) +* +* Description: +* +* This routine allows the applications layer to disable Failsafe. +* FailSafe. The opcode field determines what operation to perform. +* +* +* If the parameter abort is TRUE then FailSafe is unilaterally disabled +* for the specified drive. +* +* If the parameter abort is FALSE then a FailSafe commit is performed and +* if it is succesful then FailSafe is disabled. If the commit is unsucessful +* FailSafe is not disabled and errno will contain the reason for the failure. + +* +* Returns: +* TRUE - If Success +* FALSE - Failure +* +* If FALSE is returned errno will be set to one of the following. +* +* PEINVALIDDRIVEID - Drive argument invalid +* PEJOURNALOPENFAIL - journal file or vram init call failed +* PEINTERNAL - internal error, only occurs if ERTFS is not configured. +* PEIOERRORREAD - Error reading FAT or buffer area +* PEIOERRORWRITEJOURNAL - Error writing journal file or NVRAM section +* PEIOERRORREADJOURNAL - Error writing journal file or NVRAM section +* PEIOERRORWRITEFAT - Error writing fat area +* PEIOERRORWRITEBLOCK - Error writing directory area +* An ERTFS system error +* +*/ + +BOOLEAN pro_failsafe_shutdown(byte *drive_name, BOOLEAN abort) +{ +BOOLEAN ret_val; +int driveno; +DDRIVE *pdr; +FAILSAFECONTEXT *pfscntxt; + + if (abort) /* unilaterally suspend */ + { + if (!pc_parsedrive( &driveno, drive_name)) + return (FALSE); + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr) + return (FALSE); + OS_CLAIM_LOGDRIVE(driveno) /* check_drive_name_mount Register drive in use */ + pdr->pfscntxt = 0; /* Shutdown just unlinks it */ + if (pdr->mount_valid) /* already mounted */ + pc_dskfree(pdr->driveno); + OS_RELEASE_LOGDRIVE(driveno) + return(TRUE); + } + else + { + /* Get the drive and make sure it is mounted */ + driveno = check_drive_name_mount(drive_name); + if (driveno < 0) + return(FALSE); /* check_drive set errno */ + rtfs_set_errno(0); + pdr = pc_drno_to_drive_struct(driveno); + ret_val = TRUE; + pfscntxt = (FAILSAFECONTEXT *) pdr->pfscntxt; + if (pfscntxt) + { + if (pro_failsafe_commit_internal(pfscntxt) != 0) + ret_val = FALSE; + else + pdr->pfscntxt = 0; /* Shutdown just unlinks it */ + } + release_drive_mount(driveno); + return(ret_val); + } +} + +#endif /* (INCLUDE_FAILSAFE_CODE) */ + diff --git a/build/libraries/fatfs/ARM7/prfscore.c b/build/libraries/fatfs/ARM7/prfscore.c new file mode 100644 index 0000000..81533bb --- /dev/null +++ b/build/libraries/fatfs/ARM7/prfscore.c @@ -0,0 +1,1263 @@ +/* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* PRFSCORE.C - ERTFS-PRO FailSafe internal routines */ + +#include +#if (INCLUDE_FAILSAFE_CODE) + +BOOLEAN fs_copy_fat_block(FAILSAFECONTEXT *pfscntxt, dword replacement_block, dword blockno, dword fat_offset); +BOOLEAN fs_copy_dir_block(FAILSAFECONTEXT *pfscntxt, dword replacement_block, dword blockno); +BOOLEAN fs_copy_journal_block_pages(FAILSAFECONTEXT *pfscntxt); +BOOLEAN fs_copy_journal_fat_pages(FAILSAFECONTEXT *pfscntxt); +BOOLEAN fs_copy_journal_fsinfo_page(FAILSAFECONTEXT *pfscntxt); +BOOLEAN pro_failsafe_checksum_index(FAILSAFECONTEXT *pfscntxt, int *error); +BOOLEAN pro_failsafe_restore_buffers(FAILSAFECONTEXT *pfscntxt, int *error); +dword fs_check_mapped(FAILSAFECONTEXT *pfscntxt, dword blockno, int *error); +dword fs_map_block(FAILSAFECONTEXT *pfscntxt, dword blockno); +dword fs_block_map_scan(FAILSAFECONTEXT *pfscntxt, dword blockno, int *error); +dword fs_block_map_find_replacement(FAILSAFECONTEXT *pfscntxt, dword replacement_block, int *error); +dword fs_block_map_replace(FAILSAFECONTEXT *pfscntxt, dword blockno); +dword * fs_map_index_page(FAILSAFECONTEXT *pfscntxt, int index_page, BOOLEAN writing); +BOOLEAN fs_flush_index_pages(FAILSAFECONTEXT *pfscntxt); +int pro_failsafe_flush_header(FAILSAFECONTEXT *pfscntxt, dword status); + + +/* pro_failsafe_init_internal - Initiate a failsafe session +* +* Summary: +* BOOLEAN pro_failsafe_init_internal(DDRIVE *pdrive, +* FAILSAFECONTEXT *pfscntxt) +* +* Description: +* +* This routine checks the user supplied parameters, initializes the +* failsafe context structure and places the drive in failsafe mode. +* Once the device is in failsafe mode directory block and FAT block +* writes will be held in a journal file pro_failsafe_commit() is called. +* +* This routine is called by both pro_failsafe_init() and +* pro_failsafe_auto_init(). Please see the manual page for +* pro_failsafe_init() for a discussion of arguments and +* configuration values. +* +* Inputs: +* pdrive - drive structure +* pfscntxt - The address of a block of data that will be used as a +* context block for failsafe. This block must remain valid for the whole +* session and may not be deallocated. +* +* +* Returns: +* TRUE - Success +* FALSE - Error +* +* If it returns FALSE errno will be set to one of the following: +* +* PEFSREINIT - Failsafe already initialized +* PEINVALIDPARMS - Invalid parameters either no context block was passed +* the journal size parameter is too small +* PEINVALIDDRIVEID - Invalid drive +* PEFSCREATE - Error creating the journal file +* An ERTFS system error +* +*/ + +BOOLEAN pro_failsafe_init_internal(DDRIVE *pdrive, FAILSAFECONTEXT *pfscntxt) +{ +dword ltemp; +int i; +FSBLOCKMAP *pbm; + + /* Make sure only valid bits are used */ + ltemp = FS_MODE_AUTOCOMMIT|FS_MODE_AUTORESTORE|FS_MODE_AUTORECOVER; + ltemp = ~ltemp; + if (pfscntxt->configuration_flags & ltemp) + { +bad_parms: + rtfs_set_errno(PEINVALIDPARMS); + return (FALSE); + } + if (pfscntxt->user_journal_size) + { + /* Index in dwords */ + ltemp = pfscntxt->user_journal_size + JOURNAL_HEADER_SIZE; + /* Index in blocks */ + pfscntxt->num_index_blocks = + (ltemp+(JOURNAL_ENTRIES_P_BLOCK-1))/JOURNAL_ENTRIES_P_BLOCK; + if (pfscntxt->num_index_blocks >= pfscntxt->user_journal_size) + goto bad_parms; + pfscntxt->num_remap_blocks = pfscntxt->user_journal_size - pfscntxt->num_index_blocks; + } + + if (pfscntxt->blockmap_size) + { + pbm = pfscntxt->blockmap_freelist; + if (!pbm) + goto bad_parms; + for( i = 1; i < pfscntxt->blockmap_size; i++) + { + pbm->pnext = (pbm+1); + pbm += 1; + } + pbm->pnext = 0; + } + + /* Point the drive at the failsafe context block to put the drive in + failsafe mode */ + pfscntxt->pdrive = pdrive; + pdrive->pfscntxt = (void *) pfscntxt; + return(TRUE); +} + +/* pro_failsafe_dskopen - Open failsafe operations on a disk volume +* +* +* Summary: +* BOOLEAN pro_failsafe_dskopen(DDRIVE *pdrive) +* +* Description: +* +* This routine is called by check_media when it automounts a volume. +* This routine creates or resets the journal file. +* +* +* Returns: +* TRUE - Success +* FALSE - Error +* +* If FALSE is returned errno is set to one of these values +* +* PEFSCREATE - Error creating the journal file +* PEIOERRORWRITEJOURNAL - Error initializing the journal file +* +*/ + +BOOLEAN pro_failsafe_dskopen(DDRIVE *pdrive) +{ +FAILSAFECONTEXT *pfscntxt; +dword ltemp,*pdw; + + pfscntxt = (FAILSAFECONTEXT *) pdrive->pfscntxt; + if (!pfscntxt) + return(TRUE); + /* Make sure journalling is off for the moment */ + pfscntxt->configuration_flags &= ~FS_MODE_JOURNALING; + /* Calculate num_index_blocks and num_remap_blocks */ + /* if pfscntxt->user_journal_size is non zero num_remap_blocks + and num_index_blocks were already calculated */ + if (!pfscntxt->user_journal_size) + { + pfscntxt->num_remap_blocks = pdrive->secpfat + CFG_NUM_JOURNAL_BLOCKS; + /* Index in dwords */ + ltemp = pfscntxt->num_remap_blocks + JOURNAL_HEADER_SIZE; + /* Index in blocks */ + pfscntxt->num_index_blocks = + (ltemp+(JOURNAL_ENTRIES_P_BLOCK-1))/JOURNAL_ENTRIES_P_BLOCK; + } + + /* Create the failsafe file */ + if (!failsafe_create_nv_buffer(pfscntxt)) + { + rtfs_set_errno(PEFSCREATE); + return(FALSE); + } + + pdw = fs_map_index_page(pfscntxt, 0, TRUE); + if (!pdw) + { + pfscntxt->journal_file_size = 0; + return(FALSE); + } + /* Format the first block. Thats enough to clear the file */ + if (pro_failsafe_flush_header(pfscntxt, FS_STATUS_COMPLETE)) + { + pfscntxt->journal_file_size = 0; + return(FALSE); + } + /* Restore journal flag */ + pfscntxt->configuration_flags |= FS_MODE_JOURNALING; + pfscntxt->known_free_clusters = pfscntxt->pdrive->known_free_clusters; + return(TRUE); +} + +/* pro_failsafe_journal_full - Check if a disk journal is full. +* +* Summary: +* BOOLEAN pro_failsafe_journal_full(DDRIVE *pdrive) +* +* Description: +* +* This routine is called by fat_map_block before it places a in the +* dirty state. IF the journal file is full this routine returns TRUE +* Forcing the fat map to fail. This catches Journal full errors +* earlier in the process +*/ + +BOOLEAN pro_failsafe_journal_full(DDRIVE *pdrive) +{ + FAILSAFECONTEXT *pfscntxt; + pfscntxt = (FAILSAFECONTEXT *) pdrive->pfscntxt; + if (pfscntxt && (pfscntxt->total_blocks_mapped >= pfscntxt->num_remap_blocks)) + { + rtfs_set_errno(PEJOURNALFULL); + return(TRUE); + } + else + return(FALSE); +} + + + +/* pro_failsafe_autorestore - Restore a disk volume at mount time +* +* Summary: +* BOOLEAN pro_failsafe_autorestore(DDRIVE *pdrive) +* +* Description: +* +* This routine is called by check_media when it automounts a volume. +* This routine restores the disk from the journal file if necessary (see +* below). +* +* If failsafe is configured for autorestore +* (pfscntxt->configuration_flags & FS_MODE_AUTORESTORE is true), +* and the journal file indicates that restore is needed, it attempts to +* restore the volume. +* If the restore fails and failsafe is not configured for autorecover, it +* returns an error that forces the disk mount to fail. +* If the restore fails and failsafe is configured for autorecover +* (pfscntxt->configuration_flags & FS_MODE_AUTORECOVER is true). Then the +* restore error is ignored. +* +* If failsafe is not configured for autorestore +* (pfscntxt->configuration_flags & FS_MODE_AUTORESTORE is not true), +* and the journal file indicates that restore is needed, it sets errno +* to PEFSRESTORENEEDED and returns FALSE, forcing the mount to fail. +** +* Returns: +* TRUE - Success +* FALSE - Error +* +* If FALSE is returned errno is set to one of these values +* +* PEFSRESTOREERROR - Restore failed but not in autorecover mode +* PEFSRESTORENEEDED- Restore needed but not in autorestore mode +* +*/ + +BOOLEAN pro_failsafe_autorestore(DDRIVE *pdrive) +{ +FAILSAFECONTEXT *pfscntxt; +int restore_state; + + pfscntxt = (FAILSAFECONTEXT *) pdrive->pfscntxt; + if (!pfscntxt) + return(TRUE); + + /* This is excuted every time there is a mount so make sure + the failsafe buffer is cleared */ + fs_rewind_failsafe(pfscntxt); + /* Check the status but don't restore */ + restore_state = failsafe_restore_internal(pfscntxt, FALSE,FALSE); + switch (restore_state) + { + case FS_STATUS_OK: + case FS_STATUS_NO_JOURNAL: + return(TRUE); + case FS_STATUS_MUST_RESTORE: + if (pfscntxt->configuration_flags & FS_MODE_AUTORESTORE) + { + /* Restore the journal file if autorestore enabled */ + /* Clear the directory cache */ + pc_free_all_blk(pdrive); + /* Clear the fat cache */ + pc_free_all_fat_blocks(&pdrive->fatcontext); + restore_state = failsafe_restore_internal(pfscntxt, TRUE, FALSE); + if (restore_state != FS_STATUS_RESTORED) + { + if (!(pfscntxt->configuration_flags & FS_MODE_AUTORECOVER)) + { + pfscntxt->configuration_flags &= ~FS_MODE_JOURNALING; + rtfs_set_errno(PEFSRESTOREERROR); + return(FALSE); + } + /* Autorecover is enabled, so fall through and ignore + the error */ + } + /* We changed the FATS from the journal so close and reopen */ + pc_dskfree(pdrive->driveno); + if (!pc_i_dskopen(pdrive->driveno)) + return(FALSE); + } + else + { + rtfs_set_errno(PEFSRESTORENEEDED); + return(FALSE); + } + break; + case FS_STATUS_IO_ERROR: + /* IO error during read */ + /* Make sure journalling is off for the moment */ + pfscntxt->configuration_flags &= ~FS_MODE_JOURNALING; + rtfs_set_errno(PEIOERRORREADJOURNAL); + return(FALSE); + case FS_STATUS_BAD_JOURNAL: + case FS_STATUS_BAD_CHECKSUM: + case FS_STATUS_OUT_OF_DATE: + default: + if ( (pfscntxt->configuration_flags & FS_MODE_AUTORESTORE) && + (pfscntxt->configuration_flags & FS_MODE_AUTORECOVER)) { + return(TRUE); + }else{ + /* Make sure journalling is off for the moment */ + pfscntxt->configuration_flags &= ~FS_MODE_JOURNALING; + rtfs_set_errno(PEFSRESTOREERROR); + return(FALSE); + } + //break; + } /* End switch */ + return(TRUE); +} + +int pro_failsafe_flush_header(FAILSAFECONTEXT *pfscntxt, dword status) +{ +dword *pdw; + /* update index information to disk */ + pdw = fs_map_index_page(pfscntxt, 0, TRUE); + if (!pdw) + return(-1); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_SIGNATURE_1) ,FS_JOURNAL_SIGNATURE_1); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_SIGNATURE_2) ,FS_JOURNAL_SIGNATURE_2); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_VERSION ) ,FS_JOURNAL_VERSION); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_TOTAL_BLOCKS_MAPPED),pfscntxt->total_blocks_mapped); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_INDEX_CHECKSUM),pfscntxt->journal_checksum); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_INDEX_STATUS),status); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_FREE_CLUSTERS),pfscntxt->known_free_clusters); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_NUMB_INDEX_BLOCKS),pfscntxt->num_index_blocks ); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_NUMB_REMAP_BLOCKS),pfscntxt->num_remap_blocks); + if (!fs_flush_index_pages(pfscntxt)) + return(-1); + return(0); +} + + +int pro_failsafe_commit_internal(FAILSAFECONTEXT *pfscntxt) +{ +int ret_val; +dword saved_free_clusters; + + if (!pc_flush_all_fil(pfscntxt->pdrive)) + return(-1); + /* Flush any FAT buffers */ + if (!FATOP(pfscntxt->pdrive)->fatop_flushfat(pfscntxt->pdrive->driveno)) + return(-1); + + if (!pfscntxt->total_blocks_mapped) + return(0); + /* update index information to disk */ + if (pro_failsafe_flush_header(pfscntxt, FS_STATUS_PROCESSING)) + { + ret_val = -1; + goto ex_it; + } + if (!fs_copy_journal_block_pages(pfscntxt)) + { + ret_val = -1; + goto ex_it; + } + /* We are going to flush the fats now so invalidate + the check for known_free_clusters changing. since + it will give false positives */ + saved_free_clusters = pfscntxt->known_free_clusters; + pfscntxt->known_free_clusters = 0xffffffff; + if (pro_failsafe_flush_header(pfscntxt, FS_STATUS_PROCESSING)) + { + pfscntxt->known_free_clusters = saved_free_clusters; + ret_val = -1; + goto ex_it; + } + if (!fs_copy_journal_fat_pages(pfscntxt)) + { + pfscntxt->known_free_clusters = saved_free_clusters; + ret_val = -1; + goto ex_it; + } + if (pro_failsafe_flush_header(pfscntxt, FS_STATUS_PROCESSING)) + { + pfscntxt->known_free_clusters = saved_free_clusters; + ret_val = -1; + goto ex_it; + } + if (!fs_copy_journal_fsinfo_page(pfscntxt)) + { + pfscntxt->known_free_clusters = saved_free_clusters; + ret_val = -1; + goto ex_it; + } + if (pro_failsafe_flush_header(pfscntxt, FS_STATUS_COMPLETE)) + { + pfscntxt->known_free_clusters = saved_free_clusters; + ret_val = -1; + goto ex_it; + } + fs_rewind_failsafe(pfscntxt); /* Rewind the failsafe buffer */ + pfscntxt->known_free_clusters = pfscntxt->pdrive->known_free_clusters; + ret_val = 0; +ex_it: + return (ret_val); +} + +BOOLEAN pro_failsafe_autocommit(DDRIVE *pdrive) +{ +FAILSAFECONTEXT *pfscntxt; + pfscntxt = (FAILSAFECONTEXT *) pdrive->pfscntxt; + if (pfscntxt && (pfscntxt->configuration_flags & FS_MODE_AUTOCOMMIT) ) + { + if (pro_failsafe_commit_internal(pfscntxt) != 0) + return(FALSE); + } + return(TRUE); +} + +int failsafe_restore_internal(FAILSAFECONTEXT *pfscntxt, BOOLEAN dorestore, BOOLEAN doclear) +{ +dword *pdw; +int error; + + if (!pfscntxt) + { + return(FS_STATUS_NO_INIT); + } + + /* If we fail, default to FS_STATUS_NO_JOURNAL. Under extraordinary + conditions this will be changed to FS_STATUS_IO_ERROR */ + pfscntxt->open_status = FS_STATUS_NO_JOURNAL; + if (!failsafe_reopen_nv_buffer(pfscntxt)) + { + /* return either FS_STATUS_IO_ERROR or FS_STATUS_NO_JOURNAL */ + return(pfscntxt->open_status); + } + if (doclear) + { + if (!pro_failsafe_flush_header(pfscntxt, FS_STATUS_COMPLETE)) + return(FS_STATUS_OK); + else + return(FS_STATUS_IO_ERROR); + } + pdw = fs_map_index_page(pfscntxt, 0, FALSE); + if (!pdw) + return(FS_STATUS_IO_ERROR); + if ( + (to_DWORD((byte *)(pdw+INDEX_OFFSET_SIGNATURE_1)) != FS_JOURNAL_SIGNATURE_1) || + (to_DWORD((byte *)(pdw+INDEX_OFFSET_SIGNATURE_2)) != FS_JOURNAL_SIGNATURE_2) || + (to_DWORD((byte *)(pdw+INDEX_OFFSET_VERSION)) != FS_JOURNAL_VERSION) + ) + { +bad_journal: + return(FS_STATUS_BAD_JOURNAL); /* Unknown status */ + } + + /* It's a valid file. Did it complete ? */ + if (to_DWORD((byte *)(pdw+INDEX_OFFSET_INDEX_STATUS)) == FS_STATUS_COMPLETE) + { + return(FS_STATUS_OK); + } + else if (to_DWORD((byte *)(pdw+INDEX_OFFSET_INDEX_STATUS)) != FS_STATUS_PROCESSING) + goto bad_journal; + + /* It's a valid file but the disk flush did not complete */ + if (to_DWORD((byte *)(pdw+INDEX_OFFSET_FREE_CLUSTERS)) != (dword) pfscntxt->pdrive->known_free_clusters) + { + /* If free clusters value is all 1's, let it pass. + we set free clusters field to all 1's before we write the + FATS to the volume. To indicate that we have changed + FATs and the free clusters test will give false + positives */ + if (to_DWORD((byte *)(pdw+INDEX_OFFSET_FREE_CLUSTERS)) != 0xffffffff) + return(FS_STATUS_OUT_OF_DATE); + } + + /* It is a valid journal file, flush was begun but not completed so + we must restore the fat and block buffer region */ + if (!pro_failsafe_checksum_index(pfscntxt, &error)) + return(error); + + if (!dorestore) + return(FS_STATUS_MUST_RESTORE); + else + { + if (!pro_failsafe_restore_buffers(pfscntxt,&error)) + return (error); + /* We succeeded so write FS_STATUS_COMPLETE to the index */ + pdw = fs_map_index_page(pfscntxt, 0, TRUE); + if (!pdw) + return(FS_STATUS_IO_ERROR); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_INDEX_STATUS),FS_STATUS_COMPLETE); + if (!fs_flush_index_pages(pfscntxt)) + return (FS_STATUS_IO_ERROR); + return (FS_STATUS_RESTORED); + } +} + +BOOLEAN fs_copy_fat_block(FAILSAFECONTEXT *pfscntxt, dword replacement_block, dword blockno, dword fat_offset) +{ +FATBUFF *pfatblk; +byte *pdata; + + pfatblk = pc_find_fat_blk(&(pfscntxt->pdrive->fatcontext), blockno); + if (pfatblk) + pdata = pfatblk->fat_data; + else + { + pdata = pfscntxt->scratch_block; + if (!failsafe_read_nv_buffer(pfscntxt, replacement_block, pdata)) + return(FALSE); + } + if (!(devio_write(pfscntxt->pdrive->driveno, + blockno + fat_offset, pdata, (int) 1, FALSE) )) + return(FALSE); + return(TRUE); +} + +BOOLEAN fs_copy_dir_block(FAILSAFECONTEXT *pfscntxt, dword replacement_block, dword blockno) +{ +BLKBUFF *pblk; +byte *pdata; +BOOLEAN ret_val; + + OS_CLAIM_FSCRITICAL() + pblk = pc_find_blk(pfscntxt->pdrive, blockno); + if (pblk) + pblk->use_count += 1; + OS_RELEASE_FSCRITICAL() + + if (!pblk) + { + pdata = pfscntxt->scratch_block; + if (!failsafe_read_nv_buffer(pfscntxt, replacement_block, pdata)) + return(FALSE); + } + else + pdata = pblk->data; + + ret_val = devio_write(pfscntxt->pdrive->driveno, + blockno, pdata, (int) 1, FALSE); + if (pblk) + { + OS_CLAIM_FSCRITICAL() + pblk->use_count -= 1; + OS_RELEASE_FSCRITICAL() + } + return(ret_val); +} + + + +BOOLEAN fs_copy_journal_block_pages(FAILSAFECONTEXT *pfscntxt) +{ +struct fsblockmap *pbm; +dword fat_start,fat_end, replacement_block, blockno, info_block; +int error; + + fat_start = pfscntxt->pdrive->fatblock; + fat_end = pfscntxt->pdrive->fatblock+pfscntxt->pdrive->secpfat; + info_block = pfscntxt->pdrive->infosec; + + if (pfscntxt->blockmap_freelist) + { /* If the freelist is non empty that means that all remap blocks are + cached and in sorted order */ + pbm = pfscntxt->sorted_blockmap; + while (pbm) + { + replacement_block = pbm->replacement_block; + blockno = pbm->blockno; + if (blockno != info_block && (blockno < fat_start || blockno >= fat_end)) + { + if (!fs_copy_dir_block(pfscntxt, replacement_block, blockno)) + return(FALSE); + } + pbm = pbm->pnext_by_block; + } + } + else + { + dword ltemp; + for (replacement_block = pfscntxt->num_index_blocks, ltemp = 0; + ltemp < pfscntxt->total_blocks_mapped; + ltemp++, replacement_block++) + { + error = 0; + blockno = fs_block_map_find_replacement(pfscntxt, replacement_block, &error); + if (error) + return(FALSE); + if (blockno != info_block && (blockno < fat_start || blockno >= fat_end)) + { + if (!fs_copy_dir_block(pfscntxt, replacement_block, blockno)) + return(FALSE); + } + } + } + return(TRUE); +} + +BOOLEAN fs_copy_journal_fat_pages(FAILSAFECONTEXT *pfscntxt) +{ +struct fsblockmap *pbm; +dword fat_start, fat_offset,fat_end, replacement_block, blockno; +int pass_number,error; + + fat_start = pfscntxt->pdrive->fatblock; + fat_end = pfscntxt->pdrive->fatblock+pfscntxt->pdrive->secpfat; + fat_offset = 0; + + if (pfscntxt->blockmap_freelist) + { /* If the freelist is non empty that means that all remap blocks are + cached and in sorted order */ + for (pass_number = 0; pass_number < pfscntxt->pdrive->numfats; pass_number++) + { + pbm = pfscntxt->sorted_blockmap; + while (pbm) + { + replacement_block = pbm->replacement_block; + blockno = pbm->blockno; + if (fat_start <= blockno && fat_end > blockno) + { /* write fats on all passes */ + if (!fs_copy_fat_block(pfscntxt, replacement_block, blockno, fat_offset)) + return(FALSE); + } + pbm = pbm->pnext_by_block; + } + fat_offset += pfscntxt->pdrive->secpfat; + } + } + else + { + for (pass_number = 0; pass_number < pfscntxt->pdrive->numfats; pass_number++) + { + dword ltemp; + for (replacement_block = pfscntxt->num_index_blocks, ltemp = 0; + ltemp < pfscntxt->total_blocks_mapped; + ltemp++, replacement_block++) + { + error = 0; + blockno = fs_block_map_find_replacement(pfscntxt, replacement_block, &error); + if (error) + return(FALSE); + if (fat_start <= blockno && fat_end > blockno) + { /* write fats on all passes */ + if (!fs_copy_fat_block(pfscntxt, replacement_block, blockno, fat_offset)) + return(FALSE); + } + } + fat_offset += pfscntxt->pdrive->secpfat; + } + } + return(TRUE); +} + +BOOLEAN fs_copy_journal_fsinfo_page(FAILSAFECONTEXT *pfscntxt) +{ +struct fsblockmap *pbm; +dword info_block, replacement_block, blockno; +int error; + + info_block = pfscntxt->pdrive->infosec; + + /* Only FAT32 has an info block */ + if (!info_block) + return(TRUE); + + if (pfscntxt->blockmap_freelist) + { /* If the freelist is non empty that means that all remap blocks are + cached and in sorted order */ + pbm = pfscntxt->sorted_blockmap; + while (pbm) + { + replacement_block = pbm->replacement_block; + blockno = pbm->blockno; + if (blockno == info_block) + { + if (!fs_copy_dir_block(pfscntxt, replacement_block, blockno)) + return(FALSE); + break; + } + pbm = pbm->pnext_by_block; + } + } + else + { + dword ltemp; + for (replacement_block = pfscntxt->num_index_blocks, ltemp = 0; + ltemp < pfscntxt->total_blocks_mapped; + ltemp++, replacement_block++) + { + error = 0; + blockno = fs_block_map_find_replacement(pfscntxt, replacement_block, &error); + if (error) + return(FALSE); + if (blockno == info_block) + { + if (!fs_copy_dir_block(pfscntxt, replacement_block, blockno)) + return(FALSE); + break; + } + } + } + return(TRUE); +} + + +BOOLEAN pro_failsafe_checksum_index(FAILSAFECONTEXT *pfscntxt, int *error) +{ +dword *pdw; +dword stored_check_sum,running_check_sum; +dword entries_this_record,entries_completed; +dword total_blocks_mapped;//,num_index_blocks; +int i,index_offset,index_page; + + *error = 0; + pdw = fs_map_index_page(pfscntxt, 0, FALSE); + if (!pdw) + { /* fs_map_index_page set errno */ +io_error: + *error = FS_STATUS_IO_ERROR; + return(FALSE); + } + total_blocks_mapped = to_DWORD((byte *)(pdw+INDEX_OFFSET_TOTAL_BLOCKS_MAPPED)); + //num_index_blocks = to_DWORD((byte *)(pdw+INDEX_OFFSET_NUMB_INDEX_BLOCKS)); + stored_check_sum = to_DWORD((byte *)(pdw+INDEX_OFFSET_INDEX_CHECKSUM)); + running_check_sum = 0; + + entries_completed = 0; + entries_this_record = JOURNAL_ENTRIES_ZERO; + index_offset = JOURNAL_HEADER_SIZE; + index_page = 0; + while (entries_completed < total_blocks_mapped) + { + if ( (entries_completed + entries_this_record) > total_blocks_mapped) + entries_this_record = total_blocks_mapped-entries_completed; + pdw = fs_map_index_page(pfscntxt, index_page, FALSE); + if (!pdw) + goto io_error; + pdw += index_offset; + for (i = 0; i < (int)entries_this_record; i++,pdw++) + running_check_sum += to_DWORD((byte *)pdw); + entries_completed += entries_this_record; + index_offset = 0; + entries_this_record = JOURNAL_ENTRIES_P_BLOCK; + index_page += 1; + } + if (stored_check_sum != running_check_sum) + { + *error = FS_STATUS_BAD_CHECKSUM; + return(FALSE); + } + return(TRUE); +} + +BOOLEAN pro_failsafe_restore_buffers(FAILSAFECONTEXT *pfscntxt, int *error) +{ +dword *pdw; +dword blockno,entries_this_record,entries_completed; +dword total_blocks_mapped,replacement_block; +dword fat_start, fat_end; +int i,index_offset,index_page; + + *error = 0; + pdw = fs_map_index_page(pfscntxt, 0, FALSE); + if (!pdw) + { /* fs_map_index_page set errno */ +io_error: + *error = FS_STATUS_IO_ERROR; + return(FALSE); + } + total_blocks_mapped = to_DWORD((byte *)(pdw+INDEX_OFFSET_TOTAL_BLOCKS_MAPPED)); + /* first replacement block is just past index blocks */ + replacement_block = to_DWORD((byte *)(pdw+INDEX_OFFSET_NUMB_INDEX_BLOCKS)); + + fat_start = pfscntxt->pdrive->fatblock; + fat_end = pfscntxt->pdrive->fatblock+pfscntxt->pdrive->secpfat; + + entries_completed = 0; + entries_this_record = JOURNAL_ENTRIES_ZERO; + index_offset = JOURNAL_HEADER_SIZE; + index_page = 0; + while (entries_completed < total_blocks_mapped) + { + if ( (entries_completed + entries_this_record) > total_blocks_mapped) + entries_this_record = total_blocks_mapped-entries_completed; + pdw = fs_map_index_page(pfscntxt, index_page, FALSE); + if (!pdw) + goto io_error; + pdw += index_offset; + for (i = 0; i < (int)entries_this_record; i++,pdw++,replacement_block++) + { + blockno = to_DWORD((byte *)pdw); + if (!failsafe_read_nv_buffer(pfscntxt, replacement_block,(byte *)pfscntxt->scratch_block)) + goto io_error; + if (!(devio_write(pfscntxt->pdrive->driveno, + blockno, (byte *)pfscntxt->scratch_block, (int) 1, FALSE) )) + goto io_error; + if (fat_start <= blockno && fat_end > blockno) + if (!(devio_write(pfscntxt->pdrive->driveno, + blockno + pfscntxt->pdrive->secpfat, (byte *)pfscntxt->scratch_block, (int) 1, FALSE) )) + goto io_error; + } + entries_completed += entries_this_record; + index_offset = 0; + entries_this_record = JOURNAL_ENTRIES_P_BLOCK; + index_page += 1; + } + return(TRUE); +} + +void fs_rewind_failsafe(FAILSAFECONTEXT *pfscntxt) +{ +int i,hash_index; +struct fsblockmap *pbm; +struct fsblockmap *pbm_temp; + for (hash_index = 0; hash_index < FAILSAFE_HASHTABLE_SIZE; hash_index++) + { + pbm = pfscntxt->blockmap_hash_tbl[hash_index]; + while (pbm) + { + pbm_temp = pbm; + pbm = pbm->pnext; + pbm_temp->pnext_by_block = 0; + pbm_temp->pnext = pfscntxt->blockmap_freelist; + pfscntxt->blockmap_freelist = pbm_temp; + } + pfscntxt->blockmap_hash_tbl[hash_index] = 0; + } + /* Rewind the index page buffers */ + pfscntxt->next_index_buffer = 0; + for (i = 0; i < CFG_NUM_INDEX_BUFFERS; i++) + { + pfscntxt->index_offset[i] = 0; + pfscntxt->index_dirty[i] = 0; + + } + + /* Rewind the failsafe buffer */ + pfscntxt->sorted_blockmap = 0; + pfscntxt->total_blocks_mapped = 0; + pfscntxt->journal_checksum = 0; + +} + +dword fs_check_mapped(FAILSAFECONTEXT *pfscntxt, dword blockno, int *error) +{ +int hash_index; +struct fsblockmap *pbm; + *error = 0; + hash_index = (int) blockno&FAILSAFE_HASHMASK; + pbm = pfscntxt->blockmap_hash_tbl[hash_index]; + while (pbm) + { + if (pbm->blockno > blockno) + break; /* Sorted list and we are past it */ + if (pbm->blockno == blockno) + return(pbm->replacement_block); + pbm = pbm->pnext; + } + if (!pfscntxt->blockmap_freelist) + { /* The free list is full double check in manual mode */ + return(fs_block_map_scan(pfscntxt, blockno, error)); + } + return(0); +} + +dword fs_block_map_replace(FAILSAFECONTEXT *pfscntxt, dword blockno); + +dword fs_map_block(FAILSAFECONTEXT *pfscntxt, dword blockno) +{ +int hash_index,error; +dword replacement_block; +struct fsblockmap *pbm; +struct fsblockmap *prevpbm; +struct fsblockmap *newpbm; + hash_index = (int) blockno&FAILSAFE_HASHMASK; + pbm = pfscntxt->blockmap_hash_tbl[hash_index]; + /* Search the hash table for a replacement */ + prevpbm = 0; + while (pbm) + { + if (pbm->blockno > blockno) + break; + if (pbm->blockno == blockno) + return(pbm->replacement_block); + prevpbm = pbm; + pbm = pbm->pnext; + } + /* We didn't find one. If we are out of block map structures search + the actual map */ + if (!pfscntxt->blockmap_freelist) + { + error = 0; + replacement_block = fs_block_map_scan(pfscntxt,blockno,&error); + if (error) /* fs_block_map_scan set errno */ + return(0); + if (replacement_block) + return (replacement_block); + } + /* remap it now */ + replacement_block = fs_block_map_replace(pfscntxt,blockno); + if (!replacement_block) + return(0); /* fs_block_map_replace set errno */ + if (!pfscntxt->blockmap_freelist) /* out of block map structure. go manual */ + return (replacement_block); + /* Allocate a block map structure and put in the hash table */ + newpbm = pfscntxt->blockmap_freelist; + pfscntxt->blockmap_freelist = newpbm->pnext; + newpbm->replacement_block = replacement_block; + newpbm->blockno = blockno; + + if (prevpbm) + { + newpbm->pnext = prevpbm->pnext; + prevpbm->pnext = newpbm; + } + else + { + newpbm->pnext = pbm; + pfscntxt->blockmap_hash_tbl[hash_index] = newpbm; + } + /* Now link it in a list sorted by block number */ + newpbm->pnext_by_block = 0; + if (!pfscntxt->sorted_blockmap) + pfscntxt->sorted_blockmap = newpbm; + else + { + + prevpbm = 0; + pbm = pfscntxt->sorted_blockmap; + while (pbm) + { + if (blockno < pbm->blockno) + { + newpbm->pnext_by_block = pbm; + if (prevpbm) + prevpbm->pnext_by_block = newpbm; + else + pfscntxt->sorted_blockmap = newpbm; + break; + } + else + { + prevpbm = pbm; + pbm = pbm->pnext_by_block; + if (!pbm) + prevpbm->pnext_by_block = newpbm; + } + } + } + return(replacement_block); +} + +dword fs_block_map_scan(FAILSAFECONTEXT *pfscntxt, dword blockno, int *error) +{ +dword *pdw; +dword blockno_little_endian; +dword replacement_block; +dword entries_this_record,entries_completed; +int i,index_offset,index_page; + + blockno_little_endian = to_DWORD((byte *)&blockno); + replacement_block = pfscntxt->num_index_blocks; /* Starting block */ + + entries_completed = 0; + entries_this_record = JOURNAL_ENTRIES_ZERO; + index_offset = JOURNAL_HEADER_SIZE; + index_page = 0; + while (entries_completed < pfscntxt->total_blocks_mapped) + { + if ( (entries_completed + entries_this_record) > + pfscntxt->total_blocks_mapped ) + entries_this_record = pfscntxt->total_blocks_mapped-entries_completed; + pdw = fs_map_index_page(pfscntxt, index_page,FALSE); + if (!pdw) + { /* fs_map_index_page set errno */ + *error = 1; + return(0); + } + pdw += index_offset; + + for (i = 0; i < (int)entries_this_record; i++,replacement_block++,pdw++) + if (*pdw == blockno_little_endian) + return(replacement_block); + entries_completed += entries_this_record; + index_offset = 0; + entries_this_record = JOURNAL_ENTRIES_P_BLOCK; + index_page += 1; + } + return(0); /* Did not find one */ +} + +dword fs_block_map_find_replacement(FAILSAFECONTEXT *pfscntxt, dword replacement_block, int *error) +{ +dword ltemp, *pdw; +int index_page,index_offset; + + if (replacement_block < pfscntxt->num_index_blocks) + { /* fs_map_index_page set errno */ + *error = 1; + return(0); + } + + ltemp = replacement_block-pfscntxt->num_index_blocks; + if (ltemp < JOURNAL_ENTRIES_ZERO) + { + index_offset = (int) (ltemp+JOURNAL_HEADER_SIZE); + index_page = 0; + } + else + { + ltemp = ltemp-JOURNAL_ENTRIES_ZERO; + index_page = 1 + (int)ltemp/JOURNAL_ENTRIES_P_BLOCK; + index_offset = (int) ltemp%JOURNAL_ENTRIES_P_BLOCK; + } + + pdw = fs_map_index_page(pfscntxt, index_page, FALSE); + if (!pdw) + { /* fs_map_index_page set errno */ + *error = 1; + return(0); + } + pdw += index_offset; + return(to_DWORD((byte *)pdw)); +} + + +dword fs_block_map_replace(FAILSAFECONTEXT *pfscntxt, dword blockno) +{ +dword *pdw; +dword blockno_little_endian; +dword replacement_block; +int index_offset,index_page; + + if (pfscntxt->total_blocks_mapped >= pfscntxt->num_remap_blocks) + { + rtfs_set_errno(PEJOURNALFULL); + return(0); + } + + blockno_little_endian = to_DWORD((byte *)&blockno); + replacement_block = pfscntxt->total_blocks_mapped; + + if (replacement_block < JOURNAL_ENTRIES_ZERO) + { + index_page = 0; + index_offset = (int) replacement_block + JOURNAL_HEADER_SIZE; + } + else + { + dword ltemp; + ltemp = replacement_block - JOURNAL_ENTRIES_ZERO; + index_page = 1 + (int)ltemp/JOURNAL_ENTRIES_P_BLOCK; + index_offset = (int) ltemp%JOURNAL_ENTRIES_P_BLOCK; + } + pdw = fs_map_index_page(pfscntxt, index_page, TRUE); + if (!pdw) + return(0); /* fs_map_index_page set errno */ + pdw += index_offset; + *pdw = blockno_little_endian; + replacement_block = replacement_block + pfscntxt->num_index_blocks; + pfscntxt->journal_checksum += blockno; + pfscntxt->total_blocks_mapped += 1; + return(replacement_block); +} + +dword * fs_map_index_page(FAILSAFECONTEXT *pfscntxt, int index_page, BOOLEAN writing) +{ + int i,j,record_handle; + + record_handle = index_page + 1; /* add one since record_handle(0) == free */ + /* Search allocated buffer pool for a match */ + for (i = 0; i < CFG_NUM_INDEX_BUFFERS; i++) + { + if (pfscntxt->index_offset[i] == record_handle) + { + if (writing) + pfscntxt->index_dirty[i] = TRUE; + return (&pfscntxt->index_buffer[i][0]); + } + } + /* swap out a buffer */ + j = pfscntxt->next_index_buffer; + for (i = 0; i < CFG_NUM_INDEX_BUFFERS; i++) + { + /* don't swap first buffer record_handle == 1 because that + contains journal_index_buffer and is accessed often */ + if (pfscntxt->index_offset[j] != 1) + { + if (pfscntxt->index_dirty[j]) + { + /* write the buffer if it is dirty */ + if (!failsafe_write_nv_buffer(pfscntxt, pfscntxt->index_offset[j]-1, (byte *)pfscntxt->index_buffer[j])) + { + rtfs_set_errno(PEIOERRORWRITEJOURNAL); + return(0); + } + } + if (!failsafe_read_nv_buffer(pfscntxt, index_page, (byte *)pfscntxt->index_buffer[j])) + { + rtfs_set_errno(PEIOERRORREADJOURNAL); + return(0); + } + + pfscntxt->index_dirty[j] = writing; + pfscntxt->index_offset[j] = record_handle; + i = j+1; + if (i == CFG_NUM_INDEX_BUFFERS) + i = 0; + pfscntxt->next_index_buffer = i; + return (&pfscntxt->index_buffer[j][0]); + } + j = j+1; + if (j == CFG_NUM_INDEX_BUFFERS) + j = 0; + pfscntxt->next_index_buffer = j; + } + return (0); /* Should never get here */ +} + +BOOLEAN fs_flush_index_pages(FAILSAFECONTEXT *pfscntxt) +{ + int i; + for (i = 0; i < CFG_NUM_INDEX_BUFFERS; i++) + { + if (pfscntxt->index_dirty[i]) + { + /* write the buffer if it is dirty */ + /* index_offset[i]-1 is because records are tagged 1-N not 0-N*/ + if (!failsafe_write_nv_buffer(pfscntxt, pfscntxt->index_offset[i]-1, (byte *)pfscntxt->index_buffer[i])) + { + rtfs_set_errno(PEIOERRORWRITEJOURNAL); + return (FALSE); /* Should never get here */ + } + pfscntxt->index_dirty[i] = FALSE; + } + } + return (TRUE); /* Should never get here */ +} + + +BOOLEAN block_devio_read(DDRIVE *pdrive, dword blockno, byte * buf) +{ +int error = 0; +dword mapped_block; +FAILSAFECONTEXT *pfscntxt; + + /* Read from the journal file if enabled and the block is mapped */ + pfscntxt = (FAILSAFECONTEXT *) pdrive->pfscntxt; + if (pfscntxt && (pfscntxt->configuration_flags & FS_MODE_JOURNALING)) + { + mapped_block = fs_check_mapped(pfscntxt, blockno, &error); + if (error) + return(FALSE); + if (mapped_block) + { + if (failsafe_read_nv_buffer(pfscntxt, mapped_block, buf)) + return(TRUE); + else + { + rtfs_set_errno(PEIOERRORREADJOURNAL); + return(FALSE); + } + } + } /* not journalling or not mapped so read from the volume */ + return(devio_read(pdrive->driveno, blockno, buf, (int) 1, FALSE)); +} + +BOOLEAN block_devio_write(BLKBUFF *pblk) +{ +dword mapped_block; +FAILSAFECONTEXT *pfscntxt; + /* Write to the journal file if enabled */ + pfscntxt = (FAILSAFECONTEXT *) pblk->pdrive->pfscntxt; + if (pfscntxt && pfscntxt->configuration_flags & FS_MODE_JOURNALING) + { + mapped_block = fs_map_block(pfscntxt, pblk->blockno); + if (!mapped_block) /* fs_map_block() set errno */ + return(FALSE); + if (failsafe_write_nv_buffer(pfscntxt, mapped_block,pblk->data)) + return (TRUE); + else + { + rtfs_set_errno(PEIOERRORWRITEJOURNAL); + return(FALSE); + } + } + else /* not journalling or not mapped so read from the volume */ + return(devio_write(pblk->pdrive->driveno,pblk->blockno, pblk->data, (int) 1, FALSE)); +} + +BOOLEAN fat_devio_write(DDRIVE *pdrive, FATBUFF *pblk, int fatnumber) +{ +dword mapped_block; +FAILSAFECONTEXT *pfscntxt; + + /* Write to the journal file if enabled */ + pfscntxt = (FAILSAFECONTEXT *) pdrive->pfscntxt; + if (pfscntxt && pfscntxt->configuration_flags & FS_MODE_JOURNALING) + { + if (fatnumber) /* Don't journal second FAT copy */ + return (TRUE); + mapped_block = fs_map_block(pfscntxt, pblk->fat_blockno); + if (!mapped_block) /* fs_map_block() set errno */ + return(FALSE); + if (failsafe_write_nv_buffer(pfscntxt, mapped_block,pblk->fat_data)) + return (TRUE); + else + { + rtfs_set_errno(PEIOERRORWRITEJOURNAL); + return(FALSE); + } + } + else /* not journalling or not mapped so read from the volume */ + { + dword blockno; + if (fatnumber) + blockno = pblk->fat_blockno+pdrive->secpfat; + else + blockno = pblk->fat_blockno; + return(devio_write(pdrive->driveno,blockno, pblk->fat_data, (int) 1, FALSE)); + } +} + +BOOLEAN fat_devio_read(DDRIVE *pdrive, dword blockno, byte *fat_data) +{ +int error = 0; +dword mapped_block; +FAILSAFECONTEXT *pfscntxt; + + /* Read from the journal file if enabled and the block is mapped */ + pfscntxt = (FAILSAFECONTEXT *) pdrive->pfscntxt; + if (pfscntxt && pfscntxt->configuration_flags & FS_MODE_JOURNALING) + { + mapped_block = fs_check_mapped(pfscntxt, blockno, &error); + if (error) + return(FALSE); + if (mapped_block) + { + if (failsafe_read_nv_buffer(pfscntxt, mapped_block, fat_data)) + return(TRUE); + else + { + rtfs_set_errno(PEIOERRORREADJOURNAL); + return(FALSE); + } + } + } /* not journalling or not mapped so read from the volume */ + return(devio_read(pdrive->driveno, blockno, fat_data, (int) 1, FALSE)); +} + + +#endif /* (INCLUDE_FAILSAFE_CODE) */ + + diff --git a/build/libraries/fatfs/ARM7/prfsnvio.c b/build/libraries/fatfs/ARM7/prfsnvio.c new file mode 100644 index 0000000..8bf3ba0 --- /dev/null +++ b/build/libraries/fatfs/ARM7/prfsnvio.c @@ -0,0 +1,406 @@ +/* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* PRFSNVIO.C - ERTFS-PRO FailSafe journal file routines */ +/* Contains source code to implement journal file accesses read, write, + open and create. + The following routines are exported: + + failsafe_reopen_nv_buffer() + failsafe_create_nv_buffer() + failsafe_write_nv_buffer() + failsafe_read_nv_buffer() + + Note: These routines create and maintains a journal file on the + disk. If you have a closed system and you prefer to use system non + volatile ram instead please modify these for functions to use that + resource instead of a disk based file. +*/ + +#include + +#if (INCLUDE_FAILSAFE_CODE) + +static BOOLEAN open_failsafe_file(FAILSAFECONTEXT *pfscntxt, BOOLEAN create); + +/* +* failsafe_reopen_nv_buffer - Open the failsafe buffer. +* +* Summary: +* BOOLEAN failsafe_reopen_nv_buffer(FAILSAFECONTEXT *pfscntxt) +* +* Description: +* +* This routine must check for the existence of a failsafe buffer on the +* current disk or in system non volatile ram and return TRUE if one exists, +* or FALSE if one does not. +* It may use the field nv_buffer_handle in the structure pointed to +* by pfscntxt to store a handle for later access by failsafe_read_nv_buffer() +* and failsafe_write_nv_buffer(). +* +*/ + +BOOLEAN failsafe_reopen_nv_buffer(FAILSAFECONTEXT *pfscntxt) +{ + return(open_failsafe_file(pfscntxt, FALSE)); +} + +/* +* failsafe_create_nv_buffer - Create the failsafe buffer. +* +* Summary: +* BOOLEAN failsafe_create_nv_buffer(FAILSAFECONTEXT *pfscntxt) +* +* Description: +* +* This routine must create a failsafe buffer on the current disk or +* in system NV ram and return TRUE if successful, FALSE if it is +* unsuccessful. +* It may use the field nv_buffer_handle in the sructure pointed to +* by pfscntxt to store a handle for later access by failsafe_read_nv_buffer() +* and failsafe_write_nv_buffer(). +* +* The failsafe buffer must contain space for: +* (pfscntxt->num_remap_blocks + pfscntxt->num_index_blocks) +* 512 byte blocks. +* +* The source code in prfailsf.c implements the failsafe buffer in a hidden +* file named \FAILSAFE on the disk. +* The reference implementation is convenient and should be adequate for +* most applications. If it is more desirable to implement the failsafe +* buffer in flash or NVRAM then these functions should be modified to access +* that media instead. +* +*/ + +BOOLEAN failsafe_create_nv_buffer(FAILSAFECONTEXT *pfscntxt) +{ + /* Set the journal file size to zero to force the open routine to + open the file. If the first open succeeds and the size is as big + as pfscntxt->correct_journal_size then we won't have to reopen it + in subsequant calls to restore and create */ + pfscntxt->journal_file_size = 0; + return(open_failsafe_file(pfscntxt, TRUE)); +} + +/* +* failsafe_read_nv_buffer - Write a block to the failsafe buffer. +* +* Summary: +* BOOLEAN failsafe_write_nv_buffer( +* FAILSAFECONTEXT *pfscntxt, +* dword block_no, +* byte *pblock) +* +* Description: +* +* This routine must write one block to the block at offset block_no +* in the failsafe buffer on the current disk or in system NV ram and +* return TRUE if successful, FALSE if it is unsuccessful. +* +*/ + +BOOLEAN failsafe_write_nv_buffer(FAILSAFECONTEXT *pfscntxt, dword block_no, byte *pblock) +{ + if (!pfscntxt->nv_buffer_handle || (pfscntxt->journal_file_size <= block_no)) + { + rtfs_set_errno(PEIOERRORWRITEJOURNAL); + return(FALSE); + } + return (devio_write(pfscntxt->pdrive->driveno, + pfscntxt->nv_buffer_handle+block_no, pblock, + (word)1, TRUE)); +} + +/* +* failsafe_read_nv_buffer - Read a block from the failsafe buffer. +* +* Summary: +* BOOLEAN failsafe_read_nv_buffer( +* FAILSAFECONTEXT *pfscntxt, +* dword block_no, +* byte *pblocks) +* +* Description: +* +* This routine must read one block from the block at offset block_no +* in the failsafe buffer on the current disk or in system NV ram and +* return TRUE if successful, FALSE if it is unsuccessful. +* +*/ + +BOOLEAN failsafe_read_nv_buffer(FAILSAFECONTEXT *pfscntxt, dword block_no, byte *pblock) +{ + if (!pfscntxt->nv_buffer_handle || (pfscntxt->journal_file_size <= block_no)) + { + rtfs_set_errno(PEIOERRORREADJOURNAL); + return(FALSE); + } + return(devio_read(pfscntxt->pdrive->driveno, + pfscntxt->nv_buffer_handle+block_no, pblock, + (word)1, TRUE)); +} + +/* +* open_failsafe_file - low level open/create of failsafe journal file +* +* Summary: +* BOOLEAN open_failsafe_file(FAILSAFECONTEXT *pfscntxt, BOOLEAN create) +* +* Description: +* +* This opens the failsafe file and check if it contains enough contiguous +* blocks to hold a worst case journal file. If create is not requested and +* it can't find the file or the file is too small then it fails. +* If create is requested then it creates and extends the file as necessary. +* +* This routine is called when from ERTFS with the drive already locked, it +* uses low level access routines to create or open a contiguous file +* +* returns TRUE if successful, FALSE if it is unsuccessful. +* +*/ + +static BOOLEAN validate_failsafe_blocks(FAILSAFECONTEXT *pfscntxt, BOOLEAN fix_errors); + +static BOOLEAN open_failsafe_file(FAILSAFECONTEXT *pfscntxt, BOOLEAN create) +{ + DROBJ *proot, *pobj; + byte path[32]; /* leave room for "FAILSAFE" 32 is plenty */ + byte filename[32]; + byte fileext[4]; + dword first_cluster, contig_clusters, n_clusters,next_cluster,required_journal_file_size,first_hit; + int end_of_chain; + DDRIVE *pdrive; + FAILSAFECONTEXT *saved_pfscntxt; + BOOLEAN doupdate = FALSE; + BOOLEAN doflush = FALSE; + + + pfscntxt->nv_buffer_handle = 0; + pdrive = pfscntxt->pdrive; + if (!pdrive) + return(FALSE); + saved_pfscntxt = (FAILSAFECONTEXT *) pdrive->pfscntxt; + pdrive->pfscntxt = (void *) 0; + + proot = pc_get_root(pdrive); + if (!proot || + !pc_parsepath((byte *)&path[0],(byte *)&filename[0],fileext, + rtfs_strtab_user_string(USTRING_SYS_FSFILENAME))) + { +return_error: + pdrive->pfscntxt = (void *) saved_pfscntxt; + return(FALSE); + } + /* See if it exists */ + pobj = pc_get_inode( 0, proot, filename, fileext, GET_INODE_MATCH); + if (!pobj) + { + if (!create) + goto return_error; + else + { + /* Create a file entry in the root, hidden, readonly */ + pobj = pc_mknode( proot, (byte *)filename, fileext, ARDONLY|AHIDDEN, 0); + if (!pobj) + { + + goto return_error; + } + doupdate = TRUE; + } + } + first_cluster = pc_finode_cluster(pobj->pdrive,pobj->finode); + if (first_cluster) + n_clusters = FATOP(pdrive)->fatop_get_chain(pdrive, first_cluster,&next_cluster, 0xffffffff, &end_of_chain); + else + n_clusters = 0; + + pfscntxt->journal_file_size = n_clusters * pdrive->secpalloc; + if (create) + { /* Creating or extend or truncate to the correct size */ + n_clusters = (CLUSTERTYPE) + ((pfscntxt->num_remap_blocks + + pfscntxt->num_index_blocks + + pdrive->secpalloc-1) >> pdrive->log2_secpalloc); + required_journal_file_size = n_clusters * pdrive->secpalloc; + if (pfscntxt->journal_file_size != required_journal_file_size) + { + doupdate = TRUE; + doflush = TRUE; + /* Truncate if necessary */ + if (first_cluster) + { + FATOP(pdrive)->fatop_freechain(pdrive, first_cluster, 0, 0xffffffff); + pc_pfinode_cluster(pdrive,pobj->finode,0); + pobj->finode->fsize = 0; + } + /* Allocate */ + first_cluster = 0; +#if (defined(AF_CONTIGUOUS_MODE_FORCE)) /* Should be true for version 5.0 and up */ + contig_clusters = FATOP(pdrive)->fatop_alloc_chain(pdrive, &first_cluster, n_clusters, AF_CONTIGUOUS_MODE_FORCE); +#else + contig_clusters = 0; + first_hit = 0; + /* Bug fix Nan 9, 2006 + fatop_alloc_chain - allocates clusters starting from + pdrive->free_contig_pointer. or from first_cluster, if + first_cluster is not zero. + The problem is that if first_cluster is not zero + we link it to the newly allocated cluster chain, which is + not what we want. So the code has been modified to advance + pdrive->free_contig_pointer and always have first_cluster == 0 */ + pdrive->free_contig_pointer = pdrive->free_contig_base; + + while (contig_clusters < n_clusters) + { /* alloc chain can't guarantee n_clusters contiguous so + loop and free until we get contig_clusters == n_clusters */ + first_cluster = 0; /* must be zero or it will be linked. */ + contig_clusters = FATOP(pobj->pdrive)->fatop_alloc_chain(pobj->pdrive, &first_cluster, n_clusters, FALSE); + if (!first_hit) + first_hit = first_cluster; + else if (first_hit == first_cluster) + { + FATOP(pdrive)->fatop_freechain(pdrive, first_cluster, 0, 0xffffffff); + break; + } + if (contig_clusters == 0) + break; + if (contig_clusters < n_clusters) + { + FATOP(pdrive)->fatop_freechain(pdrive, first_cluster, 0, 0xffffffff); + pdrive->free_contig_pointer = first_cluster+contig_clusters; + } + } + pdrive->free_contig_pointer = pdrive->free_contig_base; /* restore pointer */ +#endif + if (contig_clusters < n_clusters) + { + pfscntxt->journal_file_size = 0; + pobj->finode->fsize = 0; + pc_pfinode_cluster(pdrive,pobj->finode,0); + pc_rmnode(pobj); + doupdate = FALSE; + } + else + { + pfscntxt->journal_file_size = contig_clusters * pdrive->secpalloc; + pc_pfinode_cluster(pdrive,pobj->finode,first_cluster); + pobj->finode->fsize = pfscntxt->journal_file_size * 512; + } + } + if ((doflush && !FATOP(pdrive)->fatop_flushfat(pdrive->driveno)) || + (doupdate && !pc_update_inode(pobj, TRUE, TRUE)) ) + { + pfscntxt->journal_file_size = 0; + } + } + + if (pfscntxt->journal_file_size == 0) + { + pfscntxt->nv_buffer_handle = 0; + pc_freeobj(pobj); + pc_free_all_blk(pdrive); + pc_free_all_fat_blocks(&pdrive->fatcontext); + goto return_error; + } + else + { + pfscntxt->nv_buffer_handle = + pobj->pdrive->partition_base + + pc_cl2sector(pobj->pdrive, first_cluster); + pc_freeobj(pobj); + /* Clear buffer pool and fat buffer pool */ + pc_free_all_blk(pdrive); + pc_free_all_fat_blocks(&pdrive->fatcontext); +#if (CFG_VALIDATE_JOURNAL) + if (!validate_failsafe_blocks(pfscntxt, TRUE)) + { + pfscntxt->journal_file_size = 0; + pfscntxt->nv_buffer_handle = 0; + /* Tell the upper levels that an IO error occured. + This is unrecoverable */ + pfscntxt->open_status = FS_STATUS_IO_ERROR; + pdrive->pfscntxt = (void *) saved_pfscntxt; + return(FALSE); + } +#endif + pdrive->pfscntxt = (void *) saved_pfscntxt; + return(TRUE); + } +} + +#if (CFG_VALIDATE_JOURNAL) + +static byte static_validate_buffer[CFG_VALIDATE_BUFFER_SIZE*512]; +byte *get_validate_buffer(word *pblocks_per_buffer) +{ + *pblocks_per_buffer = CFG_VALIDATE_BUFFER_SIZE; + return(&static_validate_buffer[0]); +} + +static BOOLEAN validate_failsafe_blocks(FAILSAFECONTEXT *pfscntxt, BOOLEAN fix_errors) +{ +dword block_no,n_blocks_left; +word blocks_per_buffer, blocks_this_access; +byte *pbuffer; +int driveno; + + driveno = pfscntxt->pdrive->driveno; + pbuffer = get_validate_buffer(&blocks_per_buffer); + if (blocks_per_buffer > 128) + blocks_per_buffer = 128; + /* Read the journal file */ + block_no = pfscntxt->nv_buffer_handle; + n_blocks_left = pfscntxt->journal_file_size; + while (n_blocks_left) + { + if (n_blocks_left > (dword) blocks_per_buffer) + blocks_this_access = blocks_per_buffer; + else + blocks_this_access = (word) n_blocks_left; + if (!devio_read(driveno, block_no, pbuffer,blocks_this_access,TRUE)) + break; + block_no += blocks_this_access; + n_blocks_left -= blocks_this_access; + } + if (n_blocks_left == 0) + return(TRUE); /* Read all blocks without failure */ + if (!fix_errors) + return(FALSE); + /* We got here because a read error occured + Write all zeroes to the file to see if we can correct it. */ + /* Write the journal file */ + block_no = pfscntxt->nv_buffer_handle; + n_blocks_left = pfscntxt->journal_file_size; + while (n_blocks_left) + { + if (n_blocks_left > (dword) blocks_per_buffer) + blocks_this_access = blocks_per_buffer; + else + blocks_this_access = (word) n_blocks_left; + /* Zero the buffer every time we use it. This takes a little + extra time but in the unlikely event other threads are using it + for reads we will zero it before we write each time */ + rtfs_memset((void *)pbuffer, 0, (int)(blocks_this_access*512)); + if (!devio_write(driveno, block_no, pbuffer,blocks_this_access,TRUE)) + break; + block_no += blocks_this_access; + n_blocks_left -= blocks_this_access; + } + if (n_blocks_left != 0) + return(FALSE); /* Fail because We could not write the file.. */ + else + { + /* We overwrote, now revalidate */ + return(validate_failsafe_blocks(pfscntxt, FALSE)); + } +} +#endif /* (CFG_VALIDATE_JOURNAL) */ + +#endif /* (INCLUDE_FAILSAFE_CODE) */ diff --git a/build/libraries/fatfs/ARM7/prfstest.c b/build/libraries/fatfs/ARM7/prfstest.c new file mode 100644 index 0000000..6b8be39 --- /dev/null +++ b/build/libraries/fatfs/ARM7/prfstest.c @@ -0,0 +1,1786 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ + +/*************************************************************************** + fs_test - Test Failsafe mode + + Description + Perform a series of tests on ERTFS-Pro to verify correct functioning + of FailSafe mode. + + Summary: + BOOLEAN fs_test(byte *path) + + Returns + Returns TRUE if all test succeeded False otherwise. + + Note: fs_test() sends output to the console through the macro FSDEBUG(). + which is defined: + #define FSDEBUG(X) printf("%s\n", X); + To run quietly replace this definition with the following: + #define FSDEBUG(X) + +***************************************************************************/ + +#include + +#if (INCLUDE_CS_UNICODE) +byte *testtree[] = { + (byte *)L"\\FSTSDIR1", + (byte *)L"\\FSTSDIR1\\FSTSDIR2", + (byte *)L"\\FSTSDIR1\\FSTSDIR2\\FSTSDIR3", + (byte *)L"\\FSTSDIR1\\FSTSDIR2\\FSTSDIR3\\FSTDIR4", + 0}; +byte *testfiles[] = { + (byte *)L"\\FSTSFIL1", + (byte *)L"\\FSTSFIL2", + (byte *)L"\\FSTSFIL3", + (byte *)L"\\FSTSFIL4", + (byte *)L"\\FSTSFIL5", + 0}; + +byte *apitestfile = (byte *)"\\FSTSDIR1\\FSTSFIL1"; +byte *apimovedfile = (byte *)"\\FSTSDIR1\\FSTSMOV1"; +#else +byte *testtree[] = { + (byte *)"\\FSTSDIR1", + (byte *)"\\FSTSDIR1\\FSTSDIR2", + (byte *)"\\FSTSDIR1\\FSTSDIR2\\FSTSDIR3", + (byte *)"\\FSTSDIR1\\FSTSDIR2\\FSTSDIR3\\FSTDIR4", + 0}; +byte *testfiles[] = { + (byte *)"\\FSTSFIL1", + (byte *)"\\FSTSFIL2", + (byte *)"\\FSTSFIL3", + (byte *)"\\FSTSFIL4", + (byte *)"\\FSTSFIL5", + 0}; + +byte *apitestfile = (byte *)"\\FSTSDIR1\\FSTSFIL1"; +byte *apimovedfile = (byte *)"\\FSTSDIR1\\FSTSMOV1"; +#endif + + +#if (INCLUDE_FAILSAFE_CODE) +/* #define FSDEBUG(X) printf("%s\n", X); */ +#define FSDEBUG(X) RTFS_PRINT_STRING_2(USTRING_SYS_NULL,(byte *)X,PRFLG_NL); + +#define DO_FILE_NVIO_TEST 1 +#define DO_INDEX_TEST 1 +#define DO_API_TEST 1 +#define DO_FSAPI_TEST 1 +#define DO_RESTORE_TEST 1 + +BOOLEAN fs_test_nvio_main(byte *path); +BOOLEAN fs_test_indexing_main(byte *path); +BOOLEAN fs_test_api_main(byte *path); +BOOLEAN fs_test_fsapi_main(byte *path); +BOOLEAN fs_test_restore_main(byte *path); +BOOLEAN fs_test_nvio_delete_fsfile(byte *path); + +extern int rand(void); + +/* Data and structures for test purposes */ +byte dummybuff[512]; +FAILSAFECONTEXT test_fscontext; +#define TEST_BLOCKMAPSIZE 64 +FSBLOCKMAP test_failsafe_blockmap_array[TEST_BLOCKMAPSIZE]; + +/* private routines imported from prfscore.c for test purposes */ +dword fs_map_block(FAILSAFECONTEXT *pfscntxt, dword blockno); +dword fs_block_map_find_replacement(FAILSAFECONTEXT *pfscntxt, dword replacement_block, int *error); +dword fs_block_map_scan(FAILSAFECONTEXT *pfscntxt, dword blockno, int *error); +dword fs_check_mapped(FAILSAFECONTEXT *pfscntxt, dword blockno, int *error); +BOOLEAN pro_failsafe_checksum_index(FAILSAFECONTEXT *pfscntxt, int *error); +BOOLEAN fs_flush_index_pages(FAILSAFECONTEXT *pfscntxt); +int pro_failsafe_flush_header(FAILSAFECONTEXT *pfscntxt, dword status); +dword * fs_map_index_page(FAILSAFECONTEXT *pfscntxt, int index_page, BOOLEAN writing); + +/* private routines in this file for test purposes */ +BOOLEAN open_index_test(byte *path, int flags, int block_map_size, dword user_journal_size); +BOOLEAN open_index_test_full(byte *path, int flags, int block_map_size, dword user_journal_size); +void fs_test_make_filename(byte *buffer, byte *path, byte *filename); +dword fs_test_fill_file(byte *path, byte *filename, int nblocks); +BOOLEAN fs_test_fill_disk(byte *path, byte *filename); +BOOLEAN fs_test_rm_file(byte *path, byte *filename); +BOOLEAN fs_test_mktree(byte *path); +BOOLEAN fs_test_chkstats(CHKDISK_STATS *p1,CHKDISK_STATS *p2); +static void fs_test_clear_index_buffers(void); +int fs_test_commit_failure(FAILSAFECONTEXT *pfscntxt,BOOLEAN force_checksum_error); + +int fs_test(byte *path) +{ +#if (DO_INDEX_TEST) + FSDEBUG("INDEX TEST: Begin") + if (!fs_test_indexing_main(path)) + { + FSDEBUG("INDEX TEST: Failed") + return(0); + } + FSDEBUG("INDEX TEST: Success") +#endif +#if (DO_FILE_NVIO_TEST) + FSDEBUG("NVIO TEST: Begin") + if (!fs_test_nvio_main(path)) + { + FSDEBUG("NVIO TEST: Failed") + return(0); + } + FSDEBUG("NVIO TEST: Success") +#endif +#if (DO_API_TEST) + FSDEBUG("API TEST: Begin") + if (!fs_test_api_main(path)) + { + FSDEBUG("API TEST: Failed") + return(0); + } + FSDEBUG("API TEST: Success") +#endif +#if (DO_RESTORE_TEST) + FSDEBUG("RESTORE TEST: Begin") + if (!fs_test_restore_main(path)) + { + FSDEBUG("RESTORE TEST: Fail") + return(0); + } + FSDEBUG("RESTORE TEST: Success") +#endif +#if (DO_FSAPI_TEST) + FSDEBUG("FSAPI TEST: Begin") + if (!fs_test_fsapi_main(path)) + { + FSDEBUG("FSAPI TEST: Failed") + return(0); + } + FSDEBUG("FSAPI TEST: Success") +#endif + FSDEBUG("FAILSAFE TEST SUITE: Success") + return(1); +} + +#if (DO_INDEX_TEST) +#define BIG_TEST_SIZE (4 * (CFG_NUM_INDEX_BUFFERS * 128)) +dword map_check[BIG_TEST_SIZE]; +static BOOLEAN fs_test_map_cache(dword num_to_map); +static BOOLEAN _fs_test_map_cache(dword num_to_map, BOOLEAN random_fill); +static BOOLEAN fs_test_index_errors(void); + +BOOLEAN fs_test_indexing_main(byte *path) +{ +struct fsblockmap *save_freelist; + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + fs_test_nvio_delete_fsfile(path); + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) + return(FALSE); + /* Test mapping with all fitting in cache */ + FSDEBUG("INDEX TEST: Test mapping with cache > # journaled blocks") + if (!fs_test_map_cache(test_fscontext.blockmap_size-1)) + return(FALSE); + if (!test_fscontext.blockmap_freelist) /* should be one left */ + return(FALSE); + /* Test with not all fitting in cache */ + FSDEBUG("INDEX TEST: Test mapping with cache < # journaled blocks") + if ((dword)test_fscontext.blockmap_size >= test_fscontext.num_remap_blocks) + return(FALSE); + fs_rewind_failsafe(&test_fscontext); + if (!fs_test_map_cache(test_fscontext.num_remap_blocks)) + return(FALSE); + /* It's full make sure another map call fails */ + if (fs_map_block(&test_fscontext, 1023) != 0) + return(FALSE); + /* Test with no cache */ + FSDEBUG("INDEX TEST: Test mapping with no cache") + fs_rewind_failsafe(&test_fscontext); + save_freelist = test_fscontext.blockmap_freelist; + test_fscontext.blockmap_freelist = 0; + if (!fs_test_map_cache(test_fscontext.num_remap_blocks)) + return(FALSE); + if (test_fscontext.total_blocks_mapped != test_fscontext.num_remap_blocks) + return(FALSE); + /* It's full make sure another map call fails */ + FSDEBUG("INDEX TEST: Testing Journal File Full") + if (fs_map_block(&test_fscontext, 1023) != 0) + return(FALSE); + if (get_errno() != PEJOURNALFULL) + return(FALSE); + fs_rewind_failsafe(&test_fscontext); + test_fscontext.blockmap_freelist = save_freelist; + + /* Now do the test simulating a very large file */ + FSDEBUG("INDEX TEST: Test mapping with cache and very large journal") + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE,BIG_TEST_SIZE)) + return(FALSE); + if (!fs_test_map_cache(test_fscontext.num_remap_blocks)) + return(FALSE); + return(TRUE); +} + +static BOOLEAN fs_test_map_cache(dword num_to_map) +{ + /* Test with ascending descending, then random fill */ + if (!_fs_test_map_cache(num_to_map, 1)) + return(FALSE); + if (!_fs_test_map_cache(num_to_map, -1)) + return(FALSE); + if (!_fs_test_map_cache(num_to_map, 0)) + return(FALSE); + if (!fs_test_index_errors()) + return(FALSE); + return(TRUE); +} +static BOOLEAN fs_test_index_errors(void) +{ +int error,rval; +dword *pdw; + /* Now see if the checksum is bad */ + if (pro_failsafe_flush_header(&test_fscontext, FS_STATUS_PROCESSING)) + { /* First flush the index header to the buffer and to disk */ + return(FALSE); + } + if (!pro_failsafe_checksum_index(&test_fscontext, &error)) + return(FALSE); + /* Now clear index pages and checksum (from journal, not cache)*/ + fs_test_clear_index_buffers(); + if (!pro_failsafe_checksum_index(&test_fscontext, &error)) + return(FALSE); + /* Should be in MUST restore state */ + fs_test_clear_index_buffers(); + rval = failsafe_restore_internal(&test_fscontext, FALSE, FALSE); + if (rval != FS_STATUS_MUST_RESTORE) + return(FALSE); + /* Now Simulate Index header problems and check restore */ + /* Force a checksum error */ + FSDEBUG("INDEX TEST: Testing Index Checksum Error") + pdw = fs_map_index_page(&test_fscontext, 0, TRUE); + if (!pdw) return(FALSE); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_INDEX_CHECKSUM),test_fscontext.journal_checksum-1); + if (!fs_flush_index_pages(&test_fscontext)) return(FALSE); + fs_test_clear_index_buffers(); + rval = failsafe_restore_internal(&test_fscontext, FALSE,FALSE); + if (rval != FS_STATUS_BAD_CHECKSUM) + return(FALSE); + fs_test_clear_index_buffers(); + pdw = fs_map_index_page(&test_fscontext, 0, TRUE); + if (!pdw) return(FALSE); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_INDEX_CHECKSUM),test_fscontext.journal_checksum); + if (!fs_flush_index_pages(&test_fscontext)) return(FALSE); + + /* Force an out of date error */ + FSDEBUG("INDEX TEST: Testing Index Out of Date Error") + pdw = fs_map_index_page(&test_fscontext, 0, TRUE); + if (!pdw) return(FALSE); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_FREE_CLUSTERS),test_fscontext.pdrive->known_free_clusters-1); + if (!fs_flush_index_pages(&test_fscontext)) return(FALSE); + fs_test_clear_index_buffers(); + rval = failsafe_restore_internal(&test_fscontext, FALSE,FALSE); + if (rval != FS_STATUS_OUT_OF_DATE) return(FALSE); + fs_test_clear_index_buffers(); + pdw = fs_map_index_page(&test_fscontext, 0, TRUE); + if (!pdw) return(FALSE); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_FREE_CLUSTERS),test_fscontext.pdrive->known_free_clusters); + if (!fs_flush_index_pages(&test_fscontext)) return(FALSE); + + /* Force a bad journal error */ + FSDEBUG("INDEX TEST: Testing Journal File Signature Error") + pdw = fs_map_index_page(&test_fscontext, 0, TRUE); + if (!pdw) return(FALSE); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_VERSION ) ,FS_JOURNAL_VERSION+2); + if (!fs_flush_index_pages(&test_fscontext)) return(FALSE); + fs_test_clear_index_buffers(); + rval = failsafe_restore_internal(&test_fscontext, FALSE,FALSE); + if (rval != FS_STATUS_BAD_JOURNAL) return(FALSE); + fs_test_clear_index_buffers(); + pdw = fs_map_index_page(&test_fscontext, 0, TRUE); + if (!pdw) return(FALSE); + fr_DWORD((byte *) (pdw + INDEX_OFFSET_VERSION ) ,FS_JOURNAL_VERSION); + if (!fs_flush_index_pages(&test_fscontext)) return(FALSE); + fs_test_clear_index_buffers(); + FSDEBUG("INDEX TEST: Testing Good Journal File") + if (pro_failsafe_flush_header(&test_fscontext, FS_STATUS_COMPLETE)) + return(FALSE); + fs_test_clear_index_buffers(); + rval = failsafe_restore_internal(&test_fscontext, FALSE,FALSE); + if (rval != FS_STATUS_OK) return(FALSE); + return(TRUE); +} + + +static void fs_test_clear_index_buffers(void) +{ +int i; + for (i = 0; i < CFG_NUM_INDEX_BUFFERS; i++) + test_fscontext.index_offset[i] = 0; +} + +static BOOLEAN _fs_test_map_cache(dword num_to_map, int fill_op) +{ +int error; +struct fsblockmap *pbm; +dword i, mapped_block, fake_block, ltemp, num_mapped; + + fs_rewind_failsafe(&test_fscontext); + for (i = 0; i < BIG_TEST_SIZE; i++) map_check[i] = 0; + + if (fill_op == -1) + { /* reverse order */ + FSDEBUG("INDEX TEST: Filling Journal File in descending order") + i = num_to_map; + ltemp = 0; + while(i--)/* Repeat the first test backward */ + { + fake_block = 1024+i; + mapped_block = fs_map_block(&test_fscontext, fake_block); + if (mapped_block != ltemp+test_fscontext.num_index_blocks) + return(FALSE); + map_check[i] = mapped_block; + ltemp++; + } + } + else if (fill_op == 0) /* random */ + { + FSDEBUG("INDEX TEST: Filling Journal File in random order") + num_mapped = 0; + while (num_mapped < num_to_map) + { + fake_block = 1024+(rand()%num_to_map); + mapped_block = fs_check_mapped(&test_fscontext, fake_block, &error); + if (error) + return(FALSE); + if (!mapped_block) + { + mapped_block = fs_map_block(&test_fscontext, fake_block); + if (mapped_block != num_mapped+test_fscontext.num_index_blocks) + return(FALSE); + map_check[fake_block-1024] = mapped_block; + num_mapped += 1; + } + } + } + else /* ascending order */ + { + FSDEBUG("INDEX TEST: Filling Journal File in ascending order") + fake_block = 1024; + for (ltemp = 0; ltemp < num_to_map; ltemp++, fake_block++) + { /* Remap a bunch of blocks */ + mapped_block = fs_map_block(&test_fscontext, fake_block); + if (mapped_block != ltemp+test_fscontext.num_index_blocks) + return(FALSE); + map_check[ltemp] = mapped_block; + } + } + fake_block = 1024; + for (ltemp = 0; ltemp < num_to_map; ltemp++, fake_block++) + { /* Verify that they are all there */ + mapped_block = fs_map_block(&test_fscontext, fake_block); + if (map_check[ltemp] != mapped_block) + return(FALSE); + } + /* Fail if this pass added any new blocks to the map */ + if (test_fscontext.total_blocks_mapped != num_to_map) + return(FALSE); + + FSDEBUG("INDEX TEST: Testing Journal Block Search") + fake_block = 1024; + for (ltemp = 0; ltemp < num_to_map; ltemp++, fake_block++) + { /* Check the cache layer and then scan the block layer */ + mapped_block = fs_check_mapped(&test_fscontext, fake_block, &error); + if (error || (map_check[ltemp] != mapped_block)) + return(FALSE); + mapped_block = fs_block_map_scan(&test_fscontext, fake_block, &error); + if (error || (map_check[ltemp] != mapped_block)) + return(FALSE); + } + for (ltemp = 0; ltemp < num_to_map; ltemp++) + { /* Now do the reverse. given the replacement give back the block */ + mapped_block = fs_block_map_find_replacement(&test_fscontext, + map_check[ltemp], &error); + if (error || mapped_block != ltemp+1024) + return(FALSE); + } + ltemp = num_to_map; + while(ltemp--)/* Repeat the first test backward */ + { + fake_block = 1024+ltemp; + mapped_block = fs_map_block(&test_fscontext, fake_block); + if (map_check[ltemp] != mapped_block) + return(FALSE); + } + if (test_fscontext.total_blocks_mapped != num_to_map) + return(FALSE); + + /* Make sure the sorted map is correct */ + FSDEBUG("INDEX TEST: Testing Sorted Journal Block List") + if (test_fscontext.blockmap_freelist) + { + pbm = test_fscontext.sorted_blockmap; + fake_block = 1024; + for (i = 0; i < test_fscontext.total_blocks_mapped; i++, fake_block++) + { + if (!pbm) + return(FALSE); + if (pbm->blockno != fake_block) + return(FALSE); + pbm = pbm->pnext_by_block; + } + if (pbm) + return(FALSE); + } + return(TRUE); +} + + +#endif + +#if (DO_FILE_NVIO_TEST) +dword fs_test_nvio_make_failsafe_file(byte *path, int fsfsize, BOOLEAN clean); + +BOOLEAN fs_test_nvio_main(byte *path) +{ +int step; +dword blockno, fsblockno; +int fsfsize; + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!fs_test_nvio_delete_fsfile(path)) + { + step = 1; +test_failed: + return(FALSE); + } + fsfsize = 100; + /* First verify placement of the journal file */ + FSDEBUG("NVIO TEST: verify journal file placement") + blockno = fs_test_fill_file(path, (byte *) testfiles[0], fsfsize); + if (!blockno) {step = 2; goto test_failed;} + fs_test_rm_file(path, (byte *) testfiles[0]); + fsblockno = fs_test_nvio_make_failsafe_file(path, fsfsize, TRUE); + if (blockno!=fsblockno) {step = 3; goto test_failed;} + /* Now verify placement of the journal file with fragments */ + FSDEBUG("NVIO TEST: verify journal file placement with fragmentation") + blockno = fs_test_fill_file(path, (byte *) testfiles[0], fsfsize/2); + if (!blockno) {step = 3; goto test_failed;} + blockno = fs_test_fill_file(path, (byte *) testfiles[1], fsfsize/2); + if (!blockno) {step = 4; goto test_failed;} + blockno = fs_test_fill_file(path, (byte *) testfiles[2], fsfsize); + if (!blockno) {step = 5; goto test_failed;} + fs_test_rm_file(path, (byte *) testfiles[0]); /* Create the hole */ + fs_test_rm_file(path, (byte *) testfiles[2]); /* failsafe file should go here */ + fsblockno = fs_test_nvio_make_failsafe_file(path, fsfsize, TRUE); + fs_test_rm_file(path, (byte *) testfiles[1]); /* release the fragment */ + if (blockno!=fsblockno) {step = 6; goto test_failed;} + /* Now verify that we can change the Failsafe file size */ + FSDEBUG("NVIO TEST: verify changing journal file size") + fsblockno = fs_test_nvio_make_failsafe_file(path, fsfsize/2, FALSE); + if (!fsblockno) {step = 7; goto test_failed;} + blockno = fs_test_fill_file(path, (byte *) testfiles[0], fsfsize); + if (!blockno) {step = 8; goto test_failed;} + blockno = fs_test_fill_file(path, (byte *) testfiles[1], fsfsize); + if (!blockno) {step = 9; goto test_failed;} + fs_test_rm_file(path, (byte *) testfiles[1]); /* new failsafe file should go here */ + fsblockno = fs_test_nvio_make_failsafe_file(path, fsfsize, TRUE); + fs_test_rm_file(path, (byte *) testfiles[0]); /* release the fragment */ + if (blockno!=fsblockno) {step = 10; goto test_failed;} +{ +dword blocks_total, blocks_free; + if (pc_free(path, &blocks_total, &blocks_free) < 0) + {step = 10; goto test_failed;} + if (blocks_free > 200000) /* 100 mbytes */ + { + FSDEBUG("NVIO TEST: not testing file create error with full disk") + ; + } + else + { + /* Now verify the behavior of Failsafe when the disk is full */ + FSDEBUG("NVIO TEST: test journal file create error with full disk") + if (!fs_test_fill_disk(path, (byte *) testfiles[0])) + {step = 10; goto test_failed;} + fsblockno = fs_test_nvio_make_failsafe_file(path, fsfsize, TRUE); + step = 0; + if (fsblockno) step = 11; + if (get_errno() != PEFSCREATE) step = 12; + if (step) goto test_failed; + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + fs_test_rm_file(path, (byte *) testfiles[0]); /* un-fill the disk */ + } +} + if (!fs_test_nvio_delete_fsfile(path)) + return(FALSE); + return(TRUE); +} + +dword fs_test_nvio_make_failsafe_file(byte *path, int fsfsize, BOOLEAN clean) +{ +byte buffer[32]; +dword ret_val; + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.configuration_flags = FS_MODE_AUTORECOVER; + test_fscontext.blockmap_size = 0; + test_fscontext.user_journal_size = fsfsize; + if (!pro_failsafe_init(path, &test_fscontext)) + return(0); + if (!pc_pwd(path, buffer)) /* Mount the disk */ + { + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + return(0); + } + ret_val = test_fscontext.nv_buffer_handle; + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (clean) + fs_test_nvio_delete_fsfile(path); + return(ret_val); +} + + +#endif + +#if (DO_RESTORE_TEST) + + + +BOOLEAN fs_test_restore_main(byte *path) +{ +CHKDISK_STATS chkstat[4]; +byte buffer[32]; +int rval; + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + fs_test_nvio_delete_fsfile(path); + + /* Shut down failsafe, save check disk info */ + if (!open_index_test(path, 0, TEST_BLOCKMAPSIZE, 0)) return(FALSE); + if (!pc_check_disk(path, &chkstat[0], 0, 0, 0)) return(FALSE); + + /* Check manual restore mode */ + /* Make a directory, get check disk info */ + FSDEBUG("RESTORE TEST: test manual restore") + if (!pc_mkdir(testtree[0])) return(FALSE); + if (!pc_check_disk(path, &chkstat[1], 0, 0, 0)) return(FALSE); + if (fs_test_commit_failure(&test_fscontext,FALSE)) return(FALSE); + /* Checkdisk and compare to the original check disk, should be the same */ + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(FALSE); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(FALSE); + /* Verify restore */ + rval = pro_failsafe_restore(path,&test_fscontext,FALSE,FALSE); + if (rval != FS_STATUS_MUST_RESTORE) return(FALSE); + rval = pro_failsafe_restore(path,&test_fscontext,TRUE,FALSE); + if (rval != FS_STATUS_RESTORED) return(FALSE); + /* Checkdisk,compare to check disk with mkdir when we were journaling */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(FALSE); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(FALSE); + + if (!open_index_test(path, 0, TEST_BLOCKMAPSIZE, 0)) return(FALSE); + /* Check manual restore with checksum error */ + FSDEBUG("RESTORE TEST: test manual restore with bad Journal file") + if (!pc_rmdir(testtree[0])) return(FALSE); + /* Checkdisk and compare to the original check disk, should be the same */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(FALSE); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(FALSE); + /* commit but simulate a checksum error */ + if (fs_test_commit_failure(&test_fscontext,TRUE)) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* Verify restore */ + rval = pro_failsafe_restore(path,&test_fscontext,FALSE,FALSE); + if (rval != FS_STATUS_BAD_CHECKSUM) return(FALSE); + rval = pro_failsafe_restore(path,&test_fscontext,TRUE,FALSE); + if (rval != FS_STATUS_BAD_CHECKSUM) return(FALSE); + + /* Check auto restore mode with a bad journal file */ + /* Check the clear function of pro_failsafe_restore */ + FSDEBUG("RESTORE TEST: test auto restore with bad Journal file") + if (open_index_test(path, FS_MODE_AUTORESTORE, TEST_BLOCKMAPSIZE, 0)) return(FALSE); + if (get_errno() != PEFSRESTOREERROR) return(FALSE); + /* Clear the error and see if we can now mount */ + FSDEBUG("RESTORE TEST: Test pro_failsafe_restore() clear function") + if (pro_failsafe_restore(path,0,FALSE,TRUE)!=FS_STATUS_OK) return(FALSE); + if (!pc_pwd(path, buffer)) /* Mount the disk */ + return(FALSE); + /* The directory should be there now remove and commit */ + if (!pc_rmdir(testtree[0])) return(FALSE); + if (!pro_failsafe_commit(path)) return(FALSE); + /* Checkdisk and compare to the original check disk, should be the same */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(FALSE); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(FALSE); + + /* Create a directory but force a journal error */ + if (!pc_mkdir(testtree[0])) return(FALSE); + if (fs_test_commit_failure(&test_fscontext,TRUE)) return(FALSE); + /* Automount should fail */ + if (pc_pwd(path, buffer)) return(FALSE); + if (get_errno() != PEFSRESTOREERROR) return(FALSE); + /* Now switch to autorevover mode, should work */ + FSDEBUG("RESTORE TEST: auto recover with bad Journal file") + if (!open_index_test(path, FS_MODE_AUTORECOVER|FS_MODE_AUTORESTORE, TEST_BLOCKMAPSIZE, 0)) return(FALSE); + /* Checkdisk and compare to the original check disk, should be the same */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(FALSE); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(FALSE); + + /* Create a directory force journal commit interruption */ + FSDEBUG("RESTORE TEST: test auto restore with good Journal file") + if (!pc_mkdir(testtree[0])) return(FALSE); + /* Checkdisk,compare to check disk with mkdir when we were journaling */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(FALSE); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(FALSE); + if (fs_test_commit_failure(&test_fscontext,FALSE)) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* Checkdisk and compare to the original check disk, should be the same */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(FALSE); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(FALSE); + if (!open_index_test(path, FS_MODE_AUTORESTORE, TEST_BLOCKMAPSIZE, 0)) return(FALSE); + /* Checkdisk,compare to check disk with mkdir when we were journaling */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(FALSE); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_rmdir(testtree[0])) return(FALSE); + return(TRUE); +} +#endif /* (DO_RESTORE_TEST) */ + + +#if (DO_API_TEST) + +/* API Calls to test: */ + +int fs_test_api_mkdir(byte *path); +int fs_test_api_mv(byte *path); +int fs_test_api_deltree(byte *path); +int fs_test_api_rmdir(byte *path); +int fs_test_api_open(byte *path); +int fs_test_api_unlink(byte *path); +int fs_test_api_change(byte *path,int test); + + +BOOLEAN fs_test_api_main(byte *path) +{ + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + fs_test_nvio_delete_fsfile(path); + FSDEBUG("API TEST") + if (!pc_set_default_drive(path)) + return(FALSE); + /* Make sure failsafe file exists */ + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* pc_mkdir */ + FSDEBUG("API TEST: pc_mkdir") + if (fs_test_api_mkdir(path)) return(FALSE); +/* pc_rmdir */ + FSDEBUG("API TEST: pc_rmdir") + if (fs_test_api_rmdir(path)) return(FALSE); +/* po_open */ + FSDEBUG("API TEST: po_open") + if (fs_test_api_open(path)) return(FALSE); +/* pc_unlink */ + FSDEBUG("API TEST: pc_unlink") + if (fs_test_api_unlink(path)) return(FALSE); +/* pc_mv */ + FSDEBUG("API TEST: pc_mv") + if (fs_test_api_mv(path)) return(FALSE); +/* pc_deltree */ + FSDEBUG("API TEST: pc_deltree") + if (fs_test_api_deltree(path)) return(FALSE); +/* pc_set_attributes() */ + FSDEBUG("API TEST: pc_set_attributes") + if (fs_test_api_change(path, 0)) return(FALSE); +/* po_extend_file() */ + FSDEBUG("API TEST: po_extend_file") + if (fs_test_api_change(path, 1)) return(FALSE); +/* po_chsize() */ + FSDEBUG("API TEST: po_chsize") + if (fs_test_api_change(path, 2)) return(FALSE); +/* po_truncate() */ + FSDEBUG("API TEST: po_truncate") + if (fs_test_api_change(path, 3)) return(FALSE); +/* po_write() */ + FSDEBUG("API TEST: po_write") + if (fs_test_api_change(path, 4)) return(FALSE); +/* po_close() */ + FSDEBUG("API TEST: po_close") + if (fs_test_api_change(path, 5)) return(FALSE); + FSDEBUG("API TEST: Success") + return(TRUE); +} + +int fs_test_api_mkdir(byte *path) +{ +CHKDISK_STATS chkstat[4]; + FSDEBUG("API TEST: test journalling") + if (!pc_check_disk(path, &chkstat[0], 0, 0, 0)) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_mkdir(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[1], 0, 0, 0)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* Failsafe was aborted. See that no disk changes were permanent */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(1); + + /* With no failsafe */ + if (!pc_mkdir(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + + /* Check auto commit mode */ + FSDEBUG("API TEST: test autocommit") + if (!pc_rmdir(testtree[0])) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER|FS_MODE_AUTOCOMMIT, TEST_BLOCKMAPSIZE, 0)) return(4); + if (!pc_mkdir(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + + /* Check manual commit mode */ + FSDEBUG("API TEST: test manual commit") + if (!pc_rmdir(testtree[0])) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_mkdir(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pro_failsafe_commit(path)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pc_rmdir(testtree[0])) return(1); + + /* Make sure journal full error check works */ + FSDEBUG("API TEST: test with full Journal") + if (!open_index_test_full(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (pc_mkdir(testtree[0])) return(1); + if (get_errno() != PEJOURNALFULL) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + return(0); +} + +int fs_test_api_rmdir(byte *path) +{ +CHKDISK_STATS chkstat[4]; + if (!pc_mkdir(testtree[0])) return(1); + + FSDEBUG("API TEST: test journalling") + if (!pc_check_disk(path, &chkstat[0], 0, 0, 0)) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_rmdir(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[1], 0, 0, 0)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* Failsafe was aborted. See that no disk changes were permanent */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(1); + + /* With no failsafe */ + if (!pc_rmdir(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pc_mkdir(testtree[0])) return(1); + + /* Check auto commit mode */ + FSDEBUG("API TEST: test autocommit") + if (!open_index_test(path, FS_MODE_AUTORECOVER|FS_MODE_AUTOCOMMIT, TEST_BLOCKMAPSIZE, 0)) return(4); + if (!pc_rmdir(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pc_mkdir(testtree[0])) return(1); + + /* Check manual commit mode */ + FSDEBUG("API TEST: test manual commit") + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_rmdir(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pro_failsafe_commit(path)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pc_mkdir(testtree[0])) return(1); + + /* Make sure journal full error check works */ + FSDEBUG("API TEST: test with full Journal") + if (!open_index_test_full(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (pc_rmdir(testtree[0])) return(1); + if (get_errno() != PEJOURNALFULL) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_rmdir(testtree[0])) return(1); + return(0); +} +static void fs_test_freefile(int fd) +{ +PC_FILE *pfile; +DROBJ *pobj; + pfile = prtfs_cfg->mem_file_pool+fd; + pobj = pfile->pobj; + pfile->is_free = TRUE; + if (pobj) + pc_freeobj(pobj); +} + +int fs_test_api_open(byte *path) +{ +int fd; +CHKDISK_STATS chkstat[4]; + if (!pc_mkdir(testtree[0])) return(1); + + FSDEBUG("API TEST: test journalling") + if (!pc_check_disk(path, &chkstat[0], 0, 0, 0)) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR|PO_CREAT|PO_EXCL),(word)(PS_IWRITE | PS_IREAD))) < 0) return(1); + fs_test_freefile(fd); + if (!pc_check_disk(path, &chkstat[1], 0, 0, 0)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* Failsafe was aborted. See that no disk changes were permanent */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(1); + + /* With no failsafe */ + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR|PO_CREAT|PO_EXCL),(word)(PS_IWRITE | PS_IREAD))) < 0) return(1); + fs_test_freefile(fd); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + + /* Check auto commit mode */ + FSDEBUG("API TEST: test autocommit") + if (!pc_unlink(apitestfile)) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER|FS_MODE_AUTOCOMMIT, TEST_BLOCKMAPSIZE, 0)) return(4); + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR|PO_CREAT|PO_EXCL),(word)(PS_IWRITE | PS_IREAD))) < 0) return(1); + fs_test_freefile(fd); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + + /* Check manual commit mode */ + FSDEBUG("API TEST: test manual commit") + if (!pc_unlink(apitestfile)) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR|PO_CREAT|PO_EXCL),(word)(PS_IWRITE | PS_IREAD))) < 0) return(1); + fs_test_freefile(fd); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pro_failsafe_commit(path)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pc_unlink(apitestfile)) return(1); + + /* Make sure journal full error check works */ + FSDEBUG("API TEST: test with full Journal") + if (!open_index_test_full(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR|PO_CREAT|PO_EXCL),(word)(PS_IWRITE | PS_IREAD))) >= 0) return(1); + if (get_errno() != PEJOURNALFULL) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + + if (!pc_rmdir(testtree[0])) return(1); + return(0); +} + +int fs_test_create_file(byte *fname,int fsize) +{ +int fd; + if ((fd = po_open(fname,(word)(PO_BINARY|PO_RDWR|PO_CREAT|PO_EXCL),(word)(PS_IWRITE | PS_IREAD))) < 0) + return(1); + if (fsize) + if (po_write(fd,&dummybuff[0], fsize) != fsize) return(1); + po_close(fd); + return(0); +} + +int fs_test_api_unlink(byte *path) +{ +CHKDISK_STATS chkstat[4]; + if (!pc_mkdir(testtree[0])) return(1); + if (fs_test_create_file(apitestfile,32*1024)) return(1); + + FSDEBUG("API TEST: test journalling") + if (!pc_check_disk(path, &chkstat[0], 0, 0, 0)) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_unlink(apitestfile)) return(1); + if (!pc_check_disk(path, &chkstat[1], 0, 0, 0)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* Failsafe was aborted. See that no disk changes were permanent */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(1); + + /* With no failsafe */ + if (!pc_unlink(apitestfile)) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (fs_test_create_file(apitestfile,32*1024)) return(1); + + /* Check auto commit mode */ + FSDEBUG("API TEST: test autocommit") + if (!open_index_test(path, FS_MODE_AUTORECOVER|FS_MODE_AUTOCOMMIT, TEST_BLOCKMAPSIZE, 0)) return(4); + if (!pc_unlink(apitestfile)) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (fs_test_create_file(apitestfile,32*1024)) return(1); + + /* Check manual commit mode */ + FSDEBUG("API TEST: test manual commit") + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_unlink(apitestfile)) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pro_failsafe_commit(path)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (fs_test_create_file(apitestfile,32*1024)) return(1); + + /* Make sure journal full error check works */ + FSDEBUG("API TEST: test with full Journal") + if (!open_index_test_full(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (pc_unlink(apitestfile)) return(1); + if (get_errno() != PEJOURNALFULL) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_unlink(apitestfile)) return(1); + if (!pc_rmdir(testtree[0])) return(1); + return(0); +} +BOOLEAN fs_test_check_mv(BOOLEAN shouldbemoved) +{ +ERTFS_STAT stat; + if (shouldbemoved) + { + if (pc_stat(apimovedfile, &stat) != -1) + if (pc_stat(apitestfile, &stat) == -1) + return(TRUE); + } + else + { + if (pc_stat(apitestfile, &stat) != -1) + if (pc_stat(apimovedfile, &stat) == -1) + return(TRUE); + } + return(FALSE); +} + +int fs_test_api_mv(byte *path) +{ + if (!pc_mkdir(testtree[0])) return(1); + if (fs_test_create_file(apitestfile,32*1024)) return(1); + + FSDEBUG("API TEST: test journalling") + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_mv(apitestfile, apimovedfile)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* Failsafe was aborted. See that no disk changes were permanent */ + if (!fs_test_check_mv(FALSE)) return(1); + + /* With no failsafe */ + if (!pc_mv(apitestfile, apimovedfile)) return(1); + if (!fs_test_check_mv(TRUE)) return(1); + if (!pc_mv(apimovedfile,apitestfile)) return(1); + if (!fs_test_check_mv(FALSE)) return(1); + + /* Check auto commit mode */ + FSDEBUG("API TEST: test autocommit") + if (!open_index_test(path, FS_MODE_AUTORECOVER|FS_MODE_AUTOCOMMIT, TEST_BLOCKMAPSIZE, 0)) return(4); + if (!pc_mv(apitestfile, apimovedfile)) return(1); + if (!fs_test_check_mv(TRUE)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!fs_test_check_mv(TRUE)) return(1); + if (!pc_mv(apimovedfile,apitestfile)) return(1); + if (!fs_test_check_mv(FALSE)) return(1); + + /* Check manual commit mode */ + FSDEBUG("API TEST: test manual commit") + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_mv(apitestfile, apimovedfile)) return(1); + if (!pro_failsafe_commit(path)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!fs_test_check_mv(TRUE)) return(1); + if (!pc_mv(apimovedfile,apitestfile)) return(1); + + /* Make sure journal full error check works */ + FSDEBUG("API TEST: test with full Journal") + if (!open_index_test_full(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (pc_mv(apitestfile, apimovedfile)) return(1); + if (get_errno() != PEJOURNALFULL) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + + if (!pc_unlink(apitestfile)) return(1); + if (!pc_rmdir(testtree[0])) return(1); + return(0); +} + +int fs_test_api_deltree(byte *path) +{ +CHKDISK_STATS chkstat[4]; + + FSDEBUG("API TEST: test journalling") + if (!fs_test_mktree(path)) return(1); + if (!pc_check_disk(path, &chkstat[0], 0, 0, 0)) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_deltree(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[1], 0, 0, 0)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* Failsafe was aborted. See that no disk changes were permanent */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[0],&chkstat[2])) return(1); + + /* With no failsafe */ + if (!pc_deltree(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!fs_test_mktree(path)) return(1); + + /* Check auto commit mode */ + FSDEBUG("API TEST: test autocommit") + if (!open_index_test(path, FS_MODE_AUTORECOVER|FS_MODE_AUTOCOMMIT, TEST_BLOCKMAPSIZE, 0)) return(4); + if (!pc_deltree(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!fs_test_mktree(path)) return(1); + + /* Check manual commit mode */ + FSDEBUG("API TEST: test manual commit") + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (!pc_deltree(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!pro_failsafe_commit(path)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[2], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[1],&chkstat[2])) return(1); + if (!fs_test_mktree(path)) return(1); + + /* Make sure journal full error check works */ + FSDEBUG("API TEST: test with full Journal") + if (!open_index_test_full(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (pc_deltree(testtree[0])) return(1); + if (get_errno() != PEJOURNALFULL) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_deltree(testtree[0])) return(1); + return(0); +} + + +int fs_test_change_size(int fd, int test) +{ + if (test == 1) + { + dword fileextend, new_size; + fileextend = (dword) 32*1024; + fileextend += 1023; + if (!po_extend_file(fd, fileextend, &new_size, 0, PC_FIRST_FIT) || + new_size != fileextend) + return(1); + } + else if (test == 2) + { + dword filesize; + filesize = (dword) 32*1024; + filesize = filesize * 2; + filesize += 1023; + if (po_chsize(fd, (long)filesize)) return (1); + } + else if (test == 3) + { + if (!po_truncate(fd, 0)) return(1); + } + else if (test == 4) + { + if (po_write(fd,&dummybuff[0], 32*1024) != 32*1024) return(1); + if (po_write(fd,&dummybuff[0], 32*1024) != 32*1024) return(1); + if (po_write(fd,&dummybuff[0], 1023) != 1023) return(1); + } + return(0); +} +int fs_test_check_size(int test, BOOLEAN changed) +{ +dword filesize; +ERTFS_STAT stat; + if (changed) + { + filesize = (dword) 32*1024; + filesize = filesize * 2; + filesize += 1023; + if (test == 3) + filesize = 0; + } + else + filesize = (dword) 32*1024; + if (pc_stat(apitestfile, &stat) != 0) return(1); + if (stat.st_size != filesize) return(1); + return(0); +} + +/* 0 pc_set_attributes() */ +/* 1 po_extend_file() */ +/* 2 po_chsize() */ +/* 3 po_truncate() */ +/* 4 po_write() */ +/* 5 po_close() */ +/* Use either pc_set_attributes or po_close to change attributes */ +BOOLEAN fs_set_attributes(int test, byte *pfilename, byte attribute) +{ +int fd; +PC_FILE *pfile; + if (test == 0) + { + if (!pc_set_attributes(pfilename, attribute)) return(FALSE); + } + else if (test == 5) + { + fd = po_open(pfilename,(word)(PO_BINARY|PO_RDWR),(word)(PS_IWRITE | PS_IREAD)); + if (fd < 0) + return(FALSE); + pfile = prtfs_cfg->mem_file_pool+fd; + pfile->pobj->finode->fattribute = attribute; + pfile->needs_flush = TRUE; + if (po_close(fd) != 0) + return(FALSE); + } + return(TRUE); +} + + +int fs_test_api_change(byte *path,int test) +{ +CHKDISK_STATS chkstat[4]; +byte attributes[2]; +int fd; +BOOLEAN attrib_test; + + if (test == 0 || test == 5) + attrib_test = TRUE; + else + attrib_test = FALSE; + + fd = -1; + FSDEBUG("API TEST: test journalling") + if (!pc_check_disk(path, &chkstat[0], 0, 0, 0)) return(1); + if (!pc_mkdir(testtree[0])) return(1); + if (fs_test_create_file(apitestfile,32*1024)) return(1); + if (attrib_test) + if (!pc_get_attributes(apitestfile, &attributes[0])) return(1); + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (attrib_test) + { + if (!fs_set_attributes(test, apitestfile, (byte)(attributes[0]|ASYSTEM))) return(1); + if (!pc_get_attributes(apitestfile, &attributes[1])) return(1); + if (attributes[1] != ((byte)(attributes[0]|ASYSTEM))) return(1); + } + else + { + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR),(word)(PS_IWRITE | PS_IREAD))) < 0) return(1); + if (fs_test_change_size(fd, test)) return(1); + if (fs_test_check_size(test,TRUE)) return(1); + po_close(fd); + } + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + /* Failsafe was aborted. See that no disk changes were permanent */ + if (attrib_test) + { + if (!pc_get_attributes(apitestfile, &attributes[1])) return(1); + if (attributes[1] != attributes[0]) return(1); + } + else + if (fs_test_check_size(test,FALSE)) return(1); + + /* With no failsafe */ + if (attrib_test) + { + if (!fs_set_attributes(test, apitestfile, (byte)(attributes[0]|ASYSTEM))) return(1); + if (!pc_get_attributes(apitestfile, &attributes[1])) return(1); + if (attributes[1] != ((byte)(attributes[0]|ASYSTEM) )) return(1); + if (!fs_set_attributes(test, apitestfile, attributes[0])) return(1); + } + else + { + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR),(word)(PS_IWRITE | PS_IREAD))) < 0) return(1); + if (fs_test_change_size(fd, test)) return(1); + if (fs_test_check_size(test,TRUE)) return(1); + po_close(fd); + if (!pc_unlink(apitestfile)) return(1); + if (fs_test_create_file(apitestfile,32*1024)) return(1); + } + /* Check auto commit mode */ + FSDEBUG("API TEST: test autocommit") + if (!open_index_test(path, FS_MODE_AUTORECOVER|FS_MODE_AUTOCOMMIT, TEST_BLOCKMAPSIZE, 0)) return(4); + if (attrib_test) + { + if (!fs_set_attributes(test, apitestfile, (byte)(attributes[0]|ASYSTEM))) return(1); + } + else + { + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR),(word)(PS_IWRITE | PS_IREAD))) < 0) return(1); + if (fs_test_change_size(fd, test)) return(1); + po_close(fd); + } + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (attrib_test) + { + if (!pc_get_attributes(apitestfile, &attributes[1])) return(1); + if (attributes[1] != (byte)(attributes[0]|ASYSTEM)) return(1); + } + else if (fs_test_check_size(test,TRUE)) return(1); + if (!pc_unlink(apitestfile)) return(1); + if (fs_test_create_file(apitestfile,32*1024)) return(1); + + /* Check manual commit mode */ + FSDEBUG("API TEST: test manual commit") + if (!open_index_test(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (attrib_test) + { + if (!fs_set_attributes(test, apitestfile, (byte)(attributes[0]|ASYSTEM))) return(1); + } + else + { + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR),(word)(PS_IWRITE | PS_IREAD))) < 0) return(1); + if (fs_test_change_size(fd, test)) return(1); + po_close(fd); + } + if (!pro_failsafe_commit(path)) return(1); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (attrib_test) + { + if (!pc_get_attributes(apitestfile, &attributes[1])) return(1); + if (attributes[1] != (byte)(attributes[0]|ASYSTEM)) return(1); + if (!fs_set_attributes(test, apitestfile, attributes[0])) return(1); + } + else if (fs_test_check_size(test,TRUE)) return(1); + + /* Make sure journal full error check works */ + FSDEBUG("API TEST: test with full Journal") + if (!pc_unlink(apitestfile)) return(1); + if (fs_test_create_file(apitestfile,32*1024)) return(1); + if (!open_index_test_full(path, FS_MODE_AUTORECOVER, TEST_BLOCKMAPSIZE, 0)) return(1); + if (attrib_test) + { + if (fs_set_attributes(test, apitestfile, (byte)(attributes[0]|ASYSTEM))) return(1); + } + else + { + if ((fd = po_open(apitestfile,(word)(PO_BINARY|PO_RDWR),(word)(PS_IWRITE | PS_IREAD))) < 0) return(1); + if (fs_test_change_size(fd, test) == 0) return(1); + } + if (get_errno() != PEJOURNALFULL) return(1); + if (fd >= 0) + po_close(fd); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + + if (!pc_unlink(apitestfile)) return(1); + if (!pc_rmdir(testtree[0])) return(1); + if (!pc_check_disk(path, &chkstat[1], 0, 0, 0)) return(1); + if (!fs_test_chkstats(&chkstat[0],&chkstat[1])) return(1); + return(0); +} + + +#endif /* (DO_API_TEST) */ +#if (DO_FSAPI_TEST) + +BOOLEAN fs_test_fsapi_failsafe_init(byte *path); +BOOLEAN fs_test_fsapi_failsafe_commit(byte *path); +BOOLEAN fs_test_fsapi_failsafe_restore(byte *path); + +BOOLEAN fs_test_fsapi_main(byte *path) +{ + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + fs_test_nvio_delete_fsfile(path); + if (!pc_set_default_drive(path)) + return(FALSE); + FSDEBUG("FSAPI TEST: pro_failsafe_init") + if (!fs_test_fsapi_failsafe_init(path)) + { + FSDEBUG("FSAPI TEST: pro_failsafe_init Failed") + return(FALSE); + } + FSDEBUG("FSAPI TEST: pro_failsafe_init Success") + FSDEBUG("FSAPI TEST: pro_failsafe_commit") + if (!fs_test_fsapi_failsafe_commit(path)) + { + FSDEBUG("FSAPI TEST: pro_failsafe_commit Failed") + return(FALSE); + } + FSDEBUG("FSAPI TEST: pro_failsafe_commit Success") + FSDEBUG("FSAPI TEST: pro_failsafe_restore") + if (!fs_test_fsapi_failsafe_restore(path)) + { + FSDEBUG("FSAPI TEST: pro_failsafe_restore Failed") + return(FALSE); + } + FSDEBUG("FSAPI TEST: pro_failsafe_restore Success") + + + return(TRUE); +} + +BOOLEAN fs_test_fsapi_failsafe_init(byte *path) +{ +byte buffer[32]; +dword ltemp,default_size; +CHKDISK_STATS chkstat[4]; +struct fsblockmap *pbm; +int i; + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + fs_test_nvio_delete_fsfile(path); + + FSDEBUG("FSAPI TEST: pro_failsafe_init test mount behavior") + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (test_fscontext.pdrive->mount_valid) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* Mount the disk */ + if (pro_failsafe_init(path, &test_fscontext)) return(FALSE); /*re-init */ + if (get_errno() != PEFSREINIT) return(FALSE); + + FSDEBUG("FSAPI TEST: pro_failsafe_init test bad inputs") + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + /* Test bad flags */ + test_fscontext.configuration_flags = + (FS_MODE_AUTORESTORE|FS_MODE_AUTORECOVER|FS_MODE_AUTOCOMMIT); + test_fscontext.configuration_flags = ~test_fscontext.configuration_flags; + if (pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (get_errno() != PEINVALIDPARMS) return(FALSE); + /* Test blockmap cache arguments */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.blockmap_size = 1000; + test_fscontext.blockmap_freelist = 0; + if (pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (get_errno() != PEINVALIDPARMS) + return(FALSE); + /* Test user file size arguments */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.user_journal_size = 1; /* Too small */ + if (pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (get_errno() != PEINVALIDPARMS) + return(FALSE); + FSDEBUG("FSAPI TEST: pro_failsafe_init user_journal_size > default") + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.user_journal_size = 0; /* Default */ + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* Mount the disk */ + default_size = test_fscontext.num_index_blocks+test_fscontext.num_remap_blocks; + ltemp = default_size + + (dword)(test_fscontext.pdrive->secpalloc*2); /* 2 clusters bigger*/ + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[0], 0, 0, 0)) return(FALSE); /* Remember disk info */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.user_journal_size = ltemp; + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* Mount the disk */ + ltemp = test_fscontext.num_index_blocks+test_fscontext.num_remap_blocks; + if (ltemp != test_fscontext.user_journal_size) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[1], 0, 0, 0)) return(FALSE); + if ((chkstat[1].n_hidden_clusters-chkstat[0].n_hidden_clusters)!=2) + return(FALSE); + if ((chkstat[0].n_free_clusters-chkstat[1].n_free_clusters)!=2) + return(FALSE); + FSDEBUG("FSAPI TEST: pro_failsafe_init user_journal_size < default") + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + ltemp = default_size - (dword)(test_fscontext.pdrive->secpalloc*2); /* 2 clusters smaller*/ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.user_journal_size = ltemp; + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* Mount the disk */ + ltemp = test_fscontext.num_index_blocks+test_fscontext.num_remap_blocks; + if (ltemp != test_fscontext.user_journal_size) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_check_disk(path, &chkstat[1], 0, 0, 0)) return(FALSE); + if ((chkstat[0].n_hidden_clusters-chkstat[1].n_hidden_clusters)!=2) + return(FALSE); + if ((chkstat[1].n_free_clusters-chkstat[0].n_free_clusters)!=2) + return(FALSE); + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + FSDEBUG("FSAPI TEST: pro_failsafe_init test block map arguments") + rtfs_memset((void *) &test_failsafe_blockmap_array[0], 0, sizeof(&test_failsafe_blockmap_array)); + test_fscontext.blockmap_size = TEST_BLOCKMAPSIZE; + test_fscontext.blockmap_freelist = &test_failsafe_blockmap_array[0]; + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + pbm = test_fscontext.blockmap_freelist; + for (i = 0; i < TEST_BLOCKMAPSIZE; i++) + { if (!pbm) return(FALSE); pbm = pbm->pnext;} + if (pbm) return(FALSE); + + FSDEBUG("FSAPI TEST: pro_failsafe_init test FS_MODE_AUTOCOMMIT") + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.configuration_flags = FS_MODE_AUTOCOMMIT; + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_mkdir(testtree[0])) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_rmdir(testtree[0])) return(FALSE); + + FSDEBUG("FSAPI TEST: pro_failsafe_init test FS_MODE_AUTORESTORE") + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_mkdir(testtree[0])) return(FALSE); + if (fs_test_commit_failure(&test_fscontext,FALSE)) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (pc_pwd(path, buffer)) return(FALSE); /* re-mount disk should fail */ + if (get_errno() != PEFSRESTORENEEDED) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (pc_rmdir(testtree[0])) return(FALSE); /* Shouldn't be there */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.configuration_flags = FS_MODE_AUTORESTORE; + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* re-mount disk and autorestore */ + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_rmdir(testtree[0])) return(FALSE); /* Should now be there */ + + FSDEBUG("FSAPI TEST: pro_failsafe_init test FS_MODE_AUTORECOVER") + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_mkdir(testtree[0])) return(FALSE); + if (fs_test_commit_failure(&test_fscontext,TRUE)) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (pc_pwd(path, buffer)) return(FALSE); /* re-mount disk should fail */ + if (get_errno() != PEFSRESTOREERROR) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (pc_rmdir(testtree[0])) return(FALSE); /* Shouldn't be there */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (pc_pwd(path, buffer)) return(FALSE); /* re-mount disk should fail */ + if (get_errno() != PEFSRESTOREERROR) return(FALSE); + + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.configuration_flags = FS_MODE_AUTORESTORE; + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (pc_pwd(path, buffer)) return(FALSE); /* re-mount disk should fail */ + if (get_errno() != PEFSRESTOREERROR) return(FALSE); + + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.configuration_flags = FS_MODE_AUTORESTORE|FS_MODE_AUTORECOVER; + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* re-mount disk should work */ + return(TRUE); +} + +BOOLEAN fs_test_fsapi_failsafe_commit(byte *path) +{ +byte buffer[32]; +dword ltemp; + + FSDEBUG("FSAPI TEST: pro_failsafe_commit test journal IO error handling") + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* re-mount disk */ + ltemp = test_fscontext.nv_buffer_handle; /* Save */ + test_fscontext.nv_buffer_handle = 0; /* Force errors during journal */ + if (pc_mkdir(testtree[0])) return(FALSE); + if ( (get_errno() != PEIOERRORWRITEJOURNAL) || + (get_errno() != PEIOERRORWRITEJOURNAL)) + return(FALSE); + test_fscontext.nv_buffer_handle = ltemp; /* Undo error and try again */ + if (!pc_mkdir(testtree[0])) return(FALSE); + test_fscontext.nv_buffer_handle = 0; /* Force errors during commit */ + if (pro_failsafe_commit(path)) return(FALSE); + if ( (get_errno() != PEIOERRORWRITEJOURNAL) || + (get_errno() != PEIOERRORWRITEJOURNAL)) + return(FALSE); + test_fscontext.nv_buffer_handle = ltemp; /* Undo error and try again */ + if (!pro_failsafe_commit(path)) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_rmdir(testtree[0])) return(FALSE); /* Should be on disk */ + + FSDEBUG("FSAPI TEST: pro_failsafe_commit test error handling") + + if (pro_failsafe_commit(path)) return(FALSE); /* Commit without init */ + if (get_errno() != PENOINIT) return(FALSE); + + FSDEBUG("FSAPI TEST: pro_failsafe_commit test commit process") + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* re-mount disk */ + if (!pc_mkdir(testtree[0])) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (pc_rmdir(testtree[0])) return(FALSE); /* should not be there */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* re-mount disk */ + if (!pc_mkdir(testtree[0])) return(FALSE); + if (!pro_failsafe_commit(path)) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_rmdir(testtree[0])) return(FALSE); /* should be there */ + return(TRUE); +} + +BOOLEAN fs_test_fsapi_failsafe_restore(byte *path) +{ +byte buffer[32]; +int rval; + + + FSDEBUG("FSAPI TEST: pro_failsafe_restore test normal restore") + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_mkdir(testtree[0])) return(FALSE); + if (fs_test_commit_failure(&test_fscontext,FALSE)) return(FALSE); + rval = pro_failsafe_restore(path,0,TRUE,FALSE); + if (rval != FS_STATUS_RESTORED) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + if (!pc_rmdir(testtree[0])) return(FALSE); + + FSDEBUG("FSAPI TEST: pro_failsafe_restore test journal data error handling") + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* re-mount disk */ + if (!pc_mkdir(testtree[0])) return(FALSE); + if (fs_test_commit_failure(&test_fscontext,TRUE)) return(FALSE); + rval = pro_failsafe_restore(path,0,TRUE,FALSE); + if (rval != FS_STATUS_BAD_CHECKSUM) return(FALSE); + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + + FSDEBUG("FSAPI TEST: pro_failsafe_restore test clear function") + rval = pro_failsafe_restore(path,&test_fscontext,FALSE,TRUE); + if (rval != FS_STATUS_OK) return(FALSE); + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + if (!pro_failsafe_init(path, &test_fscontext)) return(FALSE); + if (!pc_pwd(path, buffer)) return(FALSE); /* re-mount disk */ + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + return(TRUE); +} +#endif /* (DO_FSAPI_TEST) */ + +BOOLEAN fs_test_chkstats(CHKDISK_STATS *p1,CHKDISK_STATS *p2) +{ + if (p1->n_user_files != p2->n_user_files) + return(FALSE); + if (p1->n_hidden_files != p2->n_hidden_files) + return(FALSE); + if (p1->n_user_directories != p2->n_user_directories) + return(FALSE); + if (p1->n_free_clusters != p2->n_free_clusters) + return(FALSE); + if (p1->n_bad_clusters != p2->n_bad_clusters) + return(FALSE); + if (p1->n_file_clusters != p2->n_file_clusters) + return(FALSE); + if (p1->n_hidden_clusters != p2->n_hidden_clusters) + return(FALSE); + if (p1->n_dir_clusters != p2->n_dir_clusters) + return(FALSE); + if (p1->n_crossed_points != p2->n_crossed_points) + return(FALSE); + if (p1->n_lost_chains != p2->n_lost_chains) + return(FALSE); + if (p1->n_lost_clusters != p2->n_lost_clusters) + return(FALSE); + if (p1->n_bad_lfns != p2->n_bad_lfns) + return(FALSE); + return(TRUE); +} +void fs_test_make_filename(byte *buffer, byte *path, byte *filename) +{ + rtfs_cs_strcpy(buffer, path); + rtfs_cs_strcat(buffer, filename); +} + +/* Create a contiguous file of nblocks */ +dword fs_test_fill_file(byte *path, byte *filename, int nblocks) +{ +byte fullname[64]; +int fd; +dword needed_size, returned_size; +FILESEGINFO seginfo; + + fs_test_make_filename(fullname, path, filename); + needed_size = (dword) nblocks*512; + if ((fd = po_open(fullname,(word)(PO_BINARY|PO_RDWR|PO_CREAT|PO_TRUNC),(word)(PS_IWRITE | PS_IREAD))) >= 0) + { + if (!po_extend_file(fd, needed_size, &returned_size, 0, PC_FIRST_FIT) || + returned_size != needed_size) + { +return_error: + po_close(fd); + fs_test_rm_file(path, filename); + return(0); + } + if (pc_get_file_extents(fd, 1, &seginfo, FALSE) != 1) + goto return_error; + po_close(fd); + return(seginfo.block); + } + return(0); +} + +/* Create a contiguous file of nblocks */ +BOOLEAN fs_test_fill_disk(byte *path, byte *filename) +{ +byte fullname[64]; +int fd; +int needed_size,returned_size; + + needed_size = pc_cluster_size(path); + if (!needed_size) + return(FALSE); + fs_test_make_filename(fullname, path, filename); + if ((fd = po_open(fullname,(word)(PO_BINARY|PO_RDWR|PO_CREAT|PO_TRUNC),(word)(PS_IWRITE | PS_IREAD))) >= 0) + { + do + { + returned_size = po_write(fd, 0, needed_size); + } while (returned_size == needed_size); + po_close(fd); + if (returned_size == -1) + return(FALSE); + else + return(TRUE); + } + return(FALSE); +} + +BOOLEAN fs_test_rm_file(byte *path, byte *filename) +{ +byte fullname[64]; + + fs_test_make_filename(fullname, path, filename); + if (!pc_unlink(fullname)) + return(FALSE); + return(TRUE); +} + +BOOLEAN fs_test_mktree(byte *path) +{ +int i,j,k,fd; +byte dnamebuffer[256]; +byte fnamebuffer[256]; +ERTFS_STAT stat; +int filesize; + + if (pc_stat(testtree[0], &stat) != -1) + if (!pc_deltree(testtree[0])) + return(FALSE); + filesize = pc_cluster_size(path) * 4; + + for (i = 0; testtree[i]; i++) + { + fs_test_make_filename(dnamebuffer, path, testtree[i]); + if (!pc_mkdir(dnamebuffer)) + return(FALSE); + for (j = 0; testfiles[j]; j++) + { + fs_test_make_filename(fnamebuffer,dnamebuffer, testfiles[j]); + if ((fd = po_open(fnamebuffer,(word)(PO_BINARY|PO_RDWR|PO_CREAT|PO_TRUNC),(word)(PS_IWRITE | PS_IREAD))) >= 0) + { + if (filesize) + { + k = filesize; + /* Write non block alligned bytes */ + if (j == 0) + { + while (k < filesize) + { + if (po_write(fd,dummybuff,100) != 100) + goto return_error; + k += 100; + } + } + else if (j == 1) + { if (po_write(fd,&dummybuff[0],filesize) != filesize) + goto return_error; + } + else if (po_write(fd,0,filesize) != filesize) /* Extend by writing with NULL */ + { +return_error: + po_close(fd); + return(FALSE); + } + po_close(fd); + } + } + else + return(FALSE); + } + } + return(TRUE); +} + +BOOLEAN open_index_test_full(byte *path, int flags, int block_map_size, dword user_journal_size) +{ + if (!open_index_test(path, flags, block_map_size, user_journal_size)) + return(FALSE); + test_fscontext.total_blocks_mapped = + test_fscontext.num_remap_blocks; + return(TRUE); +} + + +BOOLEAN open_index_test(byte *path, int flags, int block_map_size, dword user_journal_size) +{ +byte buffer[32]; + pro_failsafe_shutdown(path, TRUE); /* Shut off failsafe */ + rtfs_memset((void *) &test_fscontext, 0, sizeof(test_fscontext)); + test_fscontext.configuration_flags = flags; + test_fscontext.blockmap_size = block_map_size; + test_fscontext.user_journal_size = user_journal_size; + if (block_map_size) + test_fscontext.blockmap_freelist = &test_failsafe_blockmap_array[0]; + if (!pro_failsafe_init(path, &test_fscontext)) + return(FALSE); + if (!pc_pwd(path, buffer)) /* Mount the disk */ + { + return(FALSE); + } + return(TRUE); +} +BOOLEAN fs_test_nvio_delete_fsfile(byte *path) +{ +byte filename[64]; +byte attrib; + fs_test_make_filename(filename, path,rtfs_strtab_user_string(USTRING_SYS_FSFILENAME)); + if (!pc_get_attributes(filename, &attrib)) + { + if (get_errno() == PENOENT) + return(TRUE); + else + return(FALSE); + } + if (!pc_set_attributes(filename, 0)) + return(FALSE); + if (!pc_unlink(filename)) + return(FALSE); + return(TRUE); +} + +int fs_test_commit_failure(FAILSAFECONTEXT *pfscntxt,BOOLEAN force_checksum_error) +{ +int ret_val; + + + if (!pfscntxt->total_blocks_mapped) + return(0); + if (force_checksum_error) + pfscntxt->journal_checksum -= 1; + /* update index information to disk */ + if (pro_failsafe_flush_header(pfscntxt, FS_STATUS_PROCESSING)) + { + ret_val = -1; + goto ex_it; + } + ret_val = 0; + pc_dskfree(pfscntxt->pdrive->driveno); +ex_it: + return (ret_val); +} + + +#endif /* INCLUDE_FAILSAFE_CODE */ diff --git a/build/libraries/fatfs/ARM7/readme.txt b/build/libraries/fatfs/ARM7/readme.txt new file mode 100644 index 0000000..585d3af --- /dev/null +++ b/build/libraries/fatfs/ARM7/readme.txt @@ -0,0 +1,14 @@ + +以下は追加したファイル。 +RTFS製品には含まれていない。 +(RTFSをバージョンアップ等で差し替える際に注意) + +attach.c +attach.h + +drdefault.c +drdefault.h + +drfile.c +drfile.h + diff --git a/build/libraries/fatfs/ARM7/revhst40.txt b/build/libraries/fatfs/ARM7/revhst40.txt new file mode 100644 index 0000000..3f47bd8 --- /dev/null +++ b/build/libraries/fatfs/ARM7/revhst40.txt @@ -0,0 +1,2063 @@ +Functional changes between version 4.4x versus version 4.4ws (August 25, 2006) +================================================================= +winhdisk,c - Removed some unreferenced performance diagnostic code that was + inadverdantly placed in the file. +appcmdsh.c - Modified optional macro DISPLAY_ERRNO() to use RTFS print + routines instead of printf. + Fixed bug in app_failsafe_init() that was inadvertantly + reporting failure when failsafe initialization had actually + succeeded. +prfstest.c - Modified optional macro FSDEBUG() to use RTFS print + routines instead of printf. + Fixed places where po_extend_file() was being called using + incorrect parameters. This error was introduced in version + 44V and was not caught until now. +prfs.h - Added CFG_VALIDATE_JOURNAL option value. The description of this + option is: + CFG_VALIDATE_JOURNAL - Set this to one to force a a low level read of the + whole journal file before it is accessed. If any of the reads call Failsafe + tries to recover by re-writing the whole file with zeros, for some media + types this will correct a read error. When the overwrite is completed + the file is read again, if that succeeds we continue, otherwise the journal + file open fails. A disk mount that encounters this error will fail and + errno will be set to PEIOERRORREADJOURNAL + +prfs.h - Added CFG_VALIDATE_JOURNAL option value. The description of this + option is: + CFG_VALIDATE_BUFFER_SIZE - This is the size, in blocks of the special + buffer dedicated to performing the tasks described in CFG_VALIDATE_JOURNAL. + The largest usable value for CFG_VALIDATE_BUFFER_SIZE is 128. Larger + values will not be utilized but memory will be wasted. + The whole journal file is scanned during startup in disk reads of up to + CFG_VALIDATE_BUFFER_SIZE blocks. Larger values will require fewer reads + and improve perfromance over smaller values. + The buffer is declared as: + static byte validate_buffer[CFG_VALIDATE_BUFFER_SIZE*512]; + in prfsnvio.c. + If CFG_VALIDATE_JOURNAL is zero the value of CFG_VALIDATE_BUFFER_SIZE + is irrelevant because the buffer is not declared. +prfs.h - Added an additional internal field "open_status" to the failsafe context + that is used to differentiate between IO errors and other errors + during journal file re-opens + +prfsapi.c - Remove one unreferenced label "ex_it" from the routine named + pro_failsafe_commit. There were no functional changes. + +prfscore.c- Modified the routine failsafe_restore_internal() to return + FS_STATUS_IO_ERROR if failsafe_reopen_nv_buffer() reported + an IO error (while validating the journal file) + +prfscore.c- Modified the routine pro_failsafe_autorestore() to fail and set + errno to PEIOERRORREADJOURNAL if failsafe_restore_internal() + returns FS_STATUS_IO_ERROR. This action is taken regardless of + whether auto-recover is enabled or not because it indicates an + unrecoverable media failure. + +prfsnvio.c- Added code to implement the algorithm that is enabled when + CFG_VALIDATE_JOURNAL is enabled. + +Functional changes between version 4.4ws versus version 4.4w (August 11, 2006) +================================================================= +rtfs.h - Added DRIVE_FLAGS_FILEIO Flag +apiwrite.c - Set DRIVE_FLAGS_FILEIO before writing file data in po_write +apifilio.c - Set DRIVE_FLAGS_FILEIO before read file data in po_read +apifilio.c - Set DRIVE_FLAGS_FILEIO before write file data in pc_flush_file_buffer + and restore previous setting +apifilio.c - Set DRIVE_FLAGS_FILEIO before read file data in pc_load_file_buffer + and restore previous setting +rtfat16.c - Modified pc_init_drv_fat_info16() to correctly distinguish between + FAT12 and FAT16 when clipping maxfindex to the number of sectors + reserved for clusters.. +rtnvfat.c - Modified pc_parsepath()to skip leading spaces and + to replace trailing spaces NULLs + +================================================================= +Functional changes between version 4.4w versus version 4.4v (June 1, 2006) +================================================================= +rtfs.h - Added po_extend_file() -prototype which was inadvertantly removed in + version 44v +Apirealt.c po_extend_file() - Fixed a bug that was calculating the number of + clusters to the end of the current file incorrectly under certain + circumstances. +Changed: line 222 from: + clusters_in_chain = + pc_chain_length(pdr, pfile->pobj->finode->fsize)- + pc_chain_length(pdr, pfile->fptr); +To: + { + dword ltemp,fptr_mod_cluster; + /* Round fptr down to cluster boundary */ + fptr_mod_cluster = pfile->fptr & ~(pdr->byte_into_cl_mask); + /* Subtract from the rounded up alloced_size value */ + ltemp = alloced_size-fptr_mod_cluster; + clusters_in_chain = ltemp >> (int) (pfile->pobj->pdrive->log2_secpalloc+9); + } + +Apiinit.c - Changed the HostDisk file's base name, it was incorrectly set + to "E:\\demotest\\Hostdisk". It is now just Hostdisk. + +================================================================= +Functional changes between version 4.4v versus version 4.4u (April 1, 2006) +================================================================= + +rtfsconf.h - Added two new configuration parameters + +RTFS_MAX_FILE_SIZE - Default 0xffffffff + Set to the maximum file size ERTFS may create. If po_chsize or po_extend_file() + are called with a size request larger than this they fail and set errno + to PETOOLARGE. When po_write() is asked to expend the file beyond this maximum + the behavior is determined by the value of RTFS_TRUNCATE_WRITE_TO_MAX */ + +RTFS_TRUNCATE_WRITE_TO_MAX - Default 1 + Set to 1 to force RTFS to truncate po_write() requests to fit within + RTFS_MAX_FILE_SIZE. If RTFS_TRUNCATE_WRITE_TO_MAX is set to 0, po_write + requests that attempt to extend the file beyond RTFS_TRUNCATE_WRITE_TO_MAX + Fail and set errno to PETOOLARGE. If RTFS_TRUNCATE_WRITE_TO_MAX is set to + 1, po_write requests that attempt to extend the file beyond + RTFS_MAX_FILE_SIZE are truncated to fill the file until its + size reaches RTFS_MAX_FILE_SIZE bytes. + +rtfs.h - Added PETOOLARGE errno value. Set when and error occurs because + trying to extend a file beyond RTFS_MAX_FILE_SIZE + +apiregrs.c - Added do_large_file_test() to test api calls on large + files (near 4 Gbyte) +apifilio.c - po_read() Truncate read operation if it wraps 4 Gbyte +apifilio.c - po_read() If passed NULL data pinter, read file without data + transfer +apifilio.c - created new API call po_ulseek() to seek across 4 Gbyte range +apifilio.c - created _po_ulseek() internal seek function with 4 Gbyte range +apifilio - change _po_lseek()internal seek to utilize _po_ulseek() + +Apirealt.c po_extend_file() Changed API calling sequence and added support + for unsigned 32 bit offsets. +Apirealt.c po_extend_file() - Set error condition if new size + is greater than or equal to RTFS_MAX_FILE_SIZE +Apirealt.c po_chsize() changed offset to unsigned 32 bit. +Apirealt.c po_chsize() - Set error condition if new size + is greater than or equal to RTFS_MAX_FILE_SIZE +apiwrite.c - po_write() Truncate write operation if it wraps 4 Gbyte +apiwrite.c - po_write() if write will make file > RTFS_MAX_FILE_SIZE then + truncate the write request if RTFS_TRUNCATE_WRITE_TO_MAX is + set to 1. Otherwise fail with with PETOOLARGE errno value. +apiwrite.c - po_write() Use _pc_ulseek() if opened in append mode +apiwrite.c - Changed po_truncate() to use unsinged 32 bit offset +apiwrite.c - po_write() If passed NULL data pointer, write file without data + transfer +apiwrite.c - po_write() - Change to set file time and archive bit whenever + file is written to, not just when it is extended. + +winhdsk.c - Updated to RTFS Pro Plus version, supports multigigabyte disks +and uses unbuffered win32 file io to better emulate a real disk. + +Functional changes between version 4.4u versus version 4.4t (Jan 25, 2006) +================================================================= +Bug Fixes: +1. A change was made to open_failsafe_file() (prfsnvio.c) to fix a +bug in the code that allocated the contigous cluster chain for the +Journal file. It was a minor problem that caused Failsafe to skip +potentially available regions of space for the Journal file. + + if (contig_clusters < n_clusters) + { + FATOP(pdrive)->fatop_freechain(pdrive, first_cluster, 0, 0xffffffff); + first_cluster = first_cluster+contig_clusters; + pdrive->free_contig_pointer = first_cluster+contig_clusters; + } + + Was changed to + + if (contig_clusters < n_clusters) + { + FATOP(pdrive)->fatop_freechain(pdrive, first_cluster, 0, 0xffffffff); + pdrive->free_contig_pointer = first_cluster+contig_clusters; + } + +Functional changes between version 4.4T versus version 4.4S (Jan 9, 2006) +================================================================= +Bug Fixes: + + +1. A change was made to open_failsafe_file() (prfsnvio.c) to fix a +bug in the code the allocated the contigous cluster chain for the +Journal file. + +The bug is described as follows: + +In pseudo-code representation the algorithm worked as follows: + + pdrive->free_contig_pointer = pdrive->free_contig_base; + first_cluster = 0; +try_again: + contig_clusters = + FATOP(pobj->pdrive)->fatop_alloc_chain(pobj->pdrive, + &first_cluster, n_clusters, FALSE); + if (contig_clusters != n_cluster) + { + free first_cluster to first_cluster+contig_clusters + first_cluster += n_clusters; + goto try_again; + } + +The algorithm relies on the fact that fatop_alloc_chain() starts looking +for free clusters starting from the initial value passed in first_cluster. +If a cluster chain less than n_clusters was found this algorithm +appropriately advanced the start of the allocation region by +n_clusters and tried to the allocation allocate again, repeating +the process until n_clusters contigous cluster was allocated. + +There was a BUG in the algorithm however: + +A side effect of fatop_alloc_chain() is to link the cluster at +first_cluster (if it is non-zero) to the newly allocated contiguous +clusters. So after the first pass the algorithm was linking first_cluster +to the new chain, causing a lost cluster if first_cluster was not part of +a chain or causing crossed cluster chains if it was. + + +In pseudo-code representation the fixed algorithm works as follows: + + pdrive->free_contig_pointer = pdrive->free_contig_base; +try_again: + first_cluster = 0; + contig_clusters = + FATOP(pobj->pdrive)->fatop_alloc_chain(pobj->pdrive, + &first_cluster, n_clusters, FALSE); + if (contig_clusters != n_cluster) + { + free first_cluster to first_cluster+contig_clusters + pdrive->free_contig_pointer += n_clusters; + goto try_again; + } + +The algorithm relies on the fact that fatop_alloc_chain() starts looking +for free clusters starting from pdrive->free_contig_pointer if the initial + value passed in first_cluster is zero. If a cluster chain less than + n_clusters is found the algorithm appropriately advanced the start of the + allocation region (as determined by pdrive->free_contig_pointer) by +n_clusters and tries to the allocation allocate again, repeating +the process until n_clusters contigous cluster is allocated. Since +first_cluster is always passed as zero the bug in the previous +implementation is eliminated. + + + + + + + + + +when end of file is reached on a read. A problem was discovered that +when a read to end of file was done with one open handle to the file +and the file was extend by writing to it from another open handle, +the next read call re-read the data at the old end of filem, not the +data that was just written to the file. + +================================================================= +Functional changes between version 4.4S versus version 4.4R (OCt 25, 2005) + +Bug Fixes: + +1. A change was made to po_read() (apifilio.c) to set the eof flag +when end of file is reached on a read. A problem was discovered that +when a read to end of file was done with one open handle to the file +and the file was extend by writing to it from another open handle, +the next read call re-read the data at the old end of filem, not the +data that was just written to the file. + +================================================================= +Functional changes between version 4.4R versus version 4.4Q (OCt 19, 2005) + +Bug Fixes: + +1. Several compile warnings were fixed. This includes adding some +#include lines in winsplsh.c and portkern.c. + +2. A bug was discovered with the partition recognition code. ERTFS now +supports Windows extended partitions in addition to supporting DOS +extended partitions. This required a couple fixes in rtlowl.c. + +3. A couple bugs were discovered with the FAT32 formatting code. The +binary volume label is now written and a value of 3 is written to the +next free cluster hint. The previous value of 2 was less correct and +caused MacOS to complain. These fixes affect rtfat32.c. + +4. A bug was discovered that prevented formatting without +partitions due to the prior READ_PARTITION_NO_TABLE fixes. Fixing this +required a change in apifrmat.c. + +5. A bug was discovered with the CHS normalization. Hard drives use a +different system for cylinder/head/sector numbers than the DOS file +system, so a conversion needs to happen. This conversion now supports +a greater number of hard drive sizes. These fixes affect apifrmat.c. + +6. A bug was discovered where ERTFS could calculate the maximum cluster +index value incorrectly in some rare format dependant cases. This resulted +in a mount failure durint the initial cluster scan. Both rtfat32.c and +rtfat16.c were modified to test the calculated max index against the +number of sectors dedicated to fat blocks (secpfat) and truncate the +maximum index. + +7. A bug was fixed in drideata.c. If the user data buffer is on an odd +address buffer ide_insw() and ide_outsw() now double buffer the data in +512 byte segments. This is needed because some architectures can not do +word transfers to odd alligned addresses + +================================================================= +Functional changes between version 4.4Q versus version 4.4P (Dec 27, 2004) + +Bug Fixes: + +1. A bug was discovered with our fix for open_failsafe_file() in 4.4P. +To fix the problem, open_failsafe_file() was modified to free an allocated +cluster chain before exiting a certain loop. + +================================================================= +Functional changes between version 4.4P versus version 4.4O (Dec 22, 2004) + +Bug Fixes: + +1. A bug was discovered in failsafe in which infinite loops could occur +while creating the failsafe file if disk space was low or fragmented. +To fix the problem, open_failsafe_file() was modified to start searching +from the beginning of the FAT if it hit the end, to catch that fact, and +then stop the search. + +2. A bug was discovered in failsafe which caused false positives using the +FS_STATUS_OUT_OF_DATE feature. To fix the problem, the task of copying +sectors was split into several functions, copying the non-FAT blocks +first, the FAT blocks second, and the FSINFO sector last. + +3. A bug was discovered with the FAT formatting code which caused some +third party utilities to not recognized drives formatted with ERTFS. +To fix the problem, pc_mkfs16() and pc_mkfs32() were modified to write +a required signature at the end of the first disk sector. Because this +signature was previously used in ERTFS as a flag for whether the drive +was partitioned, pc_read_partition_table() had to be changed. To fix +this, all lines where the function returned READ_PARTITION_NO_ENTRY were +changed to return READ_PARTITION_NO_TABLE instead. + +4. A bug was discovered with the FAT formatting code which caused large +drives to be formatted with the wrong size and number of clusters. To +fix the problem, get_format_parameters() was changed to use Microsoft- +recommended values and cleanly handle when FAT32 is turned off. + +5. A bug was discovered with large I/O which caused IDE I/O larger than +128 blocks or more generic I/O larger than 65535 blocks to fail. To +fix the problem, ide_io() was renamed to _ide_io() and a new ide_io() +was written as a wrapper to loop calls to _ide_io() with 128 blocks each +time. Also, po_write() and po_read() were modified to correctly loop +over a block count greater than 0xffff. + +6. A bug was discovered with the linear flash driver (drflsftl.c) and +failsafety. If the power was turned off at certain moments while copying +a block, the driver would either incorrectly format or just not handle the +drive. To fix the problem, the erase count is always set when copying a +block, and the driver recovers from a power-off right before setting the +target block as a DATA block. + +New Features: + +1. JIS encoding support has been expanded to include Microsoft's CP932. +This is a super-set of JIS and adds NEC special characters (83 characters), +NEC-selected IBM extended characters (374 characters), and IBM extended +characters (388 characters). In addition, seven incorrect character +mappings have been corrected. See csjistab.c for the new tables. + +================================================================= +Functional changes between version 4.4O versus version 4.4N (Nov 09, 2004) + +Bug Fixes: + +One minor change was made to pc_findin() in rtnvfat.c and rtvfat.c. The +change prevents volume labels from appearing when the flags GET_INODE_MATCH +or GET_INODE_DOTDOT are used. This allows the creation of files with the +same name as the volume label, but volume labels will still appear when using +pc_gfirst() and friends. Volume labels have the AVOLUME attribute set to +tell them apart. + +================================================================= +Functional changes between version 4.4N versus version 4.4M (July 23, 2004) + +Bug Fixes: + +Two minor changes were made to FailSafe mode. These changes were made to +force all of ERTFS's cached information to be flushed before every failsafe +commit operation. + +1. A bug was discovered in failsafe in which the FATs where not being +updated from the FAT buffer pool to disk before the auto_commit function +was called. To fix the problem pro_failsafe_commit_internal was modified +to call flush_fat(). Also, the call to flush_fat was removed from +pro_failsafe_commit() because it is now being done inside +pro_failsafe_commit_internal, which is called by pro_failsafe_commit(). +This forces flush_fat to be called from both the api layer +pro_failsafe_commit() and from the automatic layer pro_failsafe_auto_commit() + +2. A bug was discovered in failsafe in which cached directory entries +were not being updated from the internal finode cache to the journal file +before failsafe commit completed. Two fix it Pro_failsafe_commit_internal +was modified to force a flush of cached directory entries to disk before +proceeding + +New Features + +1. Created PO_AFLUSH file open flag to automatically force a flush of a +file every time time it is written to. + +Summary of changes: +1. apifilio.c po_close() - remove call to pc_flush_file_buffer() this is +now done in _po_flush(). +2. apiwrite.c po_write() - Added support for PO_AFLUSH flag +3. apiwrite.c _po_flush() - Add call to pc_flush_file_buffer() +4. apiwrite.c po_flush() - remove call to pc_flush_file_buffer() this is +now done in _po_flush(). +5. prfsapi.c - Remove call to flush_fat. This is now done inside +6. prfscore.c pro_failsafe_commit_internal() - Added caals to +pc_flush_all_fil() and flushfat(). +7. RTFS.H Added PO_AFLUSH file open flags + +================================================================= +Functional changes between version 4.4M versus version 4.4L (June 8, 2004) + +Bug Fixes: +1. A bug was found and repaired in the fat swapping code. The bug occurs when +a new fat block must be cached but there are no free blocks and no + unmodified blocks in the cache to swap with. In this case a "dirty" +block must be written to disk and then released for re-use. +pc_map_fat_block() had a bug in it that resulted in only the first copy of +the fat being written in this case. The bug is fixed in this release. It +is serious in nature, causing check disk or scan disk to report that the +FATs are missmatched, but in practice it doesn't actually happen that often +because ERTFS's FAT flush policies rarely leave "dirty" fat blocks cached for +long. +2. A bug was found and fixed in po_extend_file() which left the file pointer +at the wrong position if the extend feature failed. +3. Made unicode byte order handling code aware of KS_LITTLE_ENDIAN flag +instead of requiring a seperate localized configuration value. +4. Fixed a bug in drideata.c, the driver had been polling the status +register without allowing the settling time as prescribed by the +ATA specification. + +Summary of changes: +1. Restructured the FailSafe method first introduced in version 4.4E to +reduce ram requirements and improve flexibility and efficiency. +2. Introduce FailSafe autocommit mode. In this mode ERTFS automatically +performs the FailSafe commit function each time an API call is made that +changes the contents of the volume structure. +3. Introduce FailSafe autoinit mode. In this mode ERTFS automatically +initializes specific devices for FailSafe operation at start up. This +function along with the autrestore, autorecover and autocommit modes +provide a means to make FailSafe operation completely transparent to the +programmer and the user. +4. Greatly expanded the FailSafe test function to test all modes of FailSafe +and validate the functionality of each ERTFS API call in FailSafe mode. +5. Renamed the check disk function from check_disk() to pc_check_disk and + added parameters to pc_check_disk() that return status information, + control diagniostic output and whether to save or discard lost + cluster chains. +6. Renamed STAT to ERTFS_STAT. Some run time environments like + micro-Itron use the type STAT. The same structure is used in ERTFS so to + avoid clashes we renamed typedef struct stat {} STAT; to + typedef struct ertfs_stat {} ERTFS_STAT; +7. Modified the mount procedure to support mounting devices that may + contain just a bios parameter block (bpb) or both a master boot + record (mbr) and a bios parameter block (bpb). +8. Added support to drideata.c to support removable TRUE-IDE devices along + with the existing support for removable pcmcia devices. +9. Added support to drideata.c and pcmctrl.c to support removable + pcmcia devices by polling for a status change. This method will + work if the pcmcmia controller does not have a media changed + interrupts source but does support a media changed status latch + functions. + +Detailed description of changes: + These files were removed and replaced with other files. + prfailsf.c - File was removed + prfailsf.h - File was removed + These files were added. + prfs.h - New file that replaces prfailsf.h. It is + automatically included from rtfs.h when INCLUDE_FAILSAFE is enabled. + prfsapi.c - New file that contains FailSafe API calls. + prfscore.c - New file that contains the core FailSafe algorithm. + prfsnvio.c - New file that contains the Journal file interface. + + rtfspro.h - This is a new file that contains miscelaneous + function prototypes and definitions that are available only with + RTFS-Pro. rtfspro.h is automatically included by rtfs.h in the + RTFS pro package. + prapipro.c - This is a new file that contains miscelaneous API + calls that are available only with RTFS-Pro. In this release prapipro.c + contains the source code for the routines pro_buffer_status() and + pro_assign_buffers() + + These files were modified. + rtfs.h - Several changes were made to support + . Created CHKDSK_STAT structure to be used as an argument + . renamed STAT structure to ERTFS_STAT + to pc_check_disk() + . Created errno PEIOERRORREADJOURNAL + . Created errno PEFSREINIT + . Changed function protoype for new versions of + pc_regression_test(), and pc_check_disk(). + . created DRIVE_FLAGS_FAILSAFE to support FailSafe + Autoinit mode. + . Changed RTFS.H to include prfs.h if FailSafe is enabled + . Changed RTFS.H to include rtfspro.h if RTFS-Pro is enabled + + rtfsconf.h + . Added INCLUDE_RTFS_PRO compile time configuration constant. + + + prblock.c - Fixed a bug in pc_map_fat_block() that resulted + in only the first copy of the FAT being updated during some + swap condition + + apirealt.c - Fixed a bug in po_extend_file() which left the file's + seek pointer at the end of the file if the File extend failed. + + appcmdsh.c - Modified the code to support the new calling + sequences for check disk, regression test and FailSafe. + Changed all references to STAT to ERTFS_STAT + + apistat.c - Changed all references to STAT to ERTFS_STAT + + rttermin.c - Changed all references to STAT to ERTFS_STAT + + csstrtab.c - Minor changes to strings used by the test command shell. + csstrtab.h + + csunicod.c - Made byte order code aware of KS_LITTLE_ENDIAN flag + + drideata.c - In ide_do_command() when performing a write operation + poll the alternate status register several time before testing + for busy to provide the required 400 nsec settle time. + + rtlowl.c - new feature - + Modified pc_i_dskopen() to test if a device has a valid BPB (logical + block 0) and continue the mount process even if it has no MBR + (partition table). This specifically supports compact flash cards + fromatted with XP, which puts no MBR on the card. + + + apiregrs.c - A new argument was added to pc_regression_test(). The + new argument (do_clean) if TRUE tells the regression test to + execute multiple passes and delete all files an subdirectories + created in each pass. If do_clean is FALSE the regression test + stops after one pass and leaves all files and directories present. + + rtfatxx.c - fatxx_alloc_chain() - Added dolink argument to be able + to control whether we link to start cluster if it was provided. + This argument was added for failsafe to support allocating from + a starting point but not linking to the start point. + + apichkdsk.c - Renamed the check disk function from check_disk() to + pc_check_disk and added several parameters. + pstat - a pointer to a structure of type CHKDISK_STATS. + pc_check_disk returns information about the disk in this structure. + typedef struct chkdisk_stats { + dword n_user_files; /* Total #user files found */ + dword n_hidden_files; /* Total #hidden files found */ + dword n_user_directories; /* Total #directories found */ + dword n_free_clusters; /* # free available clusters */ + dword n_bad_clusters; /* # clusters marked bad */ + dword n_file_clusters; /* Clusters in non hidden files */ + dword n_hidden_clusters; /* Clusters in hidden files */ + dword n_dir_clusters; /* Clusters in directories */ + dword n_crossed_points; /* Number of crossed chains. */ + dword n_lost_chains; /* # lost chains */ + dword n_lost_clusters; /* # lost clusters */ + dword n_bad_lfns; /* # corrupt/disjoint win95 lfn chains */ + } CHKDISK_STATS; + + verbose - If this parameter is 1 pc_check_disk() prints status + information as it runs, if it is 0 pc_check_disk() runs silently. + fix_problems - If this parameter is 1 pc_check_disk() will make + repairs to the volume, if it is zero, problems are reported but + not fixed. + write_chains - If this parameter is 1 pc_check_disk() + creates files from lost chains. If write_chains is 0 + lost chains are automatically discarded and freed for re-use. + If fix_problems is zero then write_chains has no affect. + + apiinit.c - Added support for auto initializing FailSafe operation + on a device if the user assigns DRIVE_FLAGS_FAILSAFE to the devices + the drive flags. + + prfstest.c - This is the source code for the function fs_test() that + is documented in the FailSafe manual. In this version fs_test() was + completely re-written to be more comprehensive and test new FailSafe + features. + + Functions in the following files were modified to support FailSafe's + autocommit feature. The epilogue code for functions that may modify + the FAT or directory blocks now call release_drive_mount_write() + instead of release_drive_mount(). In autocommit mode + release_drive_mount_write() performs a failsafe commit if necessary. + + apideltr.c - pc_deltree() + apifilio.c - po_open(), po_close() + apifilmv.c - pc_mv(), pc_unlink() + apimkdir.c - pc_mkdir(), pc_rmdir() + apirealt.c - po_extend_file() + apiwrite.c - po_write(), po_truncate(), po_flush(), pc_diskflush() + + prblock.c - Changes were made to support the new FailSafe method. + . Several new functions were created to distinguish when + disk access is being performed on directory and FAT blocks. + block_devio_read(), block_devio_write(), fat_devio_write() and, + fat_devio_read(). If Failsafe is enabled routine in prfscore.c + replace these functions to provide a journalling feature. + . pc_find_block is no longer a static function because it + is now called from the FailSafe layer. + . pc_read_block(), pc_write_block(), pc_flush_chain_block() + pc_flush_fat_blocks() and pc_map_fat_block() are modified + to replace the previous FailSafe method with the new scheme. + + rtdevio.c - Changes were made to support the new FailSafe method. + . A new routine, release_drive_mount_write() was created + to support the Failsafe autocommit feature. + . check_media() was modified to support the new FailSafe + method. + + rtfat32.c - fat_flush_info() was modified to use the block buffer + pool instead of direct io. This change was needed for the new + FailSafe method. + + rtfatxx.c - fatxx_flushfat() was modified to support the new + FailSafe method. It no longer needs any FailSafe specific code + + + +================================================================= + +Functional changes between version 4.4L versus version 4.4K + +Summary of changes: + +1. The method for closing out a drive when media removal events and +unrecoverable media IO errors. +2. Support for buffered file IO has been added +3. Changes were made to detect if a disk mount was closed between calls to +po_gfirst, po_gnext and pc_gdone. +4. A potential buffer overwrite in the routine pc_partition_media() that may +occur with certain compilers has been eliminated. +5. pc_free_scratch_blk() was modified to fix a small problem that could cause +pro_buffer_status() to return an incorrect value for block_buffers_low. + +Detailed description of changes: + +The method for closing out a drive when media removal events and unrecoverable +media IO errors has changed. Under the old method the thread that detected the +failure imediately called pc_dskfree(). Under the new method a flags named +mount_abort is set when the error is detected. Later on, in the new routine +release_drive_mount(), before exlusive access to the drive is relinquished, + the flag is checked, and if it is true, pc_dskfree() is called. This method +eliminates race conditions in multitasking environments. + +rtdevio.c - check_drive_name_mount(), check_drive_number_mount() These +new functions validate and/or initiate the mount and then lock the drive +before returning. +rtdevio.c - check_drive_number_present(). This is a new function +that clears any media removal status and then checks if media is installed. +rtdevio.c - release_drive_mount(), This is a new function that checks +the pdr->mount_abort flag and if true, calls pc_dskfree() before releasing +the semaphore. +rtdevio.c - As a precaution check_media(), when it is working on +behalf of check_drive_name_mount() or check_drive_number_mount(), checks + the abort flag and if set calls pc_dskfree() before remounting the disk. +rtdevio.c - check_media_entry() and check_media_io(), +card_failed_handler() now set the mount_abort flag in the drive structure +if an error occurs. They do not call pc_dskfree(). + +The following routines were modified to take advantage of the new +functions check_drive_name_mount(name); and check_drive_number_mount(drivno); +these routines are like the previous check_drive() function which validates +and makes sure the device is mounted. If the drive is valid, the new +functions return with the drive's semaphore locked. These routines are also +modified to call release_drive_mount(driveno) instead of +OS_RELEASE_LOGDRIVE(driveno). release_drive_mount(driveno) checks to see if +the drive mount became invalid by an error or media removal event. If so, +it closes the drive structure and releases its buffers. + +apideltr.c - pc_deltree() +Apirealt.c - pc_cluster_size(), pc_raw_read(), pc_raw_write() +Apigetwd.c - pc_pwd() +Apigfrst.c - pc_gfirst() +prfailsf.c - pro_failsafe_init(), pro_failsafe_commit(),pro_failsafe_restore() +apiinfo.c - pc_free(),pc_is(),pc_get_attributes(), +APIMKDIR.C - pc_mkdir(),pc_rmdir() +Apisetwd.c - pc_set_cwd() +Apiwrite.c - pc_diskflush(),pc_set_attributes() +apistat.c - pc_stat() +Apifilmv.c - pc_mv(), pc_unlink() +apickdsk.c - check_disk() +Apifilio.c - po_open() +Apifilmv.c - A simple change was also made to change the order in which the +drive id's are validated to be more efficient. + +The following routines were modified to call the new routine named +check_drive_number_present(driveno); instead of check_media() routine +to first clear any pending drive changed status, and then +to test if media is present. This works the same as the old function +call check_media(). check_media() is now a static function in +rtdevio.c that is called by check_drive_number_present() and +check_drive_name_mount(); and check_drive_number_mount(). + +rtfat16.c - pc_mkfs16() +rtfat32.c - pc_mkfs32() +apiinit.c - auto_format_disk() +prfailsf.c - pro_failsafe_init() +appcmdsh.c - doformat() + +Apifilio.c - pc_enum_file() - This routine was modified clear the +file structure's drobj field if it was called on behalf of a media +removal or failure. pc_enum_file frees the file's structures but leaves +the file in the closed state. pc_fd2file() will recognize this state and +set errno == PECLOSED. po_close must be called on the file to clear this + +Apifilio.c - pc_fd2file() - This routine was modified so that when it +does return a valid file structure, the associated drive is locked. When it +returns failure it does not lock the drive. This routine works in harmony +with po_close() and the free all files part of pc_dskfree() to ensure proper +behavior when a disk is closed as a result of a removal event or media failure. + +Apifilio.c - po_close() - This routine was modified so that it clears +the PECLOSED error and frees the file if it is in the closed state. + +The following routines were changed in order to use a new feature of +pc_fd2file(). Users of pc_f2dfile no longer have to call OS_CLAIM_LOGDRIVE() +since pc_fd2file locks the drive for them. The following routines were +modified to support this feature. They were also changed to call +release_drive_mount(driveno) instead of OS_RELEASE_LOGDRIVE(driveno) to +release the drive and to free its resources if an error was detected. + +Apifilio.c - po_open(), po_read(), po_sleek(), po_close() +APickdsk.c - build_chk_file() +Apirealt.c - po_extend_file(), pc_get_file_extents() +Apiwrite.c - po_write(), po_truncate(), po_flush() +Apistat.c - pc_fstat() +Note that po_chsize() is not modified because it is not an atomic +function. + +The following changes were made to catch possible problems that could +occur if a device was closed and remounted between calls to pc_gfirst, +pc_gnext, and pc_gdone(). + +rtlowl.c - pc_i_dskopen() when a disk open succeeds increase the global + count of opens, prtfs_cfg->drive_opencounter, and assign it to + pdr->drive_opencounter. This number is unique to each sucessful mount. + +Apigfrst.c - pc_gfirst() set statobj->drive_opencounter to + pdrive->drive_opencounter when the call succeeds. + +Apigfrst.c - pc_gnext(), pc_done(). Compare statobj->drive_opencounter to + pdrive->drive_opencounter. If they are not the same then fail. + +The following changes were made to support buffering of non block alligned +file data. + +ERTFS.H - Added PO_BUFFERED file open flags +ERTFS.H - Added OF_BUFFERED flag to the finode structure. +ERTFS.H - Add "struct blkbuff *pfile_buffer" field to the finode +structure. +ERTFS.H - Add "int file_buffer_dirty" field to the finode structure. +APIFILIO.C - Created three new functions to support buffered file io. + pc_flush_file_buffer(PC_FILE *pfile) - If the finode structure that + the file points to contains a file buffer that has been modified + but not written to disk, write the data to disk. + + pc_load_file_buffer(PC_FILE *pfile, dword new_blockno) If new_blockno + is 0, and the finode structure that the file points to contains a + file buffer, then flush and then discard the file_buffer. + If new_blockno is not 0, and the finode structure that the file + points to contains a file buffer, then flush and then load the + file_buffer with the contents at new_blockno. + + pc_sync_file_buffer(PC_FILE *pfile, + dword start_block, dword nblocks, BOOLEAN doflush) + If the finode structure that the file points to contains a file + buffer that is in the range of start_block to start_block+nblocks, + Then: + 1. If doflush is true and the buffer has been modified, + write the data to disk. + 2. Discard the buffer +APIFILIO.C - po_open() - Added the following section to the open flags + processing part. + if (flag & PO_BUFFERED) + pobj->finode->openflags |= OF_BUFFERED; +APIFILEIO.C - po_read() modify section of code that handles non + block alligned writes. Do not discard the buffer if the + underlying finode is set for buffered IO, use the + *pfile_buffer field instead. +APIFILIO.C - po_read() modify section that handles block alligned + data reads. First call pc_sync_file_buffer() and + instruct it to flush to disk buffered data that overlaps + with the blocks to be read. +APIFILIO.C - po_close() modify to write the file buffer + if needed before closing the file. The buffer is freed + when pc_memory_finode() is called to free the finode. +APIREGRS.C - Add do_buffered_file_test(void) to the regression test. +APPCMDSH.C - Change docat() to use buffered IO and read one character + at time. +APIWRITE.C - po_write() modify section of code that handles non + block alligned writes. (a write of non alligned data + consists of a read/merge/write sequence). Do not discard + the buffer if the underlying finode is set for buffered IO, + use the *pfile_buffer field instead. +APIWRITE.C - po_write() modify section that handles block alligned + data writes. Call pc_sync_file_buffer() and instruct it + to purge, rather then write, buffered data that overlaps + with the blocks to be written. +APIWRITE.C - po_truncate() Before truncating the file, call + pc_load_file_buffer(pfile, 0) to make sure the + file buffer is written to disk if needed and then + discarded. +APIWRITE.C - po_flush() call pc_flush_file_buffer() to make sure the + file buffer is written to disk if needed. +PRBLOCK.C - Eliminate pc_read_file_block() subroutine. This is now done + by pc_load_file_buffer(). +PRBLOCK.C - Eliminate pc_write_file_block() subroutine. This is now done + by pc_flush_file_buffer(). +RTKERNFN.C - pc_memory_finode(). When freeing a finode, added code to + check if the finode contains a file buffer, and if so, + release the buffer. + + +Other miscelaneous changes. + + It was discovered that with some compilers sizeof(PTABLE) is + larger than 0x42 because of structure packing problems at the + end of the PTABLE. This caused a buffer pool corruption + problem after using pc_partition_media() + +APIFRMAT.C pc_partition_media() the line of code that used + the expression: + copybuff((pbuf + 0x1be), &part, sizeof(PTABLE)); + to + copybuff((pbuf + 0x1be), &part, 0x42); + +RTLOWL.C - pc_read_partition_table() changed two lines of code that used + the expression: + copybuff(buf->data, pbuf, sizeof(PTABLE)); + to + copybuff(buf->data, pbuf, 0x42);. + + Remove spurious line of code that was accidentally introduced in + vesion 44f. This was not causing a runtime problem since the logic + is the same. +APIMKDIR.C - pc_mkdir() +The lines: + if (!pc_parsepath(path,filename,fileext,name)) + /* Get out the filename and d:parent */ + if (!pc_parsepath(path,filename,fileext,name)) +Are changed to: + /* Get out the filename and d:parent */ + if (!pc_parsepath(path,filename,fileext,name)) + +PRBLOCK.C - pc_free_scratch_blk(), modify code to set + pblk->block_state = DIRBLOCK_FREE and to increment + pbuffcntxt->num_free. This fixes a small problem + that could cause pro_buffer_status() to return + an incorrect value for block_buffers_low. +APICHKDSK.C - print_chkdsk_statistics() - Changed a line that + was printing free kilabytes remaining when + it was supposed to print the number of free sectors + remaining. + + +==================================================================== +Functional changes between version 4.4K versus version 4.4J + +Major changes - Added errno value PECLOSED. This error number is set +by pc_fd2file() when access to a file descriptor is requested that was +marked closed by pc_dskclose. The version 44K users manual documents that +if a file operation fails with errno set to PECLOSED the application should +call po_close() on the file descriptor to clear the condition. + +==================================================================== +Functional changes between version 4.4J versus version 4.4I + +Major changes - +Change pc_dskclose() so that files opened on a volume that is being closed + are marked as closed but the files are not released. po_close() must still +be called to release the file. This eliminates the possibility of one thread +accidentally using a file descriptor that was closed out but re-used by +another thread and now is associated with a different file. +apifilio.c - po_close() - If pc_fd2file() fails check if the FD is +in the closed state and if so free it. +apifilio.c - pc_fd2file() - Check file's is_closed setting, if it +is set pc_fd2file() fails. pc_fd2file() fix minor bug, was checking + if (0 <= fd && fd <= pc_nuserfiles()) +corrected to: + if (0 <= fd && fd < pc_nuserfiles()) +apifilio.c - pc_enum_file() - Do not enumerate files in the closed state. +apifilio.c - pc_enum_file() - ENUM_FREE now marks the file as closed but +does not mark it free. +rtfs.h - Add int is_closed; as a field in the pc_file structure; +Change default drive handling so the init code stores the lowest valid device +in the prtfs_cfg structure. When the user retrieves the current working drive +we use the value from prtfs_cfg structure if the user has not explicitly called +pc_setdfltdrv(). +apiinfo.c - pc_set_default_drive() - When the drive is set, also set the +dfltdrv_set field in the user structure to indicate that the default drive +was set explicitly. +apiinfo.c - pc_getdfltdrno() - Use the value from prtfs_cfg structure if the + user has not explicitly called pc_setdfltdrv() (indicated by + user->dfltdrv_set not equal 0) +apiinit.c pc_ertfs_init() - As the devices are initialized set +prtfs_cfg->default_drive_id to the lowest valid drive id, as indicated +by the warmstart routine succeeding. +rtutil.c - pc_parsedrive() - Validate the drive number even if it is the +default drive id. +rtfs.h - Add int dfltdrv_set; as a field in the RTFS_SYSTEM_USER structure; +rtfs.h - Add int default_drive_id; as a field in the RTFS_CFG structure + +==================================================================== +Functional changes between version 4.4I versus version 4.4H + +Major changes - +Change pc_mknode() so that ".", ".." and their parent directory are +all guaranteed to have the same date stamp. +Change rtkernfn.c, apiinit.c and rtfs.h to store the LOGICAL_DRIVE +semaphore handle in the drive structure rather then in an array of 26 + possible logical drives. This fixes a bug where uninitialized semaphore +handles were used for logical drives when a logical drive's drive number +was greater than the NDRIVES configuration value. +Change rtlowl.c to support extended DOS partitions. This feature must be + enabled at compile time since it is rarely needed. +Change rtvfat.c to fix a bug where we were hanging an argument in place +from "*.*" to "*". This caused the library to missbehave if the argument +came from the string constant area of ROM. +Change csjis.c pc_patcmp_3(). Fixes a bug that incorrectly truncates JIS +.3 extensions to 2 characters. +rtdrobj.c - At approximately line 605. In pc_mknode(). Change calls to +pc_init_inode to "." and ".." to use the date value used to initialize the +subdirectory. This way the directy, "." and ".." will always have the same +timestamp. + + crdate.time = pobj->finode->ftime; + crdate.date = pobj->finode->fdate; + pc_init_inode( &lfinode, dot_str,null_str, ADIRENT|ARCHIVE, + cluster, /*size*/ 0L , &crdate); + /* And to the buffer in intel form */ + +rtkernfn.c - At approxiomately line 30. in rtfs_resource_init(void). Remove +loop that allocates drive semaphores. This is now done in pc_rtfs_init(). + + +apiinit.c - At approximately line 270 - Allocate semaphores and assign them +to the drive structure. + /* Allocate semaphores for all drives here and assign them to the + drive structures. If an allocation fails, fail to initialize */ + pdr = prtfs_cfg->mem_drives_structures; + for (j = 0; j < prtfs_cfg->cfg_NDRIVES; j++, pdr++) + { + /* make sure this drive has a semaphore associated with it */ + pdr->access_semaphore = rtfs_port_alloc_mutex(); + if (!pdr->access_semaphore) + return(FALSE); + } + +RTFS.H - At line 433 add an access semaphore field to the DDRIVE structure. +/* Access semaphore for the drive. This is initialized in pc_ertfs_init + from a value in value prtfs_cfg->drive_semaphores[i] */ + dword access_semaphore; + +RTFS.H At line 1260 - change OS_CLAIM_LOGDRIVE() and OS_RELEASE_LOGDRIVE() +macros to use the handle store in the drive structure. + +#define OS_CLAIM_LOGDRIVE(X) rtfs_port_claim_mutex((prtfs_cfg->drno_to_dr_map[X])->access_semaphore); +#define OS_RELEASE_LOGDRIVE(X) rtfs_port_release_mutex((prtfs_cfg->drno_to_dr_map[X])->access_semaphore); + +RTFS.H At line 1308 - remove drive_semaphores[] array from the RTFS_CFG +structure definition. + + +rtvfat.c At line 1057, modify the routine pc_patcmp_vfat() so it no +longer modifies the pattern argument in-place to convert "*.*" to "*" +if VFAT is supported. This was a bug since the arguments ("*.*") could +come from a constant string, which could be constant data + + +rtfsconf.h - At line 51. Create #define SUPPORT_EXTENDED_PARTITIONS, set to +1 to support DOS extended partitions. + + +Lowl.c - AT line 255. Added support for DOS "Extended Partitions". This code +must be enabled by setting SUPPORT_EXTENDED_PARTITIONS, in rtfsconf.h to 1. + +#if (SUPPORT_EXTENDED_PARTITIONS) +{ + /* Special code to support extended partitions */ + /* since most applications don't use these we compile it + conditionally to save code space */ + dword extended_base, ltemp; + int j, skip_count; + for (j = 0; j < 4; j++) + { + if (ppart->ents[j].p_typ == 5) + { + if (j <= (int)pdr->partition_number) + { + /* the partition is inside an extended partition */ + /* Get the relative start and size */ + pdr->partition_base = to_DWORD ((byte *) &ppart->ents[j].r_sec); + extended_base = pdr->partition_base; + pdr->partition_size = to_DWORD ((byte *) &ppart->ents[j].p_size); + skip_count = (int)pdr->partition_number - j; + for (;;) + { + /* Read the partition information in the extended partition */ + if (!devio_read(driveno, pdr->partition_base , buf->data , 1, TRUE)) + { + /* Failed reading Master boot record */ + rtfs_set_errno(PEIOERRORREADMBR); + ret_val = READ_PARTION_IOERROR; + goto done; + } + /* Copy the table to a word alligned buffer */ + pbuf = buf->data; + pbuf += 0x1be; /* The info starts at buf[1be] */ + copybuff(buf->data, pbuf, sizeof(PTABLE)); + ppart = (PTABLE *) buf->data; + + if (to_WORD((byte *) &ppart->signature) != 0xAA55) /*X*/ + { + rtfs_set_errno(PEINVALIDMBR); + ret_val = READ_PARTION_NO_TABLE; + goto done; + } + if (skip_count==0) + { + if (pc_validate_partition_type(ppart->ents[0].p_typ)) + { + pdr->partition_type = ppart->ents[0].p_typ; + ltemp = pdr->partition_base; + pdr->partition_base = ltemp + to_DWORD ((byte *) &ppart->ents[0].r_sec); + pdr->partition_size = to_DWORD ((byte *) &ppart->ents[0].p_size); + ret_val = READ_PARTION_OK; + goto done; + } + else + { + rtfs_set_errno(PEINVALIDMBROFFSET); + ret_val = READ_PARTION_NO_ENTRY; + goto done; + } + } + else + { + if (ppart->ents[1].p_typ != 5) + { + rtfs_set_errno(PEINVALIDMBROFFSET); + ret_val = READ_PARTION_NO_ENTRY; + goto done; + } + pdr->partition_base = extended_base + to_DWORD ((byte *) &ppart->ents[1].r_sec); + pdr->partition_size = to_DWORD ((byte *) &ppart->ents[1].p_size); + skip_count -= 1; + } + } + } + } + } +} + /* Fall through to here if not in an extended partition */ +#endif /* (SUPPORT_EXTENDED_PARTITIONS) */ + +==================================================================== +Functional changes between in version 4.4H versus version 4.4G + +Major functional changes: + +apigetcwd.c In pc_l_pwd modified source code to clean up correctly if the +depth of the current working directory depth exceeds the compile time +variable OBJDEPTH. + +appcmdsh.c - Added FAILSAFE test routines if INCLUDE_FAILSAFE_CODE is +enabkled. + +csjis.c - Fixed bug in pc_ascii_fileparse with was truncating files +with greater than 8.3 components to 7.2. Files are now correctly truncated +to 8.3 format. + +csstrtab.c - Added prompts for calling FAILSAFETEST. Prompts are only used +when failsafe is enabled. + +prblock.c - +In pc_map_fat_block change the order of evaluation of block status blocks +and query failsafe. This is a simple optimization to cut down on +subroutine calls. + +In pc_map_fat_block in FAILSAFE mode change the error condition to +PERESOURCEFATBLOCK if no fat blocks are available. + +prfailsf.c - Initial supported failsafe release + +prfailsf.h - Initial supported failsafe release + +rtdrobj.c - In pc_mknode() at line 606 call pc_init_inode for the ".." +entry passing it the date value that was retieved from the system +when it was assigned to ".". This way . and .. always have the same +time stamp. + +winhdisk.c - Modified driver to emulate a removable device. Test code can +now pass remove events to the host disk driver and it will simulate a +no media CHANGED condition. + +prfstest.c - New file containing test code for failsafe mode. This test +code is designed to run in the windows host disk environment. + +==================================================================== +Functional changes between in version 4.4G versus version 4.4F + +Major functional changes: + +In prblock.c - Added the routine pc_flush_chain_blk to support flushing +all blocks associated with a subdirectory's cluster chain + +At line 535 added the routine: +void pc_flush_chain_blk(DDRIVE *pdrive, CLUSTERTYPE cluster) + +In drmmccrd.c - Many changes to support mmc in the rtfs4.0 environment. + +In drflsftl.c - Added support for using a ram base sector map to speed +up accesses. The code is fixed to use a map large enough to hold 1000 +elements. In the next release this will be modified to be configurable +and to disable sector mapping. + +At line 58 add: +PhysicalSectorMap StaticSectorMap[1000]; + +At line 87 change: + return (LowLevelFormat(&FlashData, 0, 0)); +To: + if (LowLevelFormat(&FlashData, 0, 0) == 0) + { + MapSectors((void *) &FlashData); + return 0; + } + else + return -1; + +At line 93 - +change: + case DEVCTL_WARMSTART: + MapSectors((void *) &FlashData); +to: + case DEVCTL_WARMSTART: + FlashData.SectorMap = &StaticSectorMap[0]; + MapSectors((void *) &FlashData); + +In drflash.h at line 16 + changed: +typedef union { + byte PhysicalSectorAddr[3]; + byte MustUpdateAddr[1]; +} PhysicalSectorMap, *PhysicalSectorMapPtr; +to: +typedef struct { + byte PhysicalSectorAddr[3]; + byte MustUpdateAddr[1]; +} PhysicalSectorMap, *PhysicalSectorMapPtr; + +apifrmat.c: Fixed to use hidden sectors only on a partitioned disk + +At line 549 +changed: + fmt.secreserved = (word) 32; + if (pgeometry->dev_geometry_lbas) + /* 10-24-2000 - New code to support lba formatting */ + fmt.numhide = (unsigned long) 0; /*PS Does not work as fmt.secptrk here */ + else + /* 10-24-2000 - This was the original code */ + fmt.numhide = (unsigned long) fmt.secptrk; +to: + fmt.secreserved = (word) 32; + if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED) + { + if (pgeometry->dev_geometry_lbas) + /* 10-24-2000 - New code to support lba formatting */ + fmt.numhide = (unsigned long) 0; /*PS Does not work as fmt.secptrk here */ + else + /* 10-24-2000 - This was the original code */ + fmt.numhide = (unsigned long) fmt.secptrk; + } + else + fmt.numhide = (unsigned long) 0; +rtdrobj.c - Fix problem accessing .\xxx in vfat systems change line 175 from: + if (CS_OP_CMP_ASCII(pf0,'.') && CS_OP_IS_EOS(pf1)) /* DOT */ + ; +to: +#if (VFAT) + if (CS_OP_CMP_ASCII(pf0,'.') && CS_OP_IS_EOS(pf1)) /* DOT */ + ; +#else + if (CS_OP_CMP_ASCII(pf0,'.') && CS_OP_CMP_ASCII(pf1,' ')) /* DOT-in NON-VFAT it is space filled */ + ; +#endif + +rttdrobj.c - Fix a bug initializing the date fields for directory entries. + +change lines 590 and 599 from + pc_init_inode( &lfinode, dot_str,null_str, ADIRENT|ARCHIVE, + cluster, /*size*/ 0L , &crdate); + pc_init_inode( &lfinode, dot_str, null_str, ADIRENT|ARCHIVE, cltemp, + /*size*/ 0L , &crdate); +to: + pc_init_inode( &lfinode, dot_str,null_str, ADIRENT|ARCHIVE, + cluster, /*size*/ 0L ,pc_getsysdate(&crdate)); + pc_init_inode( &lfinode, dot_str, null_str, ADIRENT|ARCHIVE, cltemp, + /*size*/ 0L , pc_getsysdate(&crdate)); + +rttdrobj.c - Make sure cluster blocks are flushed from the buffer pool +when deleting a subdirectory + change line 689 from + if (pc_update_inode(pobj, TRUE, TRUE)) + { + /* If there is no cluster chain to delete don't call freechain */ + if (!cluster) + return(TRUE); + /* And clear up the space */ +To: + if (pc_update_inode(pobj, TRUE, TRUE)) + { + /* If there is no cluster chain to delete don't call freechain */ + if (!cluster) + return(TRUE); + /* And clear up the space */ + /* Make sure the blocks contained within the cluster chain + are flushed from the block buffer pool */ + pc_flush_chain_blk(pdrive, cluster); +Rtfs.h Added the following prototype at line 789 +void pc_flush_chain_blk(DDRIVE *pdrive, CLUSTERTYPE cluster); + +rttermin.c - Fixed date print formatting +At line 152, changed: + sprintf((char *)gotoeos(p),"-%02d",80 +(statobj->fdate >> 9) & 0xff); /* Year */ +to: + year = 80 +(statobj->fdate >> 9) & 0xff; /* Year */ + if (year >= 100) + year -= 100; + sprintf((char *)gotoeos(p),"-%02d", year); /* Year */ +==================================================================== +Functional changes between in version 4.4F versus version 4.4E +Major functional changes: +apicnfig.c + +Change NBLKBUFFS from 10 to 20. We are using a few more scratch buffers. +(maximum 3 at a time, so up the buffer pool size a little). + +apiregrs.c + Added a test to create, reopen, and delete long file names when + VFAT is enabled. + Replace large stack arrays with allocations from the block buffer pool + +Appcmdsh.c + Remove long file name test. It was moved to apiregrs.c +csstrtab.c +csstrtab.h + Added string for long file name test in apiregs.c +rtfat32.c - + Split rtfat32.c into rtfat32.c and rtfat16.c +rtfs.h - + Changed the vfat segdesc structure to contain 3 blocks, the worst case + possible. It had been 2. +rtvfat.c + Changed lfi2text to take the lfn's current length as an argument. Checks the + that the length does not exceed 255 characters. + Changed pc_findin to use scratch buffers instead of large stack byte arrays + Changed pc_deleteseglist, pc_seglist2disk and pc_seglist2text to support + long file name segments that exceed 3 blocks. + + + +==================================================================== +Functional changes between in version 4.4E versus version 4.4D +Major functional changes: +1. Fixed support for very large file and path names. This increases parse +buffer sizes substantially for JIS and UNICODE configurations so parse +buffers were moved to the DDRIVE structure and when those are not +available scratch buffers are used. +2. Support for the CD-ROM option (ASCII only) was added to ERTFS. All CD-ROM +operations are conditionally excluded at compile time. +3. Preliminary integration of FAILSAFE support was completed. All FAILSAFE +operations are conditionally excluded at compile time. +4. The pc_stat function was modified to report the root directory as a +directory. Before this fix the stat structure was initialized with +incorrect attributes. + +1. rtfvat.c - in text2lfi(), there were lines of the type: + lfn = CS_OP_INC_PTR(lfn); +This causes problems with some optimizing compilers. They were changed to: + CS_OP_INC_PTR(lfn); +2. apistat.c - in pc_stat. If the path is the root, fill in the stat + structure without consulting the finode structure since the finode structure is an + abstraction +3. apideltr.c - Added a test for read only. Don't remove a directory or its + contents if it is read only. + +4. The following modules were changed to support major functional +change one, supporting very long file names. Two main changes were made. +The first change eliminates use of stack buffers for temporary storage of + file and path specifications. In most cases the buffers were replaced + with special path and filename buffers contained in the drive structure. + In other cases, where the buffers in the drive structure are already in use, + scratch buffers are allocated. + + rtfs.h: Added pathname_buffer and filename_buffer fields that are used instead + of stack variables by the API parsing routines. + . Modified the DSTAT structure declaration for the field lfname. + . If VFAT is enabled lfname is FILENAMESIZE_BYTES wide, if not it is 14 + bytes wide. + . Added prototype for new function: int rtfs_cs_strlen_bytes(byte * string); + portconf.h: Eliminated the macros FILENAMESIZE and EMAXPATH and created + new macros named FILENAMESIZE_CHARS, FILENAMESIZE_BYTES, + EMAXPATH_CHARS, EMAXPATH_BYTES. Where FILENAMESIZE_CHARS and EMAXPATH_CHARS + are the maximum number in logical characters in the selected character + set and FILENAMESIZE_BYTES and EMAXPATH_BYTES are the number of bytes + needed to store the NULL terminated string of length FILENAMESIZE_CHARS + or EMAXPATH_CHARS. + apickdsk.c: Modified path and filename variable declarations. + apienum.c: Modified path and filename variable declarations. + Apideltr.c: Use drive's pathname_buffer and filename_buffer instead of stack + Apifilio.c: Use drive's pathname_buffer and filename_buffer instead of stack + Apifilmv.c: Use drive's pathname_buffer and filename_buffer instead of stack + APIGFRST.C: Use drive's pathname_buffer and filename_buffer instead of stack + APIMKDIR.C: Use drive's pathname_buffer and filename_buffer instead of stack + Apisetwd.c: Use drive's pathname_buffer and filename_buffer instead of stack + RTDROBJ.C: pc_findnode - Use scratch buffers instead of stack buffers + csunicod.c: pc_cs_malias - Use scratch buffers instead of stack buffers + RTNVFAT.C: pc_nibbleparse - use scratch buffers rather than stack buffers + . pc_parsepath() - Changed maximum path length test to test if the string + length in bytes is greater than the maximum character length for a path. + This works for both JIS and ASCII. + RTVFAT.C: pc_parsepath() - Changed maximum path length test to test if the logical + string in the character set is greater than the maximum character length + for a path or file. + . Modified lfi2text to accept an extra argument to allow + maximum length testing on the long file name as it is built from buffer + contents and Implemented length checking feature. + csascii.c - Created new function rtfs_cs_strlen_bytes() + csjis.c - Created new function rtfs_cs_strlen_bytes() + +5. The following modules were changed to support major functional +change number two, inclusion of optional CDROM support for ERTFS. +CDROM support is optional and is completely disabled by setting +INCLUDE_CDROM to zero in PORTCONF.H. + Rtfs.h: Added prototype for new functions: + word ide_rd_data(dword register_file_address); + void ide_wr_data(dword register_file_address, word value); + Rtfsconf.h: Removed INCLUDE_CDFS macro. This is now replaced by + INCLUDE_CDROM in portconf.h + portconf.h: added INCLUDE_CDROM configuration value + apiinit.c: Added CD-ROM support, this is conditionally excluded + drideata.c: Completed CD-ROM support, this is conditionally excluded + Appcmdsh.c: Changed all instances of INCLUDE_CDFS to INCLUDE_CDROM + Appcmdsh.c: removed call to if cd_memory_init(), this is now done + in pc_ertfs_init + portio.c: Added ide_rd_data and ide_wr_data functions to read and write + the ide data register. + +6. The following modules were changed to support major functional + change number 3, failsafe mode. Failsafe mode is not yet release, and + it must be left in the disabled state by setting INCLUDE_FAILSAFE_CODE + to zero in rtfsconf.h + Rtfs.h: Added ERRNO codes to support FAILSAFE operation + . Added num_free and low_water fields to the fat buffer context structure. + . Added num_free and low_water num_alloc_failures fields to the block buffer + context structure. This information will be made available to the user + through a new API call when the failsafe operating mode is release. + . If failsafe mode is enabled include failsafe header + Rtfsconf.h: Added INCLUDE_FAILSAFE_CODE macro to enable failsafe modules. + csstrtab.c and csstrtab.h: Added Failsafe strings, these are conditionally + excluded. + Appcmdsh.c: Added Failsafe test code, this is conditionally excluded + PRBLOCK.C: Modified DEBUG code to work correctly if the drive has a + private block buffer pool. + . Added code to track statistics on block usages, such as + current free and most ever used. + . Added calls to the failsafe library if failsafe is enabled. + This code is conditionally excluded. + rtdevio.c: Added calls to the failsafe library if failsafe is enabled. + This code is conditionally excluded. + RTFATXX.C: Added calls to the failsafe library if failsafe is enabled. + This code is conditionally excluded. +7. apirealt.c: pc_cluster_size - Add a call to check_drive to be sure +the mount the drive if it is not already mounted. + +==================================================================== +Functional changes between in version 4.4D versus version 4.4C +1. csunicode.c - Fix problem creating aliases for short file names +with invalid characters +in pc_ascii_malias() added: + /* Fill filename[8] with spaces before we start. */ + rtfs_memset(filename, ' ', 8); +2. csjis.c - Fix problem creating aliases for short file names +with invalid characters + in pc_cs_malias() added: + /* Fill filename[8] with spaces before we start. */ + rtfs_memset(filename, ' ', 8); +3. Fixed error validating file names an path name lengths +in rtvfat.c:pc_parsepath() +change: if (rtfs_cs_strlen(path) >= EMAXPATH) +to: if (rtfs_cs_strlen(path) >= EMAXPATH/2) +in rtvfat.c:pc_parserpath() +change: if (rtfs_cs_strlen(p) >= FILENAMESIZE) +to : if (rtfs_cs_strlen(p) >= FILENAMESIZE/2) + +==================================================================== +Functional changes between in version 4.4C versus version 4.4B +1. rtvfat.c - In pc_patcmp_vfat. At line 996 added the following lines: + else if (CS_OP_CMP_CHAR_NC(pp, pn)) + ; + this makes comparisons for vfat case independent. So HeLLo.txt matches +HELlo.txt. The files are stored exactly as they are specified but any +variation of case in the name finds the entry on a file open of directory +access. + +==================================================================== +Functional changes between in version 4.4B versus version 4.4A +1. rtvfat.c - Check return code of pc_malias. Set errno to PENOSPC +in pc_malias if it runs out of possible alias names. + +==================================================================== +Functional changes between in version 4.49b versus version 4.49 +apifilio.c - po_open - If truncating the file and the chain contains + invalid clusters, thruncate the file anyway, flush the + directory entry and proceed to open the file. +apifilio.c - _po_lseek - Change the function to not detect a bad cluster + chain condition if get_chain does not advance because the + seek pointer is at EOF. +rtfatxx.c - fatxx_freechain() - return error if clnext fails +rtfatxx.c - fatxx_get_chain() - fixed bug setting return value wrong. +This was problem was introduced in version 4.46 +apimkdir.c- pc_rmdir. modify error handling support to remove the + directory and chain and return FALSE if the chain contains + an invalid cluster +rtdrobj.c - pc_rmnode() - If freechain failed on an invalid cluster + proceed and flush the fat delete the directory entry + and return FALSE. + + +==================================================================== +Functional changes between in version 4.49 versus version 4.48 + +rtdrobj.c - pc_mknode - Call validate_filename() inside pc_mknode() +and remove it from routines that call pc_mknode + +apigfrst.c - pc_gfirst - If the search path is not a directory set errno +to PEINVALIDDIR instead of PENOENT. + +apisetwd.c - pc_set_cwd - If the path is not a directory set errno +to PEINVALIDDIR instead of PEACCES. +apigfrst.c - pc_gfirst - If the search path is not a directory set errno +to PEINVALIDDIR instead of PENOENT. + +rtfs.h - Changed comment about PEINVALIDDIR, checkdisk is not required. + +apideltr.c - pc_deltree - change errno setting if illegal directory +condition from PEINVALIDDIR to PEINVLIDCLUSTER + +rtdrobj.c, csjis.c, rtvfat.c, rtfs.h - Mofications to correctly support +valid leading kanji E5 character in first character of file. + +drflsftl.c - Corrected the prototype for mtd_ProgramData (); + +apiwrite.c - po_truncate Removed superfluous errno check and add validate +cluster check in case fptr_cluster is invalid. + +rtdrobj.c - pc_mkchild - remove setting errno to PEINVALIDBLOCKNUMBER +because pc_firstblock set errno already to PEINVALIDCLUSTER or PEINTERNAL + +rtutil.c - pc_path_to_driveno test for empty path string + +apifilio.c - Added a test to catch the error condition when the file size +is incorrect, longer than the actual cluster size and the file pointer +is on the terminating cluster. When a seek to a location that should be in +the next cluster is requested this was succeding. This new fix eliminates + that result. It now returns an error and sets errno to PEINVALIDCLUSTER. + +apigfrst.c - pc_gfirst/pc_gnext - Remove gnext_loop_count processing + +prblocks.c - Remove memory block loop detection from pc_free_all_blk, +pc_release_blk, pc_find_blk, pc_allocate_blk,pc_flush_fat_blocks, +pc_map_fat_block, pc_find_fat_blk, pc_sort_committed_blocks + +rtdrobj.c - Remove memory block loop detection from pc_scani,pc_free_all_i, + +rtfatxx.c - fatxx_clgrow,fatxx_cl_truncate_dir changed maximum value for +detecting endless cluster chain from 32768L to MAX_CLUSTERS_PER_DIR + +apigetcwd.c - pc_gm_name remove endless loop detection in scan for current +directory object in parent directory + +rtdrobj.c - pc_rmnode remove min and max cluster to release arguments + +pcmkdir.c - In pc_rmdir continue and delete a directory if it + contains a corrupted fat chain. + + +==================================================================== +Functional changes between in version 4.48 versus version 4.47 +In BOOLEAN pc_search_csl() +Bug fix, rotuine was not finding substring at the end of the string +Change + while (*set == *p) +To + + while (*set && *p && (*set == *p)) + + +Change + if (*set == ',' && *p == 0) +To + if ((*set == 0 || *set == ',') && *p == 0) + +==================================================================== +Functional changes between in version 4.47 versus version 4.46 +pc_validate_partition_type: add (p_type == 0x0E) Windows FAT16 Partition +==================================================================== +Functional changes between in version 4.46 versus version 4.45 +appcmdsh.c - Added DISPLAY_ERRNO() facility + +rtvfat.c - pc_malias - Clear PENOENT errno condition after verifying an +alias does not exist. + +fatop_clnext() - Changed the return values. Returns 0xffffffff on end of +chain, 0x0 if an error occurs. It is considered an error if the value +pointed to by gnext is not an end of chain marker and is +less than 2 or greater than pdr->maxfindex. If so the routine returns +0 and sets errno to PEINVLIDCLUSTER. + +fatxx_clgrow() - Modified routines to take advantage of new interface to +fatop_clnext(). Double checked clgrow callers for error handling. + +fatxx_freechain() - Added a minimum clusters to free and maximum clusters +to free to catch problems with files who's cluster chain is either +larger or smaller than the file size says it should be. +fatxx_freechain() - Modified routines to take advantage of new interface to + +fatop_clnext(). Double checked callers for error handling. +fatxx_truncate_dir() - Modified routines to take advantage of new interface to +fatop_clnext(). Added a minimum clusters to free to freechain call. + +fatxx_get_chain() - Modified to use common code and error handling in +fatop_clnext. Also added and end_of_chain return value and coordinated it +with but chain protection in the other files + +apichkdsk.c - Modified routines to take advantage of new interface to +fatop_clnext(). + +apichkdsk.c - If a file is zero sized make sure the cluster pointer in the +directory entry is set to zero. + +po_open() - Modified routines to take advantage of new interface to. Set an +error file pointer and cluster chain size do not agree while truncating file + +po_read - Use new return codes from fatop_get_chain() to detect when file +length is out of synch with cluster chain length. Fail if fatop_get_chain() +returns zero indicating IO or cluster range error + +_po_lseek - Use new return codes from fatop_get_chain() to detect when file +length is out of synch with cluster chain length. Fail if fatop_get_chain() +returns zero indicating IO or cluster range error + +pc_gnext() - Change the maximum number of calls before we think +we are in an endless loop to 0x2000000 as a maximum possible value. +thats 32768 (max number of clusters in a directory) times 64 (number of blocks in a 32 K cluster) + times 16 (maximum number of entries in a block) + +po_extend_file - When scanning to the end of the current file, use range +checking and new return codes from fatop_clnext to detect when file length +is out of synch with cluster chain length. Fail if fatop_get_chain() +returns zero indicating IO or cluster range error + +pc_get_file_extents - Use new return codes from fatop_get_chain() to detect +when file length is out of synch with cluster chain length. Fail if +fatop_get_chain() returns zero indicating IO or cluster range error + +po_write - Use new return codes from fatop_get_chain() to detect +when file length is out of synch with cluster chain length. Fail if +fatop_get_chain() returns zero indicating IO or cluster range error + +po_truncate - Use new return codes from fatop_clnext() to detect errors +in the cluster chain when seeking. +Use new interface to pc_free_chain to set an error when file length is +out of synch with cluster chain length. + +pc_set_attributes - Distignuish between illegal attributes passed as argument and an attempt to change attributes from +file to a directory or directory to file. Illegal attributes set errno to PEINVALIDPARMS otherwise set PEACCESS. + +==================================================================== +Functional changes between in version 4.45d versus version 4.45c +4.45d is released as version 4.45 +po_open - clear errno after PENOENT when doing a create +po_extend_file - Fixed range check error that was causing a leak +po_truncate - Fixed bug in calulation of clusters_to_delete +pc_regression_test() - Clean up test directory at startup with pc_deltree +Added tests for pc_deltree, po_chsize, added errno checks +rtfs.h - Added PEILLEGALERRNO for the regression test to use +==================================================================== +Functional changes between in version 4.45c versus version 4.45b +pc_mkdir - clear errno before calling freechain +fatxx_freechain - Check next_cluster value before calling gnext +==================================================================== +Functional changes between in version 4.45b versus version 4.45a + +po_open() line 226. change: + if (pc_update_inode(pobj, TRUE, TRUE)) +to + if (!pc_update_inode(pobj, TRUE, TRUE)) +pc_gnext() - changed to postincrement if (++statobj->gnext_loop_count >= 32768L) +po_truncate() +change: clusters_to_release = new_chain_len - old_chain_len; +to: clusters_to_release = old_chain_len - new_chain_len; +fatxx_freechain() - call pfaxx directly to terminate, because cl_releasedir does not +set errno + +==================================================================== +Functional changes between in version 4.45a versus version 4.44 + pc_set_attributes - sets PEINVALIDPARMS attribute argument is invalid + po_write() - Set p_errno = PEIOERRORWRITE only if devio didn't set it + pc_grow_dir() - Set errno to PENOSPC if root directory fills up + fatxx_clgrow() - Added endless loop protection and errno check + on lower level fat calls. + fatxx_clrelease_dir() - Since it is always part of cleanup in + rtdrobj.c modified code to preserve errno, since the error that + cause the cleanup call to be made is the important one. + fatxx_freechain - Add range check on the initial cluster, + added endless loop protection + added length argument from caller + po_truncate - Calulate and pass max number of clusters to freechain. + po_open - Calulate and pass max number of clusters to freechain. + pc_rmnode - Passes ass max number of clusters to freechain. + pc_deltree - Handle cases where get_inode failed but errno != PENOENT + pc_deltree - Calulate and pass max number of clusters to pc_rmnode + pc_mv - Calulate and pass max number of clusters to pc_rmnode + pc_unlink - Calulate and pass max number of clusters to pc_rmnode + rtfs.h - Added changed prototype for fatxx_freechain() and pc_rmnode + fatxx_cl_truncate_dir - Added range check on the initial cluster + fatxx_cl_truncate_dir - Added endless loop protection. + fatxx_cl_truncate_dir - Added save previous errno since we are a + cleanup function + fatxx_get_chain - removed incorrect bad cluster detect code + po_open() - test _synch_file_ptrs return code + po_read() - test _synch_file_ptrs return code + synch_file_ptrs - return TRUE/FALSE and set errno if faxx fails + po_extend_file - test _synch_file_ptrs return code + pc_gm_name - Guard against endless loop + pc_gfirst - clear a maximum loop counter for gnext + pc_gnext - Fail if > 32768 calls to gnext, infinite loop + po_extend_file - Added range check when scanning to end of file + po_truncate - added endless loop protection + pc_free_all_blk - put endless loop stop in do loop + pc_release_blk - put endless loop stop in do loop + pc_find_blk - put endless loop stop in do loop + pc_allocate_blk - endless loop stop in do loop + pc_flush_fat_blocks - put endless loop stop in do loop + pc_map_fat_block - put endless loop stop in do loop + pc_find_fat_blk - put endless loop stop in do loop + pc_sort_committed_blocks - put endless loop stop in do loop + pc_get_mom - add range check pc_finode_cluster(pdrive,pdotdot->finode) + pc_firstblock - must range check cluster values + pc_l_next_block - range check on cluster values + pc_scani - put endless loop stop + pc_free_all_i - put endless loop stop +==================================================================== +Functional changes between in version 4.44 versus version 4.43 +rtdrobj.c - pc_l_next_block() - Fixed error recovery code that was continueing after an +error. +csascii.c, csunicode.c and csjis.c - changed pc_malias() so that long file +names with 8 characters or less use their own name as an alias. +apimkdir.c - pc_rmdir() fixed error handling code to not overwrite lower +layers. +apimkdir.c - pc_rmdir() check retrun code when we use pc_get_inode to +scan the directory for empty. If errno not equal PENOENT tha we have a +problem. +apirealt.c - pc_get_file_extents() Fixed minor error handling problem +==================================================================== +Functional changes between in version 4.43 versus version 4.42 +rtvfat.c - pc_parsepath() check string lengths of path and file +rtnvfat.c - pc_parsepath() check string length of path. File is already +tested within pc_parsefile(). +apigfrst.c - pc_gfirst() - set errno to 0 and return null if directory is +valid but entry not found as reported by pc_findin setting errno to PENOENT. +apigfrst.c - pc_gnext() - set errno to 0 and return null if directory is +valid but entry not found as reported by pc_get_inode setting errno to +PENOENT. +csunicode.c - changed VFAT validate_filename() to check for reserved words +csascii.c - changed VFAT validate_filename() to check for reserved words +csjis.c - changed VFAT validate_filename() to check for reserved words +csascii.c - changed NVFAT validate_filename() to check for reserved words +csjis.c - changed NVFAT validate_filename() to check for reserved words +csunicode.c - changed VFAT validate_filename() to check for reserved words +rtvfat.c - moved name_is_reserved() to rtutil.c since it is now used by +vfat and non vfat systems. +rtutil.c - moved name_is_reserved() to rtutil.c since it is now used by +vfat and non vfat systems. +csstrtab.c - USTRING_SYS_BADALIAS. USTRING_SYS_LCRESERVED_NAMES and +USTRING_SYS_UCRESERVED_NAMES are stored as 8 bit ascii. Even when using +Unicode. These strings are always looked at as strings of 8 bit data +drflsemu.c - Removed. Put simplified emulator in drflsmtd.c +drflsftl.c - Simplified interface to MTS'd removed extraneous code and +structures. + +==================================================================== + +==================================================================== +Functional changes between in version 4.42 versus version 4.41 +apifilmv.c - Errno handling fix +apigfrst.c - Errno handling fix in pc_gdone +==================================================================== +==================================================================== +Functional changes between in version 4.41 versus version 4.4 +csjistab.c - Fixed Declare tables constant +drfloppy.c - Declare fl_errors[] constant +apiwrite.c - pc_set_attributes: Fail if trying to set bits (0x80|0x40) +drfloppy.c - Declare fl_errors constant +==================================================================== +Functional changes between in version 4.4 versus version 4.3. +prblock.c - + A new function named pc_free_all_fat_blocks() has been created, this +routine clears the fat cache. It is called by pc_dskfree() to release +cached fat data from a closing mount. For consistency the routine +pc_initialize_fat_block_pool now calls pc_free_all_fat_blocks() to +initialized the fat buffer pool once the configuration values have been +stored. +prblock.c - Improved error handling +apideltr.c - Improved error handling +apifilio.c - Improved error handling +apifilio.c - Modified pc_enum_file to not trigger errno when scanning all +files. +apifilmv.c - Improved error handling +apifrmat.c - Improved error handling +apigetwd.c - Improved error handling +apigfrst.c - Improved error handling +apiinfo.c - Improved error handling +apiinit.c - Don't print device driver name in autoformat loop if + STORE_DEVICE_NAMES_IN_DRIVE_STRUCT is disabled. +apimkdir.c - Improved error handling +apimkdir.c - Fixed bug in pc_rmdir() that was allowing removal of the +"." and ".." directory entries. +apirealt.c - Improved error handling +apisetwd.c - Improved error handling +apistat.c - Improved error handling +apiwrite.c - Improved error handling +csstrtab.c - Declare strings constant. +csstrtab.c - Eliminate MAP_UNICODE strings. Use the L modifier instead to +declare the text as UTF 16. +csjistab.c - Declare tables constant +rtkernfn.c - Declare med_st[] critical error table constant +rtkernfn.c - Set errno when out of resources +csunicode.c- Changed incorrect definition of BYTE_HIGH and BYTE_LOW +csunicode.c- Removed unicode_convert_strtable() call that was using malloc +csunicode.c- Fixed bug in unicode_make_printable() that caused an endless +loop +rtdevio.c - Remove check for dirty files before status check. Wasting bandwidth +rtdevio.c - Improved error handling +rtdrobj.c - Improved error handling +rtfat32.c - Improved error handling +rtfatxx.c - Improved error handling +rtfatxx.c - Fixed bug in fatxx_alloc_chain() and fatxx_clalloc() that was +not finding the last cluster. +rtfs.h - Added new errnos, changed drive_flags to a dword, added READ_ONLY +flag and note that high 8 bits are for private driver use. +rtlowl.c - Improved error handling +rtlowl.c - Add call to pc_free_all_fat_blocks() of disk close +rtnvfat.c - Improved error handling +rtvfat.c - Improved error handling +==================================================================== +Functional changes between in version 4.3 versus version 4.2. + +apifileio.c - Some errno handling changes have been made in po_lseek() +and po_open(). +apigfrst.c - pc_gnext() has been modified to check the DSTAT arguments to +avoid a crash if the input arguments are invalid. +apimkdir.c - pc_rmdir() now checks if a subdirectory is read only before deleting. +csstrtab.c - The string table is declared as KS_CONSTANT. The file has also +been modified to allow conditional exclusion of strings. To preserve memory +you may exclude strings from the string table if the corresponding module is + not being used. Set the definition in this table to zero if you wish to +exclude strings from the from the corresponding module. + +#define INCLUDE_CHKDSK_STRINGS 1 +#define INCLUDE_FLDRVER_STRINGS 1 +#define INCLUDE_FLASHDRV_STRINGS 1 +#define INCLUDE_FLASHEMU_STRINGS 1 +#define INCLUDE_RTFSINIT_STRINGS 1 +#define INCLUDE_IDEDRV_STRINGS 1 +#define INCLUDE_PORTMAIN_STRINGS 1 +#define INCLUDE_PORTKERN_STRINGS 1 +#define INCLUDE_RTFSDEM_STRINGS 1 /* For APIRGRS.C */ +#define INCLUDE_TSTSH_STRINGS 1 + +rtdrobj.c - Added routine pc_free_all_drobj() that releases all drobj +structures associated with a drive. Also made certain the drobj->pdrive was +initialized properly imediately after all drobj allocations. + +rtfs.h - +Eliminated spurious declaration: LFNRECORD s; +modified rtfs_system_user structure to include: dword rtfs_driver_errno +Added prototypes for: + void pc_free_all_drobj( DDRIVE *pdrive); + void rtfs_set_driver_errno(dword error); + dword rtfs_get_driver_errno(void); +rtkernfn.c - +Implemented two new functions + void rtfs_set_driver_errno(dword error); + dword rtfs_get_driver_errno(void); + +rtlowl.c - +Started adding calls to set_errno() for failures to read BPB and FAT. These +will have no effect until the upper level calls are changed to not overwrite +these values. This work will be completed in version 4.3. +In pc_dskfree() added a call to pc_free_all_drobj() to release drobj +structures not associated with files user current working directory. + +rtvfat.c - Fixed a bug in pc_parsepath() that caused problems if a path +name contained colon characters. + +Many other files were changed but the changes were not substantive. +THe files were simply editted to replace C++ style comments with C +comments. + +==================================================================== +Functional changes between in version 4.2 versus version 4.1. + pc_gfirst/pc_gnext always updates the DSTAT->lfname field. With the + short file name if necessary + fix vfat bug causing lost files in Unicode at sector boundaries. +apicnfig.c - Minor fixes to comments +apigfrst.c - Modified pc_upstat() to put the short file name in statobj->lfname + if there is no lfn. +appcmdsh.c - Set INCLUDE_STRESS_TESTS 0 as default +prblock.c - define INCLUDE_FAILSAFE_CODE as 0, disables failsafe callbacks + make pc_commit_fat_table() and pc_sort_committed_blocks() + not static +Prfailsf.c - Removed, failsafe mode is not implemened yet. +rtfs.h - Change DSTAT structure dimensions of filename[] and pname[] fixed +rtvfat.c - Change text2lfi() and routines that call it to fix vfat bug + causing lost files in Unicode at sector boundaries. +==================================================================== +Functional changes between in version 4.1 versus version 4.0. + pc_mv() - Can now move directories as well as files + Added changes to support ATA contiguous IO mode + Fixed a bug caused blocks to be freed twice on a disk abort +apifilio.c - Changed calling sequence to pc_mknode because of new argument +apifilmv.c - Modified pc_mv() to move directories as well as files. +apiinit.c - Added changes to support ATA contiguous IO mode +apimkdir.c - Changed calling sequence to pc_mknode because of new argument +drideata.c - Added changes to support ATA contiguous IO mode +portio.c - Added changes to support ATA contiguous IO mode +prblock.c - Fixed a bug that allowed blocks to be freed twice on a disk abort +rtdrobj.c - MOdified pc_mknode() so it can be used in pc_mv() +rtfs.h - Changed some prototypes to support changes to pc_mv and ATA + contiguous IO mode. + + + +April 29 + +Notes: + + +1. Endless loop checking in chains, looks pretty easy. Looks like find_in, +fndnode are the only ones. next block already has error handling so it should + be smooth. + +2. Need to change 32768 to a constant so when we decide MAX we are set +Usage by file and line number +File APIDELTR.C: +198 ret_val = pc_rmnode(pchild, 0, 32768L); MAXCLUSTERSINDIR +219 ret_val = pc_rmnode(pobj, 0, 32768L); MAXCLUSTERSINDIR +File APIMKDIR.C: +241 ret_val = pc_rmnode(pobj, 0, 32768L); MAXCLUSTERSINDIR +File APIGETWD.C: +167 if (++range_check >= 32768L) - Clusters +File APIGFRST.C: +169 Thats 32768 (max number of clusters in a directory) times + if (statobj->gnext_loop_count++ >= 0x2000000) +File RTDROBJ.C: +1084 if (++range_check >= 32768L) MAXFINODES +1182 if (++range_check >= 32768L) MAXFINODES +File PRBLOCK.C: +330 if (++range_check >= 32768L) MAXBLOCKS +337 if (++range_check >= 32768L) MAXBLOCKS +428 if (++range_check >= 32768L)MAXBLOCKS +473 if (++range_check >= 32768L)MAXBLOCKS +497 if (++range_check >= 32768L)MAXBLOCKS +585 if (++range_check >= 32768L) MAXFATBLOCKS +609 if (++range_check >= 32768L) +718 if (++range_check >= 32768L)MAXFATBLOCKS +748 if (++range_check >= 32768L)MAXFATBLOCKS +779 if (++range_check >= 32768L)MAXFATBLOCKS +904 if (++range_check >= 32768L)MAXFATBLOCKS +975 if (++range_check >= 32768L)MAXFATBLOCKS +987 if (++range_check >= 32768L)MAXFATBLOCKS +File RTFATXX.C: +321 while (nextcluster != 0xffffffff && ++range_check < 32768L) MAXCLUSTERSINDIR +330 if (range_check == 32768L) MAXCLUSTERSINDIR +518 while (nextcluster != 0xffffffff && ++range_check < 32768L)MAXCLUSTERSINDIR +530 if (!fatxx_freechain(pdr, l_cluster, 0, 32768L))MAXCLUSTERSINDIR + + + +3. Need to solve append/lseek on file with last cluster broken. + +4. Need routine to break cluster chains. + +A. POSITION - + 0 =='s DOSINODE + 1 =='s FIRST +-1 == LAST +-2 == LAST -1 +-3 == LAST -3 etc +B. OPTION + 1 == INVALID 0 + 2 == INVALID 1 + 3 == INVALID >MAX + 4 == LOOP ARG 1 to ARG 2 +Returns: &clno, &oldvalue + + +3. Need regression test to test for + + Cluster chain larger then file size + Cluster chain smaller then file size + Cluster chain contains 0,1, > maxfindex + Finode->cluster chain contains 0,1, > maxfindex + Cluster chain loops + + +BOOLEAN pc_deltree(byte *name) +int pc_enumerate( /* __apifn__ */ +PCFD po_open(byte *name, word flag, word mode) +int po_read(PCFD fd, byte *buf, int count) +long po_lseek(PCFD fd, long offset, int origin) +int po_close(PCFD fd) +BOOLEAN pc_mv(byte *old_name, byte *new_name) +BOOLEAN pc_unlink(byte *name) +BOOLEAN pc_mkdir(byte *name) +BOOLEAN pc_rmdir(byte *name) +BOOLEAN pc_pwd(byte *drive, byte *path) +BOOLEAN pc_gfirst(DSTAT *statobj, byte *name) +BOOLEAN pc_gnext(DSTAT *statobj) +void pc_gdone(DSTAT *statobj) +BOOLEAN pc_set_cwd(byte *name) +BOOLEAN pc_isdir(byte *path) +BOOLEAN pc_isvol(byte *path) +BOOLEAN pc_get_attributes(byte *path, byte *p_return) +int po_write(PCFD fd, byte *buf, int count) +BOOLEAN po_truncate(PCFD fd, long offset) +BOOLEAN po_flush(PCFD fd) +BOOLEAN pc_set_attributes(byte *path, byte attributes) +long po_extend_file(PCFD fd, long n_bytes, long start_cluster, int method) +int po_chsize(PCFD fd, long offset) +int pc_get_file_extents(PCFD fd, int infolistsize, FILESEGINFO *plist, BOOLEAN raw) +int pc_get_free_list(int driveno, int listsize, FREELISTINFO *plist, long threshhold) +int pc_fstat(PCFD fd, STAT *pstat) +int pc_stat(byte *name, STAT *pstat) + + +#define MC_ZERO 0 +#define MC_ONE 1 +#define MC_MAX 2 +#define MC_LOOP 3 +#define MC_SET 4 +#define MC_GET 5 + +/* manipulate_cluster_chain +Arguments: +DROBJ *pobj - Directory entry to break +int action - what action to take + MC_ZERO Set the cluster at pos_1 to 0. Ignore pos_2 argument + MC_ONE Set the cluster at pos_1 to 1. Ignore pos_2 argument + MC_MAX Set the cluster at pos_1 to > maxfindex. Ignore pos_2 argument + MC_LOOP link the cluster at pos_1 to pos_2 (create a loop) + MC_SET Put *value into the cluster + MC_GET Load *cluster and *value from the entry specified in pos_1 +long pos_1 - cluster position in the chain + 0 == The directory entry's cluster number + 1 == The first cluster in the chain + 2 == The second cluster in the chain + 3 == Third.. etc +-1 == The last cluster in the chain +-2 == The last cluster in the chain -2 +-3 == The last cluster in the chain -3 etc +long pos_2- cluster position in the chain to loop to if action == 4 + Same arguments as pos_1 but 0 is illegal +dword *cluster - the cluster index specified by pos_1 is put into *cluster +dword *value - the value at *cluster is stored here +*/ + + +Note: write routine to just change size field (PRO) +write routine to return return actual size of cluster size (PRO) +write routine to delete clusters beyond end of file +add flags to open +1. flush dirent when file size changes +2. flush fat when cluster allocated + + +manipulate_cluster_chain(DROBJ *pobj, int action,long pos_1, long pos2, dword *cluster, dword *value) +{ + +} + + +BOOLEAN populate_directory( + + + + +int dochsize(int agc, byte **agv) /*__fn__*/ +{ + long l; + long newsize; + dword cluster; + DROBJ *pobj; + + if (agc == 2) + { + pobj = pc_fndnode(*agv); + if (pobj) + { + /* cal pc_finode_stat() to update the stat structure */ + newsize = rtfs_atol( *(agv+1) ); + if (newsize == 99) + { + cluster = pc_finode_cluster(pobj->pdrive,pobj->finode); + while (cluster && cluster != 0xffffffff) + { + printf("%d->", cluster); + cluster = FATOP(pobj->pdrive)->fatop_clnext(pobj->pdrive, cluster); + } + printf("\n"); + pc_freeobj(pobj); + return(0); + } + + cluster = pc_finode_cluster(pobj->pdrive,pobj->finode); + for (l = 1 ; l < newsize; l++) + { + cluster = + FATOP(pobj->pdrive)->fatop_clnext(pobj->pdrive, cluster); + } + if (cluster && cluster != 0xffffffff) + { + FATOP(pobj->pdrive)->fatop_pfaxx(pobj->pdrive, cluster, BADVALUE); + } + FATOP(pobj->pdrive)->fatop_flushfat(pobj->pdrive->driveno); + pc_freeobj(pobj); + + } + return(0); + } + else + { + printf("CHSIZE path, # clusters\n"); + return(0); + } +} diff --git a/build/libraries/fatfs/ARM7/rtdevio.c b/build/libraries/fatfs/ARM7/rtdevio.c new file mode 100644 index 0000000..08ee52a --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtdevio.c @@ -0,0 +1,533 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* rtdevio.c - Media check functions and device io wrapper functions +* +* +*/ +#include + +static BOOLEAN check_media_entry(int driveno); +static BOOLEAN check_media_io(DDRIVE *pdr, BOOLEAN raw); +static BOOLEAN check_media(DDRIVE *pdr, BOOLEAN ok_to_automount, BOOLEAN raw_access_requested, BOOLEAN call_crit_err); +static int card_failed_handler(DDRIVE *pdr); + +/* Check if a drive id is valid and mount the drive. This is an internal + routine that is called by other api calls + If it fails, return -1 with the drive not claimed. + If it succeeds return the drive number with the drive claimed. + The caller will call release_drive_mount(driveno) to release it +*/ + +int check_drive_name_mount(byte *name) /*__fn__*/ +{ +int driveno; + /* Get the drive and make sure it is mounted */ + if (pc_parsedrive( &driveno, name)) + { + OS_CLAIM_LOGDRIVE(driveno) /* check_drive_name_mount Register drive in use */ + if (!check_media_entry(driveno)) + { + OS_RELEASE_LOGDRIVE(driveno) /* check_drive_name_mount release */ + return(-1); + } + return(driveno); + } + else + { + rtfs_set_errno(PEINVALIDDRIVEID); /* Check drive called with bad drive name */ + return(-1); + } +} + +/* Check if a drive id is valid and mount the drive. This is an internal + routine that is called by other api calls + If it fails, return -1 with the drive not claimed. + If it succeeds return the drive number with the drive claimed. + The caller will call release_drive_mount(driveno) to release it +*/ + +BOOLEAN check_drive_number_mount(int driveno) /*__fn__*/ +{ + if (!pc_validate_driveno(driveno)) + { + rtfs_set_errno(PEINVALIDDRIVEID); /* Check drive called with bad drive name */ + return(FALSE); + } + else + { + /* Get the drive and make sure it is mounted */ + OS_CLAIM_LOGDRIVE(driveno) /* check_drive_number_mount Register drive in use */ + if (!check_media_entry(driveno)) + { + OS_RELEASE_LOGDRIVE(driveno) /* check_drive_number_mount release */ + return(FALSE); + } + return(TRUE); + } +} + +/* Release a drive that was claimed by a succesful call to + check_drive_name_mount, or check_drive_number_mount + If the the operation queued an abort request then + free the drive an all it's structures +*/ + +void release_drive_mount(int driveno) +{ +DDRIVE *pdr; + pdr = pc_drno_to_drive_struct(driveno); + if (pdr && pdr->mount_abort) + { + pc_dskfree(driveno); + } + OS_RELEASE_LOGDRIVE(driveno) /* release_drive_mount release */ +} + +BOOLEAN release_drive_mount_write(int driveno) +{ +DDRIVE *pdr; +BOOLEAN ret_val; + + ret_val = TRUE; + pdr = pc_drno_to_drive_struct(driveno); + if (pdr) + { + if (pdr->mount_abort) + pc_dskfree(driveno); +#if (INCLUDE_FAILSAFE_CODE) /* Call failsafe autocommit */ + else + ret_val = pro_failsafe_autocommit(pdr); +#endif + } + OS_RELEASE_LOGDRIVE(driveno) /* release_drive_mount release */ + return(ret_val); +} + + +/* BOOLEAN check_drive_number_present(int driveno) +* +* Called from the apps layer when formatting +* Ignore and clear DISKCHANGED status +* Return TRUE if the device is UP. +* +* Inputs: +* int driveno; Drive number 0 .. prtfs_cfg->cfg_NDRIVES +* +* Outputs: +* TRUE if is is okay to proceed with IO +* FALSE If we can not proceed with IO +* +* +*/ + +BOOLEAN check_drive_number_present(int driveno) /*__fn__*/ +{ + int media_status; + DDRIVE *pdr; + + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr || !(pdr->drive_flags&DRIVE_FLAGS_VALID) ) + { + rtfs_set_errno(PEDEVICEINIT); /* device not initialized in pc_ertfs_init().*/ + return(FALSE); + } + media_status = DEVTEST_NOCHANGE; + if (pdr->drive_flags & DRIVE_FLAGS_REMOVABLE) + { + media_status = pdr->dev_table_perform_device_ioctl(driveno, DEVCTL_CHECKSTATUS, 0); + /* Call it again to clear media change */ + if (media_status == DEVTEST_CHANGED) + media_status = pdr->dev_table_perform_device_ioctl(driveno, DEVCTL_CHECKSTATUS, 0); + } + if (media_status == DEVTEST_NOCHANGE) + return(TRUE); + else if (media_status == DEVTEST_NOMEDIA) + rtfs_set_errno(PEDEVICENOMEDIA); + else if (media_status == DEVTEST_UNKMEDIA) + rtfs_set_errno(PEDEVICEUNKNOWNMEDIA); + else + rtfs_set_errno(PEDEVICEFAILURE); + /* Queue the drive for disk free and remount */ + if (pdr->mount_valid) + pdr->mount_abort = TRUE; + return(FALSE); +} + +/* BOOLEAN check_media_entry(int driveno) +* +* Called from the top of the API +* Return TRUE if the drive is mounted and it is okay to proceed with IO on +* a drive +* +* This routine is called from the top of the io layer. If the drive is +* Not mounted it may be (re)mounted as a result of this call and the call +* will return TRUE (it is okay to proceed). A similar call, check_media_io(), +* is called from a lower layer. It will succeed if raw IO is requested +* and the device is ok or if the device is mounted and ok +* +* Note: The the logical drive must be claimed before this routine is +* called and later released by the caller. +* +* Inputs: +* int driveno; Drive number 0 .. prtfs_cfg->cfg_NDRIVES +* +* Outputs: +* TRUE if is mounted and it is okay to proceed with IO +* FALSE if the drive ca not be used and the user did not +* replace it with a usable disk +* +* +*/ +static BOOLEAN check_media_entry(int driveno) /*__fn__*/ +{ + DDRIVE *pdr; + /* Call media check + If dev is OK and mounted proceed + If dev is OK and not mounted try to mount. + If dev is changed and mounted but buffers are not dirty then + unmount and remount. + If dev is changed and mounted but buffers are dirty then unmount and fail + */ + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr || !(pdr->drive_flags&DRIVE_FLAGS_VALID) ) + { + rtfs_set_errno(PEDEVICEINIT); /* device not initialized in pc_ertfs_init().*/ + return(FALSE); + } + return(check_media(pdr, TRUE, FALSE, TRUE)); +} + + +/* BOOLEAN check_media_io(DDRIVE *pdr, BOOLEAN raw) +* +* Called from the IO layer +* Return TRUE if the device is UP and either raw IO is requested +* or the drive is mounted. +* +* Note: The the logical drive must be claimed before this routine is +* called and later released by the caller. +* +* Inputs: +* int driveno; Drive number 0 .. prtfs_cfg->cfg_NDRIVES +* +* Outputs: +* TRUE if is is okay to proceed with IO +* FALSE If we ca not proceed with IO +* +* +*/ + +static BOOLEAN check_media_io(DDRIVE *pdr, BOOLEAN raw) /*__fn__*/ +{ + /* Call media check + If dev is OK and mounted or raw proceed + If dev is changed and not mounted and raw then proceed + If dev is changed and mounted then unmount and fail + */ + if (!pdr || !(pdr->drive_flags&DRIVE_FLAGS_VALID) ) + { + rtfs_set_errno(PEDEVICEINIT); /* device not initialized in pc_ertfs_init().*/ + return(FALSE); + } + return(check_media(pdr, FALSE, raw, TRUE)); +} + +/* BOOLEAN check_media(DDRIVE *pdr, BOOLEAN ok_to_automount, BOOLEAN raw_access_requested, BOOLEAN call_crit_err) +* +* Return TRUE is it is okay to proceed with IO on a drive +* +* Note: The the logical drive must be claimed before this routine is +* called and later released by the caller. +** +* Inputs: +* int driveno; Drive number 0 .. prtfs_cfg->cfg_NDRIVES +* BOOLEAN ok_to_automount Automount the drive +* BOOLEAN raw_access_requested Mount not needed but device must +* be functional +* BOOLEAN call_crit_err If true call error handler +*/ + +static BOOLEAN check_media(DDRIVE *pdr, BOOLEAN ok_to_automount, BOOLEAN raw_access_requested, BOOLEAN call_crit_err) /*__fn__*/ +{ + int media_status; + int crit_err_media_status; + int drive_is_dirty; + + /* If an abort request is pending, executed it */ + if (ok_to_automount && pdr->mount_valid && pdr->mount_abort) + { + pc_dskfree(pdr->driveno); + } + + /* If the device is removable check status to see if we must recover or remount */ + if (pdr->drive_flags & DRIVE_FLAGS_REMOVABLE) + media_status = pdr->dev_table_perform_device_ioctl(pdr->driveno, DEVCTL_CHECKSTATUS, 0); + else + media_status = 0; + + crit_err_media_status = 0; + + if (media_status==0) /* Device is up */ + { + if (pdr->mount_valid) /* already mounted */ + { + goto status_is_ok; + } + else + { + if (raw_access_requested) /* Mount not required */ + goto status_is_ok; + else if (ok_to_automount) + { + /* Mount the drive if we can - + . Read partition table if required + . Read block zero + */ + if (pc_i_dskopen(pdr->driveno)) + { +#if (INCLUDE_FAILSAFE_CODE) /* Call failsafe autorestore and open routines */ + if (!pro_failsafe_autorestore(pdr) || + !pro_failsafe_dskopen(pdr) ) + { /* Failsafe set errno */ + crit_err_media_status = 0; + goto status_is_bad; + } +#endif + goto status_is_ok; + } + else + { + /* Tell the user it is unformatted */ + /* pc_i_dskopen() sets errno */ + crit_err_media_status = CRERR_BAD_FORMAT; + goto status_is_bad; + } + } + else + { + /* The drive is not mounted. and ok_to_automount is false */ + rtfs_set_errno(PEDEVICECHANGED); + goto status_is_bad; + } + } + } + else if (media_status == DEVTEST_CHANGED) + { + drive_is_dirty = (pdr->mount_valid && (pdr->fat_is_dirty || pc_test_all_fil(pdr))); + /* If the drive is dirty the user must explicitly abort */ + if (drive_is_dirty) + { + rtfs_set_errno(PEDEVICECHANGED); + crit_err_media_status = CRERR_CHANGED_CARD; + goto status_is_bad; + } + else + { + /* If automount is requested then close the existing + mount and continue. */ + if (ok_to_automount) + { + pc_dskfree((int)pdr->driveno); + /* Mount the drive if we can */ + if (pc_i_dskopen(pdr->driveno)) + { +#if (INCLUDE_FAILSAFE_CODE) /* Call failsafe autorestore and open routines */ + if (!pro_failsafe_autorestore(pdr) || + !pro_failsafe_dskopen(pdr) ) + { /* Failsafe set errno */ + crit_err_media_status = 0; + goto status_is_bad; + } +#endif + goto status_is_ok; + } + else + { + /* pc_i_dskopen() sets errno */ + crit_err_media_status = CRERR_BAD_FORMAT; + goto status_is_bad; + } + } + else + { + rtfs_set_errno(PEDEVICECHANGED); + crit_err_media_status = CRERR_CHANGED_CARD; + goto status_is_bad; + } + } + } + else + { + if (media_status == DEVTEST_NOMEDIA) + { + rtfs_set_errno(PEDEVICENOMEDIA); + crit_err_media_status = CRERR_NO_CARD; + } + else if (media_status == DEVTEST_UNKMEDIA) + { + rtfs_set_errno(PEDEVICEUNKNOWNMEDIA); + crit_err_media_status = CRERR_BAD_CARD; + } + else + { + rtfs_set_errno(PEDEVICEFAILURE); + crit_err_media_status = CRERR_CARD_FAILURE; + } + goto status_is_bad; + } +status_is_ok: + return(TRUE); +status_is_bad: + if (crit_err_media_status) + { + if (call_crit_err) + critical_error_handler(pdr->driveno, crit_err_media_status, 0); + } + /* Queue the drive for disk free and remount */ + pdr->mount_abort = TRUE; + return(FALSE); +} + + + +static int card_failed_handler(DDRIVE *pdr) /* __fn__ */ +{ +int ret_val; + + ret_val = critical_error_handler(pdr->driveno,CRERR_CARD_FAILURE, 0); + + if (ret_val == CRITICAL_ERROR_ABORT) + { + /* Queue the drive for disk free and remount */ + if (pdr->mount_valid) + { + pdr->mount_abort = TRUE; + } + } + return(ret_val); +} +/* +* Note: The the logical drive must be claimed before this routine is +* called and later released by the caller. +*/ +BOOLEAN devio_read(int driveno, dword blockno, byte * buf, word n_to_read, BOOLEAN raw) /* __fn__ */ +{ +DDRIVE *pdr; + + pdr = pc_drno_to_drive_struct(driveno); + +/* Bug Fix. 1-28-02 thanks to Kerry Krouse. This code was inside the + loop, which caused retries to fail +*/ + if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED) + { + if (!raw) + blockno += pdr->partition_base; + } + + for(;;) + { + /* Check if disk is accessable and has not changed since mounted */ + if (!(pdr->drive_flags & DRIVE_FLAGS_VALID)) + return(FALSE); + if (!check_media_io(pdr, raw)) + return(FALSE); + + if (pdr->dev_table_drive_io(driveno, blockno, buf, n_to_read, TRUE)) + { + return(TRUE); + } + else + { + if (card_failed_handler(pdr) != CRITICAL_ERROR_RETRY) + break; + } + } + return(FALSE); +} + +#if (RTFS_WRITE) + +/* This is a special version of devio_write that is used by the format utility + It is the same as devio_write except that it does not automount the + drive or load the partition table. If non-raw IO is requested the + caller must first be sure that the partition table is loaded +*/ +/* +* Note: The the logical drive must be claimed before this routine is +* called and later released by the caller. +*/ + +BOOLEAN devio_write_format(int driveno, dword blockno, byte * buf, word n_to_write, BOOLEAN raw) /* __fn__ */ +{ +DDRIVE *pdr; + + pdr = pc_drno_to_drive_struct(driveno); + if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED) + { + if (!raw) + blockno += pdr->partition_base; + } + + for (;;) + { + /* Check if disk is accessable and has not changed since mounted */ + if (!check_media_io(pdr, TRUE)) + return(FALSE); + + if (pdr->dev_table_drive_io(driveno, blockno, buf, n_to_write, FALSE)) + { + return(TRUE); + } + else + { + if (card_failed_handler(pdr) != CRITICAL_ERROR_RETRY) + break; + } + } + rtfs_set_errno(PEIOERRORWRITE);/* devio_write_format: write failed */ + return(FALSE); +} +/* +* Note: The the logical drive must be claimed before this routine is +* called and later released by the caller. +*/ + +BOOLEAN devio_write(int driveno, dword blockno, byte * buf, word n_to_write, BOOLEAN raw) /* __fn__ */ +{ +DDRIVE *pdr; + + pdr = pc_drno_to_drive_struct(driveno); + if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED) + { + if (!raw) + blockno += pdr->partition_base; + } + + for (;;) + { + /* Check if disk is accessable and has not changed since mounted */ + if (!check_media_io(pdr, raw)) + return(FALSE); + + if (pdr->dev_table_drive_io(driveno, blockno, buf, n_to_write, FALSE)) + { + return(TRUE); + } + else + { + if (card_failed_handler(pdr) != CRITICAL_ERROR_RETRY) + break; + } + } + return(FALSE); +} + +#endif + diff --git a/build/libraries/fatfs/ARM7/rtdrobj.c b/build/libraries/fatfs/ARM7/rtdrobj.c new file mode 100644 index 0000000..3bb8051 --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtdrobj.c @@ -0,0 +1,1424 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* RTDROBJ.C - Directory object manipulation routines + pc_get_cwd - Get the current working directory DROBJ for a drive, + pc_fndnode - Find a file or directory on disk and return a DROBJ. + pc_get_inode - Find a filename within a subdirectory + static pc_findin - Find a filename in the same directory as argument. + + pc_get_mom - Find the parent inode of a subdirectory. + + pc_mkchild - Allocate a DROBJ and fill in based on parent object. + pc_mknode - Create an empty subdirectory or file. + pc_insert_inode - Called only by pc_mknode + + pc_rmnode - Delete an inode unconditionally. + pc_update_inode - Flush an inode to disk + + pc_read_obj - Assign an initialized BLKBUFF to a DROBJ + pc_write_obj - Write a DROBJ s BLKBUFF to disk + + pc_get_root - Create the special ROOT object for a drive. + pc_firstblock - Return the absolute block number of a directory + pc_next_block - Calculate the next block owned by an object. + pc_l_next_block - Calculate the next block in a chain. + + We keep a shared image of directory entries around. These routines + keep track of them for us. + pc_marki - Set dr:sec:index, + stitch FINODE into the inode list + pc_scani - Search for an inode in the internal inode list. + Heap management routines + + pc_allocobj - + pc_alloci - + pc_free_all_i - + pc_freei - + pc_freeobj - + + Simple helper routines + pc_dos2inode - + pc_ino2dos - + pc_init_inode - + pc_isadir - + +*/ + +#include + + +/*************************************************************************** + PC_GET_CWD - Get the current working directory for a drive, + + Description + Return the current directory inode for the drive represented by ddrive. + +***************************************************************************/ + +/* Get the current working directory and copy it into pobj */ +DROBJ *pc_get_cwd(DDRIVE *pdrive) /*__fn__*/ +{ + DROBJ *pcwd; + DROBJ *pobj; + + pcwd = (DROBJ *)(rtfs_get_system_user()->lcwd[pdrive->driveno]); + + /* If no current working dir set it to the root */ + if (!pcwd) + { + pcwd = pc_get_root(pdrive); + rtfs_get_system_user()->lcwd[pdrive->driveno] = pcwd; + } + + if (pcwd) + { + pobj = pc_allocobj(); + if (!pobj) + { + return (0); + } + /* Free the inode that comes with allocobj */ + pc_freei(pobj->finode); + OS_CLAIM_FSCRITICAL() + copybuff(pobj, pcwd, sizeof(DROBJ)); + pobj->finode->opencount += 1; + OS_RELEASE_FSCRITICAL() + return (pobj); + } + else /* If no cwd is set error */ + { + rtfs_set_errno(PEINTERNAL); + return(0); + } +} + + + /************************************************************************** + PC_FNDNODE - Find a file or directory on disk and return a DROBJ. + + Description + Take a full path name and traverse the path until we get to the file + or subdir at the end of the path spec. When found allocate and init- + ialize (OPEN) a DROBJ. + + Returns + Returns a pointer to a DROBJ if the file was found, otherwise 0. + +***************************************************************************/ + + +/* Find path and create a DROBJ structure if found */ +DROBJ *pc_fndnode(byte *path) /*__fn__*/ +{ + DROBJ *pobj; + DROBJ *pmom; + DROBJ *pchild; + int driveno; + DDRIVE *pdrive; + byte *filename; + byte fileext[4]; + byte *pf0,*pf1; + BLKBUFF *scratch; + + + /* Get past D: plust get drive number if there */ + path = pc_parsedrive( &driveno, path ); + if (!path) + { + rtfs_set_errno(PEINVALIDDRIVEID); /* pc_fndnode: parsedrive failed */ + return (0); + } + /* Find the drive */ + pdrive = pc_drno2dr(driveno); + if (!pdrive) + { + rtfs_set_errno(PEINVALIDDRIVEID); /* pc_fndnode: pc_drno2dr failed */ + return (0); + } + /* Get the top of the current path */ + if (CS_OP_CMP_ASCII(path, '\\')) + { + CS_OP_INC_PTR(path); + pobj = pc_get_root(pdrive); + } + else + { + pobj = pc_get_cwd(pdrive); + } + + if (!pobj) + return (0); + scratch = pc_scratch_blk(); + if (!scratch) + return(0); + filename = (byte *)scratch->data; + + /* Search through the path til exausted */ + while (CS_OP_IS_NOT_EOS(path)) + { + path = pc_nibbleparse(filename,fileext, path ); + if (!path) + { + pc_free_scratch_blk(scratch); + rtfs_set_errno(PENOENT); /* pc_fndnode: no match */ + pc_freeobj(pobj); + return (0); + } +#if (RTFS_SUBDIRS) + pf1 = pf0 = filename; + CS_OP_INC_PTR(pf1); +#if (VFAT) + if (CS_OP_CMP_ASCII(pf0,'.') && CS_OP_IS_EOS(pf1)) /* DOT */ + ; +#else + /* Tomo */ + if (CS_OP_CMP_ASCII(pf0,'.') && CS_OP_CMP_ASCII(pf1,' ')) /* DOT-in NON-VFAT it is space filled */ + ; +#endif +#endif + else + { + /* Find Filename in pobj. and initialize lpobj with result */ + pchild = pc_get_inode(0, pobj, filename, fileext, GET_INODE_MATCH); + if (!pchild) + { /* get_inode set errno */ + pc_free_scratch_blk(scratch); + pc_freeobj(pobj); + return (0); + } +#if (RTFS_SUBDIRS) + /* We found it. We have one special case. if DOTDOT we need + to shift up a level so we are not the child of mom + but of grand mom. */ + pf1 = pf0 = filename; + CS_OP_INC_PTR(pf1); + if (CS_OP_CMP_ASCII(pf0,'.') && CS_OP_CMP_ASCII(pf1,'.')) + { + /* Find pobj s parent. By looking back from DOTDOT */ + pmom = pc_get_mom(pchild); + /* We are done with pobj for now */ + pc_freeobj(pobj); + + if (!pmom) + { /* Get mom set errno */ + pc_free_scratch_blk(scratch); + pc_freeobj(pchild); + return (0); + } + else + { + /* We found the parent now free the child */ + pobj = pmom; + pc_freeobj(pchild); + } + } + else +#endif /* SUBDIRS */ + { + /* We are done with pobj for now */ + pc_freeobj(pobj); + /* Make sure pobj points at the next inode */ + pobj = pchild; +#if (RTFS_SUBDIRS) +#else +/* No subdirectory support. Return the one we found */ + pc_free_scratch_blk(scratch); + return (pobj); +#endif + } + } + } + pc_free_scratch_blk(scratch); + return (pobj); +} + +/*************************************************************************** + PC_GET_INODE - Find a filename within a subdirectory + + Description + Search the directory pmom for the pattern or name in filename:ext and + return the an initialized object. If pobj is NULL start the search at + the top of pmom (getfirst) and allocate pobj before returning it. + Otherwise start the search at pobj (getnext). (see also pc_gfirst, + pc_gnext) + + + Note: Filename and ext must be right filled with spaces to 8 and 3 bytes + respectively. Null termination does not matter. + + Note the use of the action variable. This is used so we do not have to + use match patterns in the core code like *.* and .. + GET_INODE_MATCH Must match the pattern exactly + GET_INODE_WILD Pattern may contain wild cards + GET_INODE_STAR Like he passed *.* (pattern will be null) + GET_INODE_DOTDOT Like he past .. (pattern will be null + + + Returns + Returns a drobj pointer or NULL if file not found. +***************************************************************************/ + +/* Give a directory mom. And a file name and extension. + Find find the file or dir and initialize pobj. + If pobj is NULL. We allocate and initialize the object otherwise we get the + next item in the chain of dirents. +*/ +DROBJ *pc_get_inode( DROBJ *pobj, DROBJ *pmom, byte *filename, byte *fileext, int action) /*__fn__*/ +{ + BOOLEAN starting = FALSE; + /* Create the child if just starting */ + if (!pobj) + { + starting = TRUE; + pobj = pc_mkchild(pmom); /* If failure sets errno */ + if (!pobj) + return(0); + } + else /* If doing a gnext do not get stuck in and endless loop */ + { + if ( ++(pobj->blkinfo.my_index) >= INOPBLOCK ) + { + rtfs_set_errno(0); /* Clear errno to be safe */ + if (!pc_next_block(pobj)) /* Will set errno illegal block id encountered */ + { + if (!get_errno()) + rtfs_set_errno(PENOENT); /* pc_get_inode: end of dir */ + if (starting) + pc_freeobj(pobj); + return(0); + } + else + pobj->blkinfo.my_index = 0; + } + } + if (pc_findin(pobj, filename, fileext,action)) + { + return (pobj); + } + else + { + /* pc_findin set errno */ + if (starting) + pc_freeobj(pobj); + return (0); + } +} + +/************************************************************************** + PC_GET_MOM - Find the parent inode of a subdirectory. + + Description + Given a DROBJ initialized with the contents of a subdirectory s DOTDOT + entry, initialize a DROBJ which is the parent of the current directory. + + Returns + Returns a DROBJ pointer or NULL if could something went wrong. + + +****************************************************************************/ +#if (RTFS_SUBDIRS) + +/* +* Get mom: +* if (!dotodot->cluster) Mom is root. +* getroot() +* else cluster points to mom. +* find .. in mom +* then search through the directory pointed to by moms .. until +* you find mom. This will be current block startblock etc for mom. +*/ + +DROBJ *pc_get_mom(DROBJ *pdotdot) /*__fn__*/ +{ + DROBJ *pmom; + DDRIVE *pdrive = pdotdot->pdrive; + BLOCKT sectorno; + BLKBUFF *rbuf; + DIRBLK *pd; + DOSINODE *pi; + FINODE *pfi; + dword clno; + /* We have to be a subdir */ + if (!pc_isadir(pdotdot)) + { + rtfs_set_errno(PEINVALIDDIR); + return(0); + } + + /* If ..->cluster is zero then parent is root */ + if (!pc_finode_cluster(pdrive,pdotdot->finode)) + return(pc_get_root(pdrive)); + + /* Otherwise : cluster points to the beginning of our parent. + we also need the position of our parent in its parent */ + pmom = pc_allocobj(); + if (!pmom) + return (0); + + pmom->pdrive = pdrive; + /* Find .. in our parent s directory */ + clno = pc_finode_cluster(pdrive,pdotdot->finode); + if ((clno < 2) || (clno > pdrive->maxfindex) ) + { + pc_freeobj(pmom); + rtfs_set_errno(PEINVALIDCLUSTER); + return (0); + } + sectorno = pc_cl2sector(pdrive,clno); + /* We found .. in our parents dir. */ + pmom->pdrive = pdrive; + pmom->blkinfo.my_frstblock = sectorno; + pmom->blkinfo.my_block = sectorno; + pmom->blkinfo.my_index = 0; + pmom->isroot = FALSE; + pd = &pmom->blkinfo; + + pmom->pblkbuff = rbuf = pc_read_blk(pdrive, pmom->blkinfo.my_block); + if (rbuf) + { + pi = (DOSINODE *) &rbuf->data[0]; + OS_CLAIM_FSCRITICAL() + pc_dos2inode(pmom->finode , pi ); + OS_RELEASE_FSCRITICAL() + + pc_release_buf(rbuf); + + /* See if the inode is in the buffers */ + pfi = pc_scani(pdrive, sectorno, 0); + if (pfi) + { + pc_freei(pmom->finode); + pmom->finode = pfi; + } + else + { + pc_marki(pmom->finode , pmom->pdrive , pd->my_block, + pd->my_index); + } + return (pmom); + + } + else /* Error, something did not work */ + { + pc_freeobj(pmom); + return (0); + } +} +#endif + +/************************************************************************** + PC_MKCHILD - Allocate a DROBJ and fill in based on parent object. + + Description + Allocate an object and fill in as much of the the block pointer section + as possible based on the parent. + + Returns + Returns a partially initialized DROBJ if enough core available and + pmom was a valid subdirectory. + +****************************************************************************/ + +DROBJ *pc_mkchild( DROBJ *pmom) /*__fn__*/ +{ + DROBJ *pobj; + DIRBLK *pd; + + /* Mom must be a directory */ + if (!pc_isadir(pmom)) + { + rtfs_set_errno(PEINVALIDDIR); /* pc_mkchild: Internal error, parent dir provided */ + return(0); + } + /* init the object - */ + pobj = pc_allocobj(); + if (!pobj) + return (0); + + pd = &pobj->blkinfo; + + pobj->isroot = FALSE; /* Child can not be root */ + pobj->pdrive = pmom->pdrive; /* Child inherets moms drive */ + + /* Now initialize the fields storing where the child inode lives */ + pd->my_index = 0; + pd->my_block = pd->my_frstblock = pc_firstblock(pmom); + if (!pd->my_block) + { + pc_freeobj(pobj); + return (0); + } + + return (pobj); +} + +/************************************************************************** + PC_MKNODE - Create an empty subdirectory or file. + + Description + Creates a file or subdirectory inode depending on the flag values in + attributes. A pointer to an inode is returned for further processing. See + po_open(),po_close(), pc_mkdir() et al for examples. + + Note: After processing, the DROBJ must be released by calling pc_freeobj. + + Returns + Returns a pointer to a DROBJ structure for further use, or NULL if the + inode name already exists or path not found. +**************************************************************************/ + +/* Make a node from path and attribs create and fill in pobj */ +/* Note: the parent directory is locked before this routine is called */ +DROBJ *pc_mknode(DROBJ *pmom ,byte *filename, byte *fileext, byte attributes, CLUSTERTYPE incluster) /*__fn__*/ +{ + DROBJ *pobj; + BOOLEAN ret_val; + DOSINODE *pdinodes; + FINODE lfinode; + CLUSTERTYPE cluster; + CLUSTERTYPE cltemp; + DATESTR crdate; + BLKBUFF *pbuff; + DDRIVE *pdrive; + byte attr; +#if (RTFS_SUBDIRS) + byte dot_str[4]; /* Make DOT and DOTDOT strings */ + byte null_str[4]; +#endif + + ret_val = TRUE; + + if (!pmom || !pmom->pdrive) + { + rtfs_set_errno(PEINTERNAL); /* pc_mknode: Internal error*/ + return (0); + } + /* Make sure the file/directory name is legal */ + if (!validate_filename(filename, fileext)) + { + rtfs_set_errno(PEINVALIDPATH); + return (0); + } + + + pdrive = pmom->pdrive; + cluster = incluster; +#if (RTFS_SUBDIRS) + if (attributes & ADIRENT) + { + /*Unless renaming a directory we grab a cluster for a new dir and */ + /* clear it */ + if (!incluster) + { + /* pc_alloc_dir will set errno */ + cluster = pc_alloc_dir(pdrive,pmom); + if (!cluster) + ret_val = FALSE; + else if (!pc_clzero( pdrive , cluster ) ) + { + /* clzero will set errno */ + FATOP(pdrive)->fatop_clrelease_dir(pdrive , cluster); + ret_val = FALSE; + } + } + } +#endif + + if (!ret_val) + { + return (0); + } + + /* For a subdirectory. First make it a simple file. We will change the + attribute after all is clean */ + attr = attributes; +#if (RTFS_SUBDIRS) + if (attr & ADIRENT) + attr = ANORMAL; +#endif + /* Allocate an empty DROBJ and FINODE to hold the new file */ + + pobj = pc_allocobj(); + if (!pobj) + { + return (0); + } + /* Set up the drive link */ + pobj->pdrive = pmom->pdrive; + + /* Load the inode copy name,ext,attr,cluster, size,datetime */ + /* Convert pobj to native and stitch it in to mom */ + if (!pc_insert_inode(pobj , pmom, attr, cluster, filename, fileext)) + { + if (cluster && !incluster) + FATOP(pdrive)->fatop_clrelease_dir(pdrive , cluster); + pc_freeobj(pobj); + return (0); + } + +#if (RTFS_SUBDIRS) + /* Now if we are creating subdirectory we have to make the DOT and DOT DOT + inodes and then change pobj s attribute to ADIRENT + The DOT and DOTDOT are not buffered inodes. We are simply putting + the to disk */ + if ( attributes & ADIRENT) + { + if (incluster) + { + pbuff = pc_read_blk( pdrive , pc_cl2sector(pdrive , incluster)); + if (!pbuff) + { + pc_freeobj(pobj); + return (0); + } + } + else + { + /* Set up a buffer to do surgery */ + pbuff = pc_init_blk( pdrive , pc_cl2sector(pdrive , cluster)); + if (!pbuff) + { + pc_freeobj(pobj); + FATOP(pdrive)->fatop_clrelease_dir(pdrive , cluster); + return (0); + } + } + pdinodes = (DOSINODE *) &pbuff->data[0]; + /* Load DOT and DOTDOT in native form */ + /* DOT first. It points to the begining of this sector */ + dot_str[0] = CS_OP_ASCII('.');dot_str[1]=CS_OP_ASCII('\0'); + null_str[0] = CS_OP_ASCII('\0'); + + /* Load the time and date stamp to be used for "." and ".." + from the subdirectory that we are creating. In this way + the three directory entries will all have the same timestamp + value */ + crdate.time = pobj->finode->ftime; + crdate.date = pobj->finode->fdate; + pc_init_inode( &lfinode, dot_str,null_str, ADIRENT|ARCHIVE, + cluster, /*size*/ 0L , &crdate); + /* And to the buffer in intel form */ + pc_ino2dos (pdinodes, &lfinode); + /* Now DOTDOT points to mom s cluster */ + cltemp = pc_get_parent_cluster(pdrive, pobj); + dot_str[0] = CS_OP_ASCII('.');dot_str[1] = CS_OP_ASCII('.'); + dot_str[2] = CS_OP_ASCII('\0'); + null_str[0] = CS_OP_ASCII('\0'); + pc_init_inode( &lfinode, dot_str, null_str, ADIRENT|ARCHIVE, cltemp, + /*size*/ 0L , &crdate); + /* And to the buffer in intel form */ + pc_ino2dos (++pdinodes, &lfinode ); + /* Write the cluster out */ + if ( !pc_write_blk ( pbuff ) ) + { + pc_discard_buf(pbuff); + pc_freeobj(pobj); + if (!incluster) + FATOP(pdrive)->fatop_clrelease_dir(pdrive , cluster); + return (0); + } + else + pc_release_buf(pbuff); + + /* And write the node out with the original attributes */ + pobj->finode->fattribute = (byte)(attributes|ARCHIVE); + + /* Convert to native. Overwrite the existing inode.Set archive/date */ + if (!pc_update_inode(pobj, TRUE, TRUE)) + { + pc_freeobj(pobj); + if (!incluster) + FATOP(pdrive)->fatop_clrelease_dir(pdrive , cluster); + return (0); + } + } +#endif + ret_val = FATOP(pdrive)->fatop_flushfat(pdrive->driveno); + + if (ret_val) + { + return (pobj); + } + else + { + pc_freeobj(pobj); + return (0); + } +} + +/*************************************************************************** + PC_RMNODE - Delete an inode unconditionally. + + Description + Delete the inode at pobj and flush the file allocation table. Does not + check file permissions or if the file is already open. (see also pc_unlink + and pc_rmdir). The inode is marked deleted on the disk and the cluster + chain associated with the inode is freed. (Un-delete wo not work) + + Returns + Returns TRUE if it successfully deleted the inode an flushed the fat. + +*****************************************************************************/ + + +/* Delete a file / dir or volume. Do not check for write access et al */ +/* Note: the parent directory is locked before this routine is called */ +BOOLEAN pc_rmnode( DROBJ *pobj) /*__fn__*/ +{ + CLUSTERTYPE cluster; + DDRIVE *pdrive; + + /* Do not delete anything that has multiple links */ + if (pobj->finode->opencount > 1) + { + rtfs_set_errno(PEACCES); /* pc_rmnode() - directory entry is in use */ +err_ex: + pc_report_error(PCERR_REMINODE); + return (FALSE); + } + pdrive = pobj->pdrive; + /* Mark it deleted and unlink the cluster chain */ + pobj->finode->fname[0] = PCDELETEESCAPE; + + cluster = pc_finode_cluster(pdrive,pobj->finode); + + if (!pc_delete_lfn_info(pobj)) /* Delete lonf file name info associated with DROBJ */ + goto err_ex; + + /* We free up store right away. Do not leave cluster pointer + hanging around to cause problems. */ + pc_pfinode_cluster(pdrive,pobj->finode,0); + /* Convert to native. Overwrite the existing inode.Set archive/date */ + if (pc_update_inode(pobj, TRUE, TRUE)) + { + /* If there is no cluster chain to delete don't call freechain */ + if (!cluster) + return(TRUE); + /* And clear up the space */ + /* Tomo */ + /* Make sure the blocks contained within the cluster chain + are flushed from the block buffer pool */ + pc_flush_chain_blk(pdrive, cluster); + /* Set min to 0 and max to 0xffffffff to eliminate range checking on the + cluster chain and force removal of all clusters */ + if (!FATOP(pdrive)->fatop_freechain(pdrive, cluster, 0, 0xffffffff)) + { + /* If freechain failed still flush the fat in INVALID CLUSTER + was the failure condition */ + if (get_errno() == PEINVALIDCLUSTER) + FATOP(pdrive)->fatop_flushfat(pobj->pdrive->driveno); + return(FALSE); + + } + else if ( FATOP(pdrive)->fatop_flushfat(pobj->pdrive->driveno) ) + return (TRUE); + } + /* If it gets here we had a problem */ + return(FALSE); +} + +/************************************************************************** + PC_UPDATE_INODE - Flush an inode to disk + +Summary + +Description + Read the disk inode information stored in pobj and write it to + the block and offset on the disk where it belongs. The disk is + first read to get the block and then the inode info is merged in + and the block is written. (see also pc_mknode() ) + +Returns + Returns TRUE if all went well, no on a write error. + +***************************************************************************** +*/ + +/* Take a DROBJ that contains correct my_index & my_block. And an inode. + Load the block. Copy the inode in and write it back out */ +BOOLEAN pc_update_inode(DROBJ *pobj, BOOLEAN set_archive, BOOLEAN set_date) /*__fn__*/ +{ + BLKBUFF *pbuff; + DOSINODE *pi; + int i; + DIRBLK *pd; + DATESTR crdate; + + pd = &pobj->blkinfo; + i = pd->my_index; + if ( i >= INOPBLOCK || i < 0 ) /* Index into block */ + { + rtfs_set_errno(PEINTERNAL); /* pc_update_inode: Internal error, illegal inode index */ + return (FALSE); + } + OS_CLAIM_FSCRITICAL() + /* Set the archive bit and the date */ + if (set_archive) + pobj->finode->fattribute |= ARCHIVE; + + if (set_date) + { + pc_getsysdate(&crdate); + pobj->finode->ftime = crdate.time; + pobj->finode->fdate = crdate.date; + } + OS_RELEASE_FSCRITICAL() + /* Read the data */ + pobj->pblkbuff = pbuff = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + if (pbuff) + { + pi = (DOSINODE *) &pbuff->data[0]; + /* Copy it off and write it */ + pc_ino2dos( (pi+i), pobj->finode ); + if (!pc_write_blk(pbuff)) + { + pc_discard_buf(pbuff); + return (FALSE); + } + else + { + pc_release_buf(pbuff); + return (TRUE); + } + } + return (FALSE); +} + +#if (RTFS_WRITE) + /* Load an in memory inode up with user supplied values */ +void pc_init_inode(FINODE *pdir, KS_CONSTANT byte *filename, /*__fn__*/ + KS_CONSTANT byte *fileext, byte attr, + CLUSTERTYPE cluster, dword size, DATESTR *crdate) /*__fn__*/ +{ + /* Copy the file names and pad with spaces */ + pc_cppad(pdir->fname,(byte*)filename,8); + pc_cppad(pdir->fext,(byte*)fileext,3); + pdir->fattribute = attr; + rtfs_memset(&pdir->resarea[0],(byte) 0,8); + + pdir->ftime = crdate->time; + pdir->fdate = crdate->date; + /* Note: fclusterhi is of resarea in fat 16 system and cluster>>16 is 0*/ + pdir->fclusterhi = (word)(cluster >> 16); + pdir->fcluster = (word) cluster; + pdir->fsize = size; + pc_zero_lfn_info(pdir); + +} + +/*************************************************************************** + PC_INO2DOS - Convert an in memory inode to a dos disk entry. + + Description + Take in memory native format inode information and copy it to a + buffer. Translate the inode to INTEL byte ordering during the transfer. + + Returns + Nothing + +***************************************************************************/ +/* Un-Make a disk directory entry */ +/* Convert an inmem inode to dos form. */ +void pc_ino2dos (DOSINODE *pbuff, FINODE *pdir) /*__fn__*/ +{ + pc_ascii_strn2upper((byte *)&pbuff->fname[0],(byte *)&pdir->fname[0],8); /*X*/ + pc_ascii_strn2upper((byte *)&pbuff->fext[0],(byte *)&pdir->fext[0],3); /*X*/ + pbuff->fattribute = pdir->fattribute; /*X*/ + /* If the first character is 0xE5 (valid kanji) convert it to 0x5 */ + if (pdir->fname[0] == PCDELETE) /* 0XE5 */ + pbuff->fname[0] = 0x5; + + /* If rmnode wants us to delete the file set it to 0xE5 */ + if (pdir->fname[0] == PCDELETEESCAPE) + pbuff->fname[0] = PCDELETE; + + + copybuff(&pbuff->resarea[0],&pdir->resarea[0], 8); /*X*/ +#if (KS_LITTLE_ENDIAN) + pbuff->ftime = pdir->ftime; + pbuff->fdate = pdir->fdate; + pbuff->fcluster = pdir->fcluster; + /* Note: fclusterhi is of resarea in fat 16 system */ + pbuff->fclusterhi = pdir->fclusterhi; + pbuff->fsize = pdir->fsize; +#else + fr_WORD((byte *) &pbuff->ftime,pdir->ftime); /*X*/ + fr_WORD((byte *) &pbuff->fdate,pdir->fdate); /*X*/ + fr_WORD((byte *) &pbuff->fcluster,pdir->fcluster); /*X*/ + /* Note: fclusterhi is of resarea in fat 16 system */ + fr_WORD((byte *) &pbuff->fclusterhi,pdir->fclusterhi); /*X*/ + fr_DWORD((byte *) &pbuff->fsize,pdir->fsize); /*X*/ +#endif +} + +#endif + + +/************************************************************************* + PC_GET_ROOT - Create the special ROOT object for a drive. + Description + Use the information in pdrive to create a special object for accessing + files in the root directory. + + Returns + Returns a pointer to a DROBJ, or NULL if no core available. +****************************************************************************/ + +/* Initialize the special root object + Note: We do not read any thing in here we just set up + the block pointers. */ + +DROBJ *pc_get_root( DDRIVE *pdrive) /*__fn__*/ +{ + DIRBLK *pd; + DROBJ *pobj; + FINODE *pfi; + + pobj = pc_allocobj(); + if (!pobj) + return (0); + pobj->pdrive = pdrive; + + pfi = pc_scani(pdrive, 0, 0); + + if (pfi) + { + pc_freei(pobj->finode); + pobj->finode = pfi; + } + else /* No inode in the inode list. Copy the data over + and mark where it came from */ + { + pc_marki(pobj->finode , pdrive , 0, 0); + } + + + /* Add a TEST FOR DRIVE INIT Here later */ + pobj->pdrive = pdrive; + /* Set up the tree stuf so we know it is the root */ + pd = &pobj->blkinfo; + pd->my_frstblock = pdrive->rootblock; + pd->my_block = pdrive->rootblock; + pd->my_index = 0; + pobj->isroot = TRUE; + return (pobj); +} + + +/**************************************************************************** + PC_FIRSTBLOCK - Return the absolute block number of a directory s + contents. + Description + Returns the block number of the first inode in the subdirectory. If + pobj is the root directory the first block of the root will be returned. + + Returns + Returns 0 if the obj does not point to a directory, otherwise the + first block in the directory is returned. + +*****************************************************************************/ + +/* Get the first block of a root or subdir */ +BLOCKT pc_firstblock( DROBJ *pobj) /*__fn__*/ +{ + dword clno; + if (!pc_isadir(pobj)) + { + rtfs_set_errno(PEINTERNAL); /* pc_firstblock: Internal error */ + return (BLOCKEQ0); + } + /* Root dir ? */ +#if (RTFS_SUBDIRS) + if (!pobj->isroot) + { + clno = pc_finode_cluster(pobj->pdrive, pobj->finode); + if ((clno < 2) || (clno > pobj->pdrive->maxfindex) ) + { + rtfs_set_errno(PEINVALIDCLUSTER); + return (0); + } + return (pc_cl2sector(pobj->pdrive , clno)); + } + else +#endif + return (pobj->blkinfo.my_frstblock); +} + +/*************************************************************************** + PC_NEXT_BLOCK - Calculate the next block owned by an object. + + Description + Find the next block owned by an object in either the root or a cluster + chain and update the blockinfo section of the object. + + Returns + Returns TRUE or FALSE on end of chain. + +*****************************************************************************/ + + +/* Calculate the next block in an object */ +BOOLEAN pc_next_block( DROBJ *pobj) /*__fn__*/ +{ + BLOCKT nxt; + + nxt = pc_l_next_block(pobj->pdrive, pobj->blkinfo.my_block); + + if (nxt) + { + pobj->blkinfo.my_block = nxt; + return (TRUE); + } + else + return (FALSE); +} + +/************************************************************************** + PC_L_NEXT_BLOCK - Calculate the next block in a chain. + + Description + Find the next block in either the root or a cluster chain. + + Returns + Returns 0 on end of root dir or chain. + +****************************************************************************/ + + /* Return the next block in a chain */ +BLOCKT pc_l_next_block(DDRIVE *pdrive, BLOCKT curblock) /*__fn__*/ +{ + CLUSTERTYPE cluster; + /* If the block is in the root area */ + if (curblock < pdrive->firstclblock) + { + if (curblock < pdrive->rootblock) + { + rtfs_set_errno(PEINVALIDBLOCKNUMBER); /* pc_l_next_block: Internal error */ + return (BLOCKEQ0); + } + else if (++curblock < pdrive->firstclblock) + return (curblock); + else + return (BLOCKEQ0); + } + else /* In cluster space */ + { + if (curblock >= pdrive->numsecs) + { + rtfs_set_errno(PEINVALIDBLOCKNUMBER); /* pc_l_next_block: Internal error */ + return (BLOCKEQ0); + } + /* Get the next block */ + curblock += 1; + + /* If the next block is not on a cluster edge then it must be + in the same cluster as the current. - otherwise we have to + get the firt block from the next cluster in the chain */ + if (pc_sec2index(pdrive, curblock)) + return (curblock); + else + { + curblock -= 1; + /* Get the old cluster number */ + cluster = pc_sec2cluster(pdrive,curblock); + if ((cluster < 2) || (cluster > pdrive->maxfindex) ) + { + rtfs_set_errno(PEINVALIDBLOCKNUMBER); /* pc_l_next_block: Internal error */ + return (BLOCKEQ0); + } + /* Consult the fat for the next cluster */ + cluster = FATOP(pdrive)->fatop_clnext(pdrive, cluster); /* Directory */ + if (cluster == 0xffffffff) + return (BLOCKEQ0); /* End of chain */ + else if (cluster == 0) /* clnext detected error */ + { + return (BLOCKEQ0); + } + else + return (pc_cl2sector(pdrive, cluster)); + } + } +} + +/************************************************************************** + PC_MARKI - Set dr:sec:index info and stitch a FINODE into the inode list + + +Description + Each inode is uniquely determined by DRIVE, BLOCK and Index into that + block. This routine takes an inode structure assumed to contain the + equivalent of a DOS directory entry. And stitches it into the current + active inode list. Drive block and index are stored for later calls + to pc_scani and the inode s opencount is set to one. + +Returns + Nothing + +***************************************************************************/ + + + +/* Take an unlinked inode and link it in to the inode chain. Initialize + the open count and sector locater info. */ +void pc_marki( FINODE *pfi, DDRIVE *pdrive, BLOCKT sectorno, int index)/*__fn__*/ +{ + OS_CLAIM_FSCRITICAL() + + pfi->my_drive = pdrive; + pfi->my_block = sectorno; + pfi->my_index = index; + pfi->opencount = 1; + + /* Stitch the inode at the front of the list */ + if (prtfs_cfg->inoroot) + prtfs_cfg->inoroot->pprev = pfi; + + pfi->pprev = 0; + pfi->pnext = prtfs_cfg->inoroot; + prtfs_cfg->inoroot = pfi; + + OS_RELEASE_FSCRITICAL() +} + + +/************************************************************************** + PC_SCANI - Search for an inode in the internal inode list. + +Description + Each inode is uniquely determined by DRIVE, BLOCK and Index into that + block. This routine searches the current active inode list to see + if the inode is in use. If so the opencount is changed and a pointer is + returned. This guarantees that two processes will work on the same + information when manipulating the same file or directory. + +Returns + A pointer to the FINODE for pdrive:sector:index or NULL if not found + +****************************************************************************/ + +/* See if the inode for drive,sector , index is in the list. If so.. + bump its open count and return it. Else return NULL */ + +FINODE *pc_scani( DDRIVE *pdrive, BLOCKT sectorno, int index) /*__fn__*/ +{ + FINODE *pfi; + OS_CLAIM_FSCRITICAL() + pfi = prtfs_cfg->inoroot; + while (pfi) + { + if ( (pfi->my_drive == pdrive) && + (pfi->my_block == sectorno) && + (pfi->my_index == index) ) + { + pfi->opencount += 1; + OS_RELEASE_FSCRITICAL() + return (pfi); + } + pfi = pfi->pnext; + } + OS_RELEASE_FSCRITICAL() + return (0); +} + + +/************************************************************************** + PC_ALLOCOBJ - Allocate a DROBJ structure + Description + Allocates and zeroes the space needed to store a DROBJ structure. Also + allocates and zeroes a FINODE structure and links the two via the + finode field in the DROBJ structure. + + Returns + Returns a valid pointer or NULL if no more core. + +*****************************************************************************/ + +DROBJ *pc_allocobj(void) /*__fn__*/ +{ + DROBJ *pobj; + + /* Alloc a DROBJ */ + pobj = pc_memory_drobj(0); + if (pobj) + { + pobj->finode = pc_alloci(); + if (!pobj->finode) + { + /* Free the DROBJ */ + pc_memory_drobj(pobj); + pobj = 0; + } + } + return (pobj); +} + +/************************************************************************** + PC_ALLOCI - Allocate a FINODE structure + + Description + Allocates and zeroes a FINODE structure. + + Returns + Returns a valid pointer or NULL if no more core. + +****************************************************************************/ + +FINODE *pc_alloci(void) /*__fn__*/ +{ +FINODE *p; + p = pc_memory_finode(0); + return(p); +} + +/************************************************************************** + PC_FREE_ALL_DROBJ - Release all drobj buffers associated with a drive. +Description + For each internally buffered drobj structure associated with pdrive +Returns + Nothing +****************************************************************************/ + +void pc_free_all_drobj( DDRIVE *pdrive) /*__fn__*/ +{ + int i; + DROBJ *pobj; + pobj = prtfs_cfg->mem_drobj_pool; + for (i = 0; i < prtfs_cfg->cfg_NDROBJS; i++,pobj++) + { + if (pobj->pdrive == pdrive) + pc_memory_drobj(pobj); + } +} + +/************************************************************************** + PC_FREE_ALL_I - Release all inode buffers associated with a drive. +Description + For each internally buffered finode (dirent) check if it exists on + pdrive. If so delete it. In debug mode print a message since all + finodes should be freed before pc_dskclose is called. +Returns + Nothing +****************************************************************************/ + +void pc_free_all_i( DDRIVE *pdrive) /*__fn__*/ +{ + FINODE *pfi; + + OS_CLAIM_FSCRITICAL() + pfi = prtfs_cfg->inoroot; + OS_RELEASE_FSCRITICAL() + while (pfi) + { + if (pfi->my_drive == pdrive) + { + /* Set the opencount to 1 so freei releases the inode */ + pfi->opencount = 1; + pc_freei(pfi); + /* Since we changed the list go back to the top */ + OS_CLAIM_FSCRITICAL() + pfi = prtfs_cfg->inoroot; + OS_RELEASE_FSCRITICAL() + } + else + { + OS_CLAIM_FSCRITICAL() + pfi = pfi->pnext; + OS_RELEASE_FSCRITICAL() + } + } +} + + +/***************************************************************************** + PC_FREEI - Release an inode from service + +Description + If the FINODE structure is only being used by one file or DROBJ, unlink it + from the internal active inode list and return it to the heap; otherwise + reduce its open count. + + Returns + Nothing + +****************************************************************************/ + +void pc_freei( FINODE *pfi) /*__fn__*/ +{ + if (!pfi) + { + return; + } + OS_CLAIM_FSCRITICAL() + if (pfi->opencount) + { + if (--pfi->opencount) /* Decrement opencount and return if non zero */ + { + OS_RELEASE_FSCRITICAL() + return; + } + else + { + if (pfi->pprev) /* Pont the guy behind us at the guy in front*/ + { + pfi->pprev->pnext = pfi->pnext; + } + else + { + + prtfs_cfg->inoroot = pfi->pnext; /* No prev, we were at the front so + make the next guy the front */ + } + + if (pfi->pnext) /* Make the next guy point behind */ + { + pfi->pnext->pprev = pfi->pprev; + } + } + } + OS_RELEASE_FSCRITICAL() + /* release the core */ + pc_memory_finode(pfi); +} + +/*************************************************************************** + PC_FREEOBJ - Free a DROBJ structure + + Description + Return a drobj structure to the heap. Calls pc_freei to reduce the + open count of the finode structure it points to and return it to the + heap if appropriate. + + Returns + Nothing + + +****************************************************************************/ + +void pc_freeobj( DROBJ *pobj) /*__fn__*/ +{ + if (pobj) + { + pc_freei(pobj->finode); + /* Release the core */ + pc_memory_drobj(pobj); + } +} + +/*************************************************************************** + PC_DOS2INODE - Convert a dos disk entry to an in memory inode. + + Description + Take the data from pbuff which is a raw disk directory entry and copy + it to the inode at pdir. The data goes from INTEL byte ordering to + native during the transfer. + + Returns + Nothing +****************************************************************************/ + +/* Convert a dos inode to in mem form. */ +void pc_dos2inode (FINODE *pdir, DOSINODE *pbuff) /*__fn__*/ +{ + copybuff(&pdir->fname[0],&pbuff->fname[0],8); /*X*/ + /* If the on disk representation is 0x5, change it to 0xE5, a valid + kanji character */ + if (pdir->fname[0] == 0x5) + pdir->fname[0] = PCDELETE; + + copybuff(&pdir->fext[0],&pbuff->fext[0],3); /*X*/ + pdir->fattribute = pbuff->fattribute; /*X*/ + copybuff(&pdir->resarea[0],&pbuff->resarea[0], 8); /*X*/ +#if (!KS_LITTLE_ENDIAN) + pdir->ftime = to_WORD((byte *) &pbuff->ftime); /*X*/ + pdir->fdate = to_WORD((byte *) &pbuff->fdate); /*X*/ + /* Note: fclusterhi is of resarea in fat 16 system */ + pdir->fclusterhi = to_WORD((byte *) &pbuff->fclusterhi); + pdir->fcluster = to_WORD((byte *) &pbuff->fcluster); /*X*/ + pdir->fsize = to_DWORD((byte *) &pbuff->fsize); /*X*/ +#else + pdir->ftime = pbuff->ftime; /*X*/ + pdir->fdate = pbuff->fdate; /*X*/ + /* Note: fclusterhi is of resarea in fat 16 system */ + pdir->fclusterhi = pbuff->fclusterhi; /*X*/ + pdir->fcluster = pbuff->fcluster; /*X*/ + pdir->fsize = pbuff->fsize; /*X*/ +#endif +} + +/************************************************************************** + PC_INIT_INODE - Load an in memory inode up with user supplied values. + + Description + Take an uninitialized inode (pdir) and fill in some fields. No other + processing is done. This routine simply copies the arguments into the + FINODE structure. + + Note: filename & fileext do not need null termination. + + Returns + Nothing +****************************************************************************/ +/************************************************************************** + PC_ISAVOL - Test a DROBJ to see if it is a volume + + Description + Looks at the appropriate elements in pobj and determines if it is a root + or subdirectory. + + Returns + Returns FALSE if the obj does not point to a directory. + +****************************************************************************/ + +BOOLEAN pc_isavol( DROBJ *pobj) /*__fn__*/ +{ + if (pobj->finode->fattribute & AVOLUME) + return(TRUE); + else + return(FALSE); +} + + +/************************************************************************** + PC_ISADIR - Test a DROBJ to see if it is a root or subdirectory + + Description + Looks at the appropriate elements in pobj and determines if it is a root + or subdirectory. + + Returns + Returns FALSE if the obj does not point to a directory. + +****************************************************************************/ + +BOOLEAN pc_isadir( DROBJ *pobj) /*__fn__*/ +{ + if ( (pobj->isroot) || (pobj->finode->fattribute & ADIRENT) ) + return(TRUE); + else + return(FALSE); +} + + +/************************************************************************** + PC_ISROOT - Test a DROBJ to see if it is the root directory + + Description + Looks at the appropriate elements in pobj and determines if it is a root + directory. + + Returns + Returns NO if the obj does not point to the root directory. + +****************************************************************************/ + +/* Get the first block of a root or subdir */ +BOOLEAN pc_isroot( DROBJ *pobj) /*__fn__*/ +{ + return(pobj->isroot); +} diff --git a/build/libraries/fatfs/ARM7/rtfat16.c b/build/libraries/fatfs/ARM7/rtfat16.c new file mode 100644 index 0000000..5a849ca --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtfat16.c @@ -0,0 +1,360 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* RTFAT16.C - FAT16 management routines */ + +#include + +CLUSTERTYPE fatxx_clalloc(DDRIVE *pdr, CLUSTERTYPE clhint); +CLUSTERTYPE fatxx_clgrow(DDRIVE *pdr, CLUSTERTYPE clno); + + +CLUSTERTYPE fatxx_clalloc(DDRIVE *pdr, CLUSTERTYPE clhint); +CLUSTERTYPE fatxx_clgrow(DDRIVE *pdr, CLUSTERTYPE clno); + + +BOOLEAN pc_init_drv_fat_info16(DDRIVE *pdr, struct pcblk0 *pbl0) +{ + pdr->secpfat = (CLUSTERTYPE) pbl0->secpfat; /* sectors / fat */ + pdr->numfats = pbl0->numfats; /* Number of fat copies */ + pdr->fatblock = (BLOCKT) pbl0->secreserved; + pdr->rootblock = pdr->fatblock + pdr->secpfat * pdr->numfats; + pdr->secproot = (int)((pdr->numroot + INOPBLOCK - 1)/INOPBLOCK); + /* The first block of the cluster area is just past the root */ + /* Round up if we have to */ + pdr->firstclblock = pdr->rootblock + + (pdr->numroot + INOPBLOCK - 1)/INOPBLOCK; + /* Calculate the largest index in the file allocation table. + Total # block in the cluster area)/Blockpercluster == s total + Number of clusters. Entries 0 & 1 are reserved so the highest + valid fat index is 1 + total # clusters. + */ + pdr->maxfindex = (long)(1 + ((pdr->numsecs - pdr->firstclblock)/pdr->secpalloc)); + /* Nibbles/fat entry if < 4087 clusters then 12 bit else 16 */ + pdr->fasize = (int) ((pdr->maxfindex < 4087) ? 3 : 4); + { + /* Make sure the calculated index doesn't overflow the fat sectors */ + dword max_index; + /* For FAT16 Each block of the fat holds 256 entries so the maximum index is + (pdr->secpfat * 256)-1; */ + max_index = (dword) pdr->secpfat; + if (pdr->fasize == 3) + {/* Max clusters in FAT12.. 1024 per 3 blocks plus 341 per residual block */ + /* Modified 8-16-06 to distinguish between FAT12 and FAT16 */ + dword max_index3,div3,ltemp,residual; + max_index3 = max_index; + div3 = max_index3/3; + max_index = div3 * 1024; + ltemp = div3 * 3; + residual = max_index3-ltemp; + max_index += (residual * 341); + } + else + { + max_index *= 256; + } + max_index -= 1; + if (pdr->maxfindex > max_index) + pdr->maxfindex = max_index; + } + /* if calculated size > fff0 set it to one less. fff0 to ffff are + reserved values. */ + if (pdr->maxfindex >= 0xfff0 && pdr->maxfindex <= 0xffff) + pdr->maxfindex = 0xffef; + + /* Create a hint for where we should write file data. We do this + because directories are allocated in one cluster chunks while + file may allocate larger chunks. We Try to put directory + data at the beginning of the disk in a seperate region so we + do not break the contiguous space further out */ + + /* guess that 1/32nd of the disk will store directory info and the + rest will be data. */ + pdr->free_contig_base = (word) (pdr->maxfindex >> 5); + if (pdr->free_contig_base < 2) + pdr->free_contig_base = 2; + /* set the pointer to where to look for free clusters to the contiguous + area. On the first call to write this will hunt for the real free + blocks. */ + pdr->free_contig_pointer = pdr->free_contig_base; + + pdr->infosec = 0; + return(TRUE); +} +BOOLEAN pc_mkfs16(int driveno, FMTPARMS *pfmt, BOOLEAN use_raw) /*__fn__*/ +{ + byte *b; + BLKBUFF *buf; + dword ltotsecs; + word totsecs; + word nclusters; + dword lnclusters; + dword ldata_area; + int fausize; + word i,j; + BLOCKT blockno; + BOOLEAN ret_val; + + if (use_raw) /* check media and clear change conditions */ + check_drive_number_present(driveno); + + ret_val = FALSE; + + buf = pc_scratch_blk(); + if (!buf) + { + return(FALSE); + } + b = buf->data; + + /* Build up a block 0 */ + rtfs_memset(&b[0], 0, 512); + + b[0] = (byte) 0xe9; /* Jump vector. Used to id MS-DOS disk */ + b[1] = (byte) 0x00; + b[2] = (byte) 0x00; + /* Copy the OEM name */ + pc_cppad(&b[3], pfmt->oemname, 8); + /* bytes per sector */ + + fr_WORD ( &(b[11]), 512); /*X*/ + /* sectors / cluster */ + b[13] = pfmt->secpalloc; + /* Number of reserved sectors. (Including block 0) */ + fr_WORD ( &(b[14]), pfmt->secreserved); /*X*/ + /* number of dirents in root */ + fr_WORD ( &(b[17]), pfmt->numroot); /*X*/ + /* total sectors in the volume */ + + /* Set totsecs to 0 if size > 64k. This triggers sensing huge 4.0 + partitions. */ + + ltotsecs = pfmt->numcyl; + ltotsecs *= pfmt->secptrk; + ltotsecs *= pfmt->numhead; + ltotsecs -= pfmt->numhide; //ctr modified + + if (ltotsecs > 0xffffL) + { + /* HUGE partition the 3.xx totsecs field is zeroed */ + totsecs = 0; + } + else + { + totsecs = (word) ltotsecs; + } + fr_WORD ( &(b[19]), totsecs); /*X*/ + + /* Media descriptor */ + b[21] = pfmt->mediadesc; + /* sectors per trak */ + fr_WORD ( &(b[24]), pfmt->secptrk); /*X*/ + /* number heads */ + fr_WORD ( &(b[26]), pfmt->numhead); /*X*/ + /* number hidden sectors */ + fr_WORD ( &(b[28]), pfmt->numhide); /*X*/ //ctr modified + /* number of duplicate fats */ + b[16] = pfmt->numfats; + fr_WORD ( &(b[22]), (word)pfmt->secpfat); /*X*/ + + /* Now fill in 4.0 specific section of the boot block */ + if (ltotsecs > 0xffffL) + { + /* HUGE partition */ + fr_DWORD ( &(b[32]), ltotsecs); /*X*/ + } + else + { + fr_DWORD ( &(b[32]), 0L); /*X*/ + } + + b[36] = pfmt->physical_drive_no; + b[38] = 0x29; /* extended boot signature */ + fr_DWORD(&(b[39]) , pfmt->binary_volume_label); /*X*/ + pc_cppad( &(b[43]), pfmt->text_volume_label, 11); + fr_WORD(&(b[0x01fe]), (word)0xaa55); + + /* Count the size of the area managed by the fat. */ + ldata_area = ltotsecs; + ldata_area -= pfmt->numfats * pfmt->secpfat; + ldata_area -= pfmt->secreserved; + + /* Note: numroot must be an even multiple op INOPBLOCK */ + ldata_area -= pfmt->numroot/INOPBLOCK; + + /* Nibbles/fat entry if < 4087 clusters then 12 bit else 16 */ + lnclusters = ldata_area/pfmt->secpalloc; + if (lnclusters > 0xffffL) + { + rtfs_set_errno(PEINVALIDPARMS); /* pc_mkfs16: volume too large */ + goto errex; + } + else + { + nclusters = (word) lnclusters; + } + + fausize = (int) ( (nclusters < 4087) ? 3 : 4 ); + + /* Check the FAT. + if ( (nibbles needed) > (nibbles if fatblocks) + trouble; + */ + { + long ltotnibbles; + long lnibsinfatbls; + + /* Total nibbles = (# clusters * nibbles/cluster) */ + ltotnibbles = lnclusters; + ltotnibbles *= fausize; + + /* How many nibbles are available. */ + lnibsinfatbls = pfmt->secpfat; + lnibsinfatbls <<= 10; /* 1024 nibbles/block */ + + if (ltotnibbles > lnibsinfatbls) + { + rtfs_set_errno(PEINVALIDPARMS); /* pc_mkfs16: volume too large */ + goto errex; + } + } + + if (pfmt->numroot % INOPBLOCK) + { + rtfs_set_errno(PEINVALIDPARMS); /* pc_mkfs16: numroot incorrect */ + goto errex; + } + + + if (!devio_write_format(driveno, (dword) 0 + pfmt->numhide, &(b[0]), 1, use_raw) ) + { + goto errex; + } + /* Now write the fats out */ + for (i = 0; i < pfmt->numfats; i++) + { + rtfs_memset(&b[0], 0, 512); + /* The first 3(4) bytes of a fat are MEDIADESC,FF,FF,(FF) */ + b[0] = pfmt->mediadesc; + b[1] = (byte) 0xff; + b[2] = (byte) 0xff; + if (fausize == 4) + b[3] = (byte) 0xff; + + blockno = pfmt->numhide + pfmt->secreserved + (i * pfmt->secpfat); //ctr modified + for ( j = 0; j < pfmt->secpfat; j++) + { + /* WRITE */ + if (!devio_write_format(driveno, blockno, &(b[0]), 1, use_raw) ) + { + goto errex; + } + blockno += 1; + rtfs_memset(&b[0], 0, 512); + } + } + + /* Now write the root sectors */ + blockno = pfmt->numhide + pfmt->secreserved + pfmt->numfats * pfmt->secpfat; //ctr modified + rtfs_memset(&b[0], 0, 512); + for ( j = 0; j < (pfmt->numroot/INOPBLOCK) ; j++) + { + if (!devio_write_format(driveno, blockno, &(b[0]), 1, use_raw) ) + { + goto errex; + } + blockno += 1; + } + ret_val = TRUE; + +errex: /* Not only errors return through here. Everything does. */ + pc_free_scratch_blk(buf); + return(ret_val); +} + +#if (!FAT32) + +BOOLEAN pc_init_drv_fat_info(DDRIVE *pdr, struct pcblk0 *pbl0) +{ + return (pc_init_drv_fat_info16(pdr, pbl0)); +} + +CLUSTERTYPE pc_get_parent_cluster(DDRIVE *pdrive, DROBJ *pobj) +{ + return(pc_sec2cluster(pdrive,pobj->blkinfo.my_frstblock)); +} +/* Grab a cluster for a new directory entry. + To minimize fragmentation give a hint where to start looking for new + clusters based on the position of the parent directory */ + +CLUSTERTYPE pc_alloc_dir(DDRIVE *pdrive, DROBJ *pmom) +{ +CLUSTERTYPE clbase,cluster; + if (pc_isroot(pmom)) + clbase = 0; + else + clbase = pc_finode_cluster(pmom->pdrive,pmom->finode); + /*Grab a cluster for the new dir */ + cluster = fatxx_clalloc(pdrive, clbase); + return(cluster); +} + +CLUSTERTYPE pc_grow_dir(DDRIVE *pdrive, DROBJ *pobj) +{ +CLUSTERTYPE cluster; + if (pc_isroot(pobj)) + { + rtfs_set_errno(PENOSPC); + cluster = 0; + } + else + cluster = fatxx_clgrow(pdrive, pc_finode_cluster(pdrive,pobj->finode)); + return(cluster); +} +void pc_truncate_dir(DDRIVE *pdrive, DROBJ *pobj, CLUSTERTYPE cluster) +{ + FATOP(pdrive)->fatop_cl_truncate_dir(pdrive, pc_finode_cluster(pdrive,pobj->finode), cluster); +} +BOOLEAN pc_mkfs32(int driveno, FMTPARMS *pfmt, BOOLEAN use_raw) /*__fn__*/ +{ + RTFS_ARGSUSED_INT(driveno); + RTFS_ARGSUSED_PVOID((void *) pfmt); + RTFS_ARGSUSED_INT((int) use_raw); + return(FALSE); +} +CLUSTERTYPE pc_finode_cluster(DDRIVE *pdr, FINODE *finode) /* __fn__ */ +{ + RTFS_ARGSUSED_PVOID((void *) pdr); + return ( (CLUSTERTYPE)finode->fcluster ); +} + +void pc_pfinode_cluster(DDRIVE *pdr, FINODE *finode, CLUSTERTYPE value) /*__fn__ */ +{ + finode->fcluster = (word)value; + RTFS_ARGSUSED_PVOID((void *) pdr); +} +BOOLEAN pc_gblk0_32(word driveno, struct pcblk0 *pbl0, byte *b) /*__fn__*/ +{ + RTFS_ARGSUSED_INT((int) driveno); + RTFS_ARGSUSED_PVOID((void *) pbl0); + RTFS_ARGSUSED_PVOID((void *) b); + return(FALSE); +} +BOOLEAN pc_validate_partition_type(byte p_type) +{ + if ( (p_type == 0x01) || (p_type == 0x04) || (p_type == 0x06) || (p_type == 0x0E)) + return(TRUE); + else + return(FALSE); +} +BOOLEAN fat_flushinfo(DDRIVE *pdr) +{ + RTFS_ARGSUSED_PVOID((void *) pdr); + return(TRUE); +} +#endif diff --git a/build/libraries/fatfs/ARM7/rtfat32.c b/build/libraries/fatfs/ARM7/rtfat32.c new file mode 100644 index 0000000..2147d53 --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtfat32.c @@ -0,0 +1,502 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* RTFAT32.C - FAT32 specific managment routines */ + +#include + +CLUSTERTYPE fatxx_clalloc(DDRIVE *pdr, CLUSTERTYPE clhint); +CLUSTERTYPE fatxx_clgrow(DDRIVE *pdr, CLUSTERTYPE clno); + +#if (FAT32) + +struct fat32_info { + dword fs_sig; /* Signature of FAT32 (0x61417272) */ +#define FSINFOSIG 0x61417272ul + dword free_alloc; /* Free clusters on drive (-1 if unknown) */ + dword next_alloc; /* Most recently allocated cluster */ + dword reserved; /* Reserved - ignore */ + }; + +BOOLEAN pc_init_drv_fat_info(DDRIVE *pdr, struct pcblk0 *pbl0) +{ + if (pdr->numroot!=0) /* Drive is not FAT32 */ + return (pc_init_drv_fat_info16(pdr, pbl0)); + pdr->secpfat = (CLUSTERTYPE) pbl0->secpfat; /* sectors / fat */ + pdr->numfats = pbl0->numfats; /* Number of fat copies */ + if (pdr->secpfat == 0L) + pdr->secpfat = pbl0->secpfat2; + + if (pbl0->flags & NOFATMIRROR) + { + pdr->fatblock = (BLOCKT) pbl0->secreserved + + ((pbl0->flags & ACTIVEFAT) * pdr->secpfat); + pdr->numfats = 1; + } + else + pdr->fatblock = (BLOCKT) pbl0->secreserved; + /* The first block of the root is just past the fat copies */ + pdr->firstclblock = pdr->fatblock + pdr->secpfat * pdr->numfats; +/* DM: 7-6-99: BUG FIX: */ + pdr->rootblock = (pbl0->rootbegin-2) * pdr->secpalloc + pdr->firstclblock; +/* WRONG: pdr->rootblock = pbl0->rootbegin-2 + pdr->firstclblock; */ + + /* Calculate the largest index in the file allocation table. + Total # block in the cluster area)/Blockpercluster == s total + Number of clusters. Entries 0 & 1 are reserved so the highest + valid fat index is 1 + total # clusters. + */ + pdr->maxfindex = (long) (1 + ((pdr->numsecs - pdr->firstclblock)/pdr->secpalloc)); + { + /* Make sure the calculated index doesn't overflow the fat sectors */ + dword max_index; + /* For FAT32 Each block of the fat holds 128 entries so the maximum index is + (pdr->secpfat * 128)-1; */ + max_index = (dword) pdr->secpfat; + max_index *= 128; + max_index -= 1; + if (pdr->maxfindex > max_index) + pdr->maxfindex = max_index; + } + + /* Create a hint for where we should write file data. We do this + because directories are allocated in one cluster chunks while + file may allocate larger chunks. We Try to put directory + data at the beginning of the disk in a seperate region so we + do not break the contiguous space further out */ + + pdr->known_free_clusters = pbl0->free_alloc; + pdr->free_contig_base = pbl0->next_alloc; + pdr->free_contig_pointer = pdr->free_contig_base; + pdr->infosec = pbl0->infosec; + pdr->fasize = 8; + return(TRUE); +} +CLUSTERTYPE pc_get_parent_cluster(DDRIVE *pdrive, DROBJ *pobj) /* __fatfn__ */ +{ + if ((pdrive->fasize == 8) && + (pobj->blkinfo.my_frstblock == pdrive->rootblock)) + return(0); + else + return(pc_sec2cluster(pdrive,pobj->blkinfo.my_frstblock)); +} + +/* Grab a cluster for a new directory entry. + To minimize fragmentation give a hint where to start looking for new + clusters based on the position of the parent directory */ + +CLUSTERTYPE pc_alloc_dir(DDRIVE *pdrive, DROBJ *pmom) /* __fatfn__ */ +{ +CLUSTERTYPE clbase,cluster; + if ( pdrive->fasize != 8 && pc_isroot(pmom)) + clbase = 0; + else + clbase = pc_finode_cluster(pmom->pdrive,pmom->finode); + /*Grab a cluster for the new dir */ + cluster = fatxx_clalloc(pdrive, clbase); + return(cluster); +} + +CLUSTERTYPE pc_grow_dir(DDRIVE *pdrive, DROBJ *pobj) /* __fatfn__ */ +{ +CLUSTERTYPE tmpcl, cluster; + if ( pdrive->fasize != 8 && pc_isroot(pobj)) + { + rtfs_set_errno(PENOSPC); + cluster = 0; + } + else + { + tmpcl = pc_finode_cluster(pdrive,pobj->finode); + if ( (pdrive->fasize == 8) && (tmpcl == 0) ) + tmpcl = pc_sec2cluster(pdrive, pdrive->rootblock); + cluster = fatxx_clgrow(pdrive, tmpcl); + } + return(cluster); +} + +void pc_truncate_dir(DDRIVE *pdrive, DROBJ *pobj, CLUSTERTYPE cluster) /* __fatfn__ */ +{ + if (pdrive->fasize == 8) + { + dword tmpcl = pc_finode_cluster(pdrive,pobj->finode); + if (tmpcl == 0) + tmpcl = pc_sec2cluster(pdrive, pdrive->rootblock); + FATOP(pdrive)->fatop_cl_truncate_dir(pdrive, tmpcl, cluster); + } + else + FATOP(pdrive)->fatop_cl_truncate_dir(pdrive, pc_finode_cluster(pdrive,pobj->finode), cluster); +} + +BOOLEAN pc_mkfs32(int driveno, FMTPARMS *pfmt, BOOLEAN use_raw) /*__fn__*/ +{ + byte *b; + BLKBUFF *buf; + dword ltotsecs; + word totsecs; + dword lnclusters; + dword ldata_area; + int fausize; + word i,j,k; + BLOCKT blockno; + BOOLEAN ret_val; + + if (use_raw) /* check media and clear change conditions */ + check_drive_number_present(driveno); + + ret_val = FALSE; + + buf = pc_scratch_blk(); + if (!buf) + return(FALSE); + b = buf->data; + + /* Build up a block 0 */ + rtfs_memset(&b[0], 0, 512); + for (i=0;isecreserved;i++) + { + /* WRITE */ + if (!devio_write_format(driveno, pfmt->numhide + (dword) i, &(b[0]), 1, use_raw) ) //ctr modified + { + goto errex; + } + } +#if (INCLUDE_FAT32_BOOT_CODE) + copybuff(&b[0],&FAT32_BOOT_CODE[512],512); + if (!devio_write_format(driveno, pfmt->numhide + (dword) 8, &(b[0]), 1, use_raw) ) //ctr modified + { + goto errex; + } + if (!devio_write_format(driveno, pfmt->numhide + (dword) 2, &(b[0]), 1, use_raw) ) //ctr modified + { + goto errex; + } + copybuff(&b[0],&FAT32_BOOT_CODE[0],512); +#else + rtfs_memset(&b[0], 0, 512); +#endif + b[0] = (byte) 0xe9; /* Jump vector. Used to id MS-DOS disk */ + b[1] = (byte) 0x00; + b[2] = (byte) 0x00; + b[0x40] = 0x80; + b[0x41] = 0x00; + b[0x42] = 0x29; +#if (INCLUDE_FAT32_BOOT_CODE) + b[0] = (byte) 0xeb; /* Jump vector. Used to id MS-DOS disk */ + b[1] = (byte) 0x58; + b[2] = (byte) 0x90; +#endif + /* Copy the OEM name */ + pc_cppad(&b[3], pfmt->oemname, 8); + /* bytes per sector */ + + fr_WORD ( &(b[11]), 512); /*X*/ + /* sectors / cluster */ + b[13] = pfmt->secpalloc; + /* Number of reserved sectors. (Including block 0) */ + fr_WORD ( &(b[14]), pfmt->secreserved); /*X*/ + /* number of dirents in root */ + fr_WORD ( &(b[17]), pfmt->numroot); /*X*/ + /* total sectors in the volume */ + + /* Set totsecs to 0 if size > 64k. This triggers sensing huge 4.0 + partitions. */ + + ltotsecs = pfmt->numcyl; + ltotsecs *= pfmt->secptrk; + ltotsecs *= pfmt->numhead; + ltotsecs -= pfmt->numhide; + + if (ltotsecs > 0xffffL) + { + /* HUGE partition the 3.xx totsecs field is zeroed */ + totsecs = 0; + } + else + { + totsecs = (word) ltotsecs; + } + fr_WORD ( &(b[19]), totsecs); /*X*/ + + /* if secpfat was not provided calculate it here */ + /* TONY - Bug fix 6/23/98 */ + if (!pfmt->secpfat) + { + pfmt->secpfat = (ltotsecs + 128*pfmt->secpalloc) + /(128*pfmt->secpalloc + 1); + } + /* Media descriptor */ + b[21] = pfmt->mediadesc; + /* sectors per trak */ + fr_WORD ( &(b[24]), pfmt->secptrk); + /* number heads */ + fr_WORD ( &(b[26]), pfmt->numhead); + /* number hidden sectors */ + fr_DWORD ( &(b[28]), pfmt->numhide); + /* number of duplicate fats */ + b[16] = pfmt->numfats; + fr_WORD ( &(b[22]), (word)0); + fr_DWORD ( &(b[36]), (dword)pfmt->secpfat); + fr_DWORD ( &(b[40]), (dword)0); /* flags and version */ + fr_DWORD ( &(b[44]), (dword)2); /* root dir starting cluster */ + fr_WORD ( &(b[48]), (word)1); /* info sector */ + fr_WORD ( &(b[50]), (word)6); /* backup boot sector */ + + /* Now fill in 4.0 specific section of the boot block */ + if (ltotsecs > 0xffffL) + { + /* HUGE partition */ + fr_DWORD ( &(b[32]), ltotsecs); /*X*/ + } + else + { + fr_DWORD ( &(b[32]), 0L); /*X*/ + } + + fr_DWORD( &(b[0x43]), pfmt->binary_volume_label); + pc_cppad( &(b[0x47]), (byte*)pfmt->text_volume_label, 11); + pc_cppad( &(b[0x52]), (byte*)"FAT32",8); + fr_WORD(&(b[0x01fe]), (word)0xaa55); + + /* Count the size of the area managed by the fat. */ + ldata_area = ltotsecs; + ldata_area -= pfmt->numfats * pfmt->secpfat; + ldata_area -= pfmt->secreserved; + + /* Note: numroot must be an even multiple op INOPBLOCK */ + ldata_area -= pfmt->numroot/INOPBLOCK; + + /* Nibbles/fat entry if < 4087 clusters then 12 bit else 16 */ + lnclusters = ldata_area/pfmt->secpalloc; + fausize = 8; + + /* Check the FAT. + if ( (nibbles needed) > (nibbles if fatblocks) + trouble; + */ + { + long ltotnibbles; + long lnibsinfatbls; + + /* Total nibbles = (# clusters * nibbles/cluster) */ + ltotnibbles = lnclusters; + ltotnibbles *= fausize; + + /* How many nibbles are available. */ + lnibsinfatbls = pfmt->secpfat; + lnibsinfatbls <<= 10; /* 1024 nibbles/block */ + + if (ltotnibbles > lnibsinfatbls) + { + rtfs_set_errno(PEINVALIDPARMS); + goto errex; + } + } + + if (pfmt->numroot % INOPBLOCK) + { + rtfs_set_errno(PEINVALIDPARMS); + goto errex; + } + + if (!devio_write_format(driveno, (dword) 0 + pfmt->numhide, &(b[0]), 1, use_raw) ) + { + goto errex; + } + /* Put the initial free cluster value in right */ + rtfs_memset(&b[0], 0, 512); + fr_DWORD( &(b[0]), (dword) 0x41615252ul); + fr_DWORD( &(b[0x01e4]), (dword) FSINFOSIG); + fr_DWORD( &(b[0x01e8]), (dword)(lnclusters-1)); + fr_DWORD( &(b[0x01ec]), (dword)0x00000003); + fr_WORD( &(b[0x01fe]), (word)0xaa55); + + if (!devio_write_format(driveno, pfmt->numhide + (dword) 7, &(b[0]), 1, use_raw) ) //ctr modified + { + goto errex; + } + if (!devio_write_format(driveno, pfmt->numhide + (dword) 1, &(b[0]), 1, use_raw) ) //ctr modified + { + goto errex; + } + /* Now write the fats out */ + for (i = 0; i < pfmt->numfats; i++) + { + rtfs_memset(&b[0], 0, 512); + /* The first 3(4) bytes of a fat are MEDIADESC,FF,FF,(FF) */ + b[0] = pfmt->mediadesc; + b[1] = (byte) 0xff; + b[2] = (byte) 0xff; + j = (word) fausize; + if (j==8) j=12; + while(j > 3) + { + if (j%4 == 0 && fausize == 8) + b[--j] = (byte) 0x0f; + else + b[--j] = (byte) 0xff; + } + + blockno = pfmt->numhide + pfmt->secreserved + (i * pfmt->secpfat); //ctr modified + for ( j = 0; j < pfmt->secpfat; j++) + { + /* WRITE */ + if (!devio_write_format(driveno, blockno, &(b[0]), 1, use_raw) ) + { + goto errex; + } + blockno += 1; + rtfs_memset(&b[0], 0, 512); + } + } + + /* Now write the root sectors */ + blockno = pfmt->numhide + pfmt->secreserved + pfmt->numfats * pfmt->secpfat; //ctr modified + rtfs_memset(&b[0], 0, 512); + /* Bug fix 11-22-99 use secpalloc instead of 8 */ + for(k=0;ksecpalloc;k++) /* Is 8 blocks per cluster? */ + { + if (!devio_write_format(driveno, blockno+k, &(b[0]), 1, use_raw) ) + goto errex; + } + ret_val = TRUE; + +errex: /* Not only errors return through here. Everything does. */ + pc_free_scratch_blk(buf); + return(ret_val); + +} +CLUSTERTYPE pc_finode_cluster(DDRIVE *pdr, FINODE *finode) /* __fatfn__ */ +{ + if (pdr->fasize == 8) + return ( (dword)finode->fcluster | ((dword)finode->fclusterhi << 16) ); + else + return ( (CLUSTERTYPE)finode->fcluster ); +} + +void pc_pfinode_cluster(DDRIVE *pdr, FINODE *finode, CLUSTERTYPE value) /*__fatfn__ */ +{ + finode->fcluster = (word)value; + if (pdr->fasize == 8) + finode->fclusterhi = (word)(value >> 16); +} +BOOLEAN pc_gblk0_32(word driveno, struct pcblk0 *pbl0, byte *b) /*__fn__*/ +{ + word i; + if (pbl0->numroot == 0) + { + pbl0->secpfat2 = to_DWORD(b+0x24); + pbl0->flags = to_WORD(b+0x28); + pbl0->fs_version = to_WORD(b+0x2a); + pbl0->rootbegin = to_DWORD(b+0x2c); + pbl0->infosec = to_WORD(b+0x30); + pbl0->backup = to_WORD(b+0x32); + copybuff( &pbl0->vollabel[0],b+0x47,11); /* Volume label FAT32 */ + if (!devio_read(driveno,(BLOCKT)(pbl0->infosec) ,b,1, FALSE)) + { + return(FALSE); + } + for (i=0; to_DWORD((void *)b) != FSINFOSIG && i<512; b++,i++); + pbl0->free_alloc = to_DWORD((void *)&((struct fat32_info *)b)->free_alloc); + pbl0->next_alloc = to_DWORD((void *)&((struct fat32_info *)b)->next_alloc); + } + return(TRUE); +} +BOOLEAN pc_validate_partition_type(byte p_type) +{ + if ( (p_type == 0x01) || (p_type == 0x04) || (p_type == 0x06) || + (p_type == 0x0E) || /* Windows FAT16 Partition */ + (p_type == 0x0B) || /* FAT32 Partition */ + (p_type == 0x0C) || /* FAT32 Partition */ + (p_type == 0x55) ) /* FAT32 Partition */ + return(TRUE); + else + return(FALSE); +} +/* Update a fat32 voulme s info sector */ +BOOLEAN fat_flushinfo(DDRIVE *pdr) /*__fn__*/ +{ + byte *pf; + int j; + BLKBUFF *buf; + + if (pdr->fasize == 8) + { + /* Use read_blk, to take advantage of the failsafe cache */ + buf = pc_read_blk(pdr, (dword) pdr->infosec); + if(!buf) + { + rtfs_set_errno(PEIOERRORREADINFO32); /* fat_flushinfo: failed writing info block */ + goto info_error; + } + /* Merge in the new values */ + pf = buf->data; /* Now we do not have to use the stack */ + for (j=0; to_DWORD(pf)!=FSINFOSIG && j<512; pf++,j++); + fr_DWORD((byte *) (&((struct fat32_info *)pf)->free_alloc), pdr->known_free_clusters ); + fr_DWORD((byte *) (&((struct fat32_info *)pf)->next_alloc), pdr->free_contig_pointer ); + /* Use write_blk, to take advantage of the failsafe cache */ + if (!pc_write_blk(buf)) + { + rtfs_set_errno(PEIOERRORWRITEINFO32); /* fat_flushinfo: failed writing info block */ + pc_discard_buf(buf); +info_error: + pc_report_error(PCERR_FAT_FLUSH); + return(FALSE); + } + pc_release_buf(buf); /* Leave it cached */ + } + return (TRUE); +} + +#if (RTFS_WRITE) + +BOOLEAN fatxx_pfaxxterm(DDRIVE *pdr, CLUSTERTYPE clno); +byte * fatxx_pfswap(DDRIVE *pdr, CLUSTERTYPE index, BOOLEAN for_write); + +/* Put a DWORD value into the fat at index */ +BOOLEAN fatxx_pfpdword(DDRIVE *pdr, dword index, dword *pvalue) /*__fatfn__*/ +{ + dword *ppage; + dword offset; + /* Make sure we have access to the page. Mark it for writing */ + ppage = (dword *)fatxx_pfswap(pdr,index,TRUE); + + if (!ppage) + return(FALSE); + else + { + /* there are 128 entries per page */ + offset = (dword) (index & 0x7f); + ppage[(int)offset] = *pvalue; + } + return(TRUE); +} +#endif + +/* Get a DWORD value from the fat at index */ +BOOLEAN fatxx_pfgdword(DDRIVE *pdr, dword index, dword *value) /*__fatfn__*/ +{ + dword *ppage; + dword offset; + /* Make sure we have access to the page. Do not Mark it for writing */ + ppage = (dword *)fatxx_pfswap(pdr,index,FALSE); + + if (!ppage) + return(FALSE); + else + { + /* there are 128 entries per page */ + offset = (dword) (index & 0x7f); + *value = ppage[(int)offset]; + } + return(TRUE); +} + + +#endif + diff --git a/build/libraries/fatfs/ARM7/rtfatxx.c b/build/libraries/fatfs/ARM7/rtfatxx.c new file mode 100644 index 0000000..780184b --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtfatxx.c @@ -0,0 +1,1071 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* RTFATXX.C - Low level File allocation table management functions. + + Routines in this file include: + + fatxx_alloc_chain - Allocate a chain from the FAT. + fatxx_find_free_cluster- Find the first free cluster in a given range. + fatxx_clalloc - Allocate a single cluster in the fragmented region. + fatxx_clgrow - Grow a directory chain in the fragmented region. + fatxx_clnext - Get the next cluster in a chain. + fatxx_clrelease - Return a cluster to the free list. + fatxx_faxx - Get a value from the FAT. + fatxx_flushfat - Make sure the FAT is up to date on disk. + fatxx_freechain - Release a chain to the free list. + fatxx_cl_truncate_dir - Truncate a cluster chain. + fatxx_get_chain - Return contiguous clusters in a chain. + fatxx_pfaxx - Put a value to the FAT. + fatxx_pfswap - Swap a block of the FAT into the cache. + fatxx_fword - Get or put a value from the swap cache. +*/ + + + +#include + +CLUSTERTYPE fatxx_alloc_chain(DDRIVE *pdr, CLUSTERTYPE *pstart_cluster, CLUSTERTYPE n_clusters, BOOLEAN dolink); +CLUSTERTYPE fatxx_find_free_cluster(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE endpt, int *is_error); +CLUSTERTYPE fatxx_clnext(DDRIVE *pdr, CLUSTERTYPE clno); +BOOLEAN fatxx_clrelease_dir(DDRIVE *pdr, CLUSTERTYPE clno); +BOOLEAN fatxx_faxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE *pvalue); +BOOLEAN fatxx_flushfat(int driveno); +BOOLEAN fatxx_freechain(DDRIVE *pdr, CLUSTERTYPE cluster, dword min_clusters_to_free, dword max_clusters_to_free); +CLUSTERTYPE fatxx_cl_truncate_dir(DDRIVE *pdr, CLUSTERTYPE cluster, CLUSTERTYPE l_cluster); +CLUSTERTYPE fatxx_get_chain(DDRIVE *pdr, CLUSTERTYPE start_cluster, CLUSTERTYPE *pnext_cluster, CLUSTERTYPE n_clusters, int *end_of_chain); +BOOLEAN fatxx_pfaxxterm(DDRIVE *pdr, CLUSTERTYPE clno); +BOOLEAN fatxx_pfaxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE value); +BOOLEAN fatxx_fword(DDRIVE *pdr, CLUSTERTYPE index, word *pvalue, BOOLEAN putting); +BOOLEAN fatxx_pfpdword(DDRIVE *pdr, dword index, dword *pvalue); +BOOLEAN fatxx_pfgdword(DDRIVE *pdr, dword index, dword *value); + +/****************************************************************************** + PC_ALLOC_CHAIN - Allocate as many contiguous clusters as possible. + + Description + Reserve up to n_clusters contiguous clusters from the FAT and + return the number of contiguous clusters reserved. + If pstart_cluster points to a valid cluster and dolink is true + then link the new chain to it. + + Returns + Returns the number of contiguous clusters found. Or zero on an error. + pstart_cluster contains the address of the start of the chain on + return. + +*****************************************************************************/ +#if (RTFS_WRITE) + +CLUSTERTYPE fatxx_alloc_chain(DDRIVE *pdr, CLUSTERTYPE *pstart_cluster, CLUSTERTYPE n_clusters, BOOLEAN dolink) /*__fatfn__*/ +{ + CLUSTERTYPE start_cluster; + CLUSTERTYPE first_new_cluster; + CLUSTERTYPE clno; + CLUSTERTYPE n_contig; + CLUSTERTYPE value; + CLUSTERTYPE last_cluster; /* NEW for 5/18/97 bug fix */ + int is_error; + + is_error = 0; + start_cluster = *pstart_cluster; + if (start_cluster && + ( (start_cluster < 2) || (start_cluster > pdr->maxfindex) ) ) + { + rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_alloc_chain: bad cluster value internal error */ + return (0); + } + + /* If the user provided a cluster we find the next cluster beyond that + one. Otherwise we look at the disk structure and find the next + free cluster in the free cluster region after the current best guess + of the region. If that fails we look to the beginning of the region + and if that fails we look in the non-contiguous region. */ + +/* Begin changes for the 5/18/97 bug fix */ + clno = 0; +/* NEW */ + if (start_cluster) + { + /* search from the start_cluster hint to the end of the fat */ + /* use: pdr->maxfindex+1 because find_free_cluster uses < end not <= */ + clno = fatxx_find_free_cluster(pdr, start_cluster, pdr->maxfindex+1, &is_error); + if (is_error) /* Error reading fat */ + return(0); + /* If we search again search only to the start_cluster */ + last_cluster = start_cluster; + } + else + /* When we search again search to the end */ + /* use: pdr->maxfindex+1 because find_free_cluster uses < end not <= */ + last_cluster = pdr->maxfindex + 1; + /* Check the most likely place to find contiguous space */ + if (!clno) + { +/* NEW */ + if (!start_cluster || start_cluster >= pdr->free_contig_pointer) + { + /* search from free_contig_pointer to start_cluster or maxfindex whichever + is less */ + clno = fatxx_find_free_cluster(pdr, pdr->free_contig_pointer, last_cluster, &is_error); + if (is_error) /* Error reading fat */ + return(0); + /* If we search again search only to the free_contig_pointer */ + last_cluster = pdr->free_contig_pointer; + } + } + /* Check the area of the disk beyond where we typically write fragments */ + if (!clno) + { + /* NEW */ + if (!start_cluster || start_cluster > pdr->free_contig_base) + /* search from free_contig_base to start_cluster or free_contig_pointer whichever + is less */ + clno = fatxx_find_free_cluster(pdr, pdr->free_contig_base, last_cluster, &is_error); + if (is_error) /* Error reading fat */ + return(0); + } + /* Check the beginning of the the disk where we typically write fragments */ + if (!clno) + { + clno = fatxx_find_free_cluster(pdr, 2, pdr->free_contig_base, &is_error); + if (is_error) /* Error reading fat */ + return(0); + } + /* We did not find any clusters. Scan the whole fat again this should + never work but we did have a bug in this area once before ... */ + if (!clno) + { + clno = fatxx_find_free_cluster(pdr, 2, pdr->maxfindex, &is_error); + if (is_error) /* Error reading fat */ + return(0); + } + if (!clno) + { + rtfs_set_errno(PENOSPC); /* fatxx_alloc_chain: No free clusters */ + return(0); + } +/* End changes for the 5/18/97 bug fix */ + + first_new_cluster = clno; + value = 0; + n_contig = 1; + + /* look up the FAT. If the next cluster is free we link to it + and up the contig count. */ + while ( (n_contig < n_clusters) && (clno < pdr->maxfindex) ) + { + if (!fatxx_faxx(pdr,(CLUSTERTYPE)(clno+1), &value)) + return(0); + + /* If the next cluster is in-use we are done. */ + if (value) + break; + + /* Link the current cluster to the next one */ + if (!fatxx_pfaxx(pdr, clno, (CLUSTERTYPE)(clno+1))) + return (0); + n_contig += (CLUSTERTYPE)1; /* Yep.. we got another */ + clno += (CLUSTERTYPE)1; /* Up the FAT table */ + } + /* Terminate the list we just made */ + if (!fatxx_pfaxxterm(pdr, clno)) + return (0); + + /* Update the hint of most likeley place to find a free cluster */ + if ((clno < pdr->maxfindex) && (clno >= pdr->free_contig_pointer)) + pdr->free_contig_pointer = (CLUSTERTYPE)(clno+1); + + /* If we were handed a starting cluster we have to stitch our new + chain after it. */ + if (dolink && start_cluster) + { + if (!fatxx_pfaxx(pdr, start_cluster, first_new_cluster)) + return (0); + } + + *pstart_cluster = first_new_cluster; + + pdr->known_free_clusters = (long)(pdr->known_free_clusters - n_contig); + + return(n_contig); +} + +/* Find the first free cluster in a range */ +/* Note: The caller locks the fat before calling this routine */ +CLUSTERTYPE fatxx_find_free_cluster(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE endpt, int *is_error) /*__fatfn__*/ +{ +CLUSTERTYPE i; +CLUSTERTYPE value; + + *is_error = 0; + for (i = startpt; i < endpt; i++) + { + if ( !fatxx_faxx(pdr, i, &value) ) + { + *is_error = 1; + return(0); + } + if (value == 0) + { + return(i); + } + } + return(0); +} + +#if (RTFS_SUBDIRS) +/*************************************************************************** + PC_CLALLOC - Reserve and return the next free cluster on a drive + + Description + Given a DDRIVE, mark the next available cluster in the file allocation + table as used and return the associated cluster number. Clhint provides + a means of selecting clusters that are near eachother. This should + reduce fragmentation. + + NOTE: This routine is used to allocate single cluster chunks for + maintaining directories. We artificially break the disks into + two regions. The first region is where single clusters chunks + used in directory files come from. These are allocated by this + routine only. Data file clusters are allocated by fatxx_alloc_chain. + + THE DISK IS NOT REALLY PARTITIONED. If this routine runs out of + space in the first region it grabs a cluster from the second + region. + Returns + Return a new cluster number or 0 if the disk is full. + +****************************************************************************/ +/* Note: The caller locks the fat before calling this routine */ +CLUSTERTYPE fatxx_clalloc(DDRIVE *pdr, CLUSTERTYPE clhint) /*__fatfn__*/ +{ + CLUSTERTYPE clno; + int is_error; + + if (clhint < 2) + clhint = 2; + if (clhint >= pdr->free_contig_base) + clhint = 2; + + /* Look in the fragmentable region first from clhint up */ + clno = fatxx_find_free_cluster(pdr, clhint, pdr->free_contig_base, &is_error); + if (is_error) /* Error reading fat */ + return(0); + + /* Look in the fragmentable region up to clhint */ + if (!clno) + { + clno = fatxx_find_free_cluster(pdr, 2, clhint, &is_error); + if (is_error) /* Error reading fat */ + return(0); + } + + /* Look in the contiguos region if the fragmentable region is full */ + if (!clno) + { + /* use: pdr->maxfindex+1 because find_free_cluster uses < end not <= */ + clno = fatxx_find_free_cluster(pdr, pdr->free_contig_base, pdr->maxfindex+1, &is_error); + if (is_error) /* Error reading fat */ + return(0); + } + + if (!clno) + rtfs_set_errno(PENOSPC); /* fatxx_clalloc: No free clusters */ + else + { + /* Mark the cluster in use */ + if (!fatxx_pfaxxterm(pdr, clno)) + return (0); + pdr->known_free_clusters = pdr->known_free_clusters - 1; + } + return(clno); +} + +/**************************************************************************** + PC_CLGROW - Extend a cluster chain and return the next free cluster + + Description + Given a DDRIVE and a cluster, extend the chain containing the cluster + by allocating a new cluster and linking clno to it. If clno is zero + assume it is the start of a new file and allocate a new cluster. + + Note: The chain is traversed to the end before linking in the new + cluster. The new cluster terminates the chain. + Returns + Return a new cluster number or 0 if the disk is full. + +****************************************************************************/ + +/* Note: The caller locks the fat before calling this routine */ +CLUSTERTYPE fatxx_clgrow(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__*/ +{ + CLUSTERTYPE nxt; + CLUSTERTYPE nextcluster; + long range_check; + + /* Check the incoming argument. Should be a valid cluster */ + if ((clno < 2) || (clno > pdr->maxfindex) ) + { + rtfs_set_errno(PEINVALIDCLUSTER); + return (0); + } + /* Make sure we are at the end of chain */ + range_check = 0; + nextcluster = fatxx_clnext(pdr , clno); + while (nextcluster != 0xffffffff && ++range_check < MAX_CLUSTERS_PER_DIR) + { + if (!nextcluster) /* fatxx_clnext - set errno */ + return (0); + clno = nextcluster; + nextcluster = fatxx_clnext(pdr , clno); + } + if (get_errno()) + return (0); + if (range_check == MAX_CLUSTERS_PER_DIR) + { + rtfs_set_errno(PEINVALIDCLUSTER); + return (0); + } + /* Get a cluster, clno provides a hint for more efficient cluster + allocation */ + nxt = fatxx_clalloc(pdr,clno); + if (!nxt) + return((CLUSTERTYPE) 0); + /* Attach it to the current cluster if not at the begining of the chain */ + if (!fatxx_pfaxx(pdr, clno, nxt)) + return((CLUSTERTYPE) 0); + return(nxt); +} + +/**************************************************************************** + PC_CLRELEASE - Return a cluster to the pool of free space on a disk + + Description + Given a DDRIVE and a cluster, mark the cluster in the file allocation + table as free. It will be used again by calls to fatxx_clalloc(). + + Returns + Nothing + + + +***************************************************************************/ + + +/* Return a cluster to the free list */ +/* Note: The caller locks the fat before calling this routine */ +BOOLEAN fatxx_clrelease_dir(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__*/ +{ + int current_errno; + /* This is a cleanup routine, an earlier event is the interesting errno + to the application, so we restore errno if we fail */ + current_errno = get_errno(); + /* No need to check clno value, pfaxx will catch it */ + /* Do not catch any lower level errors here. You will catch them soon enough */ + if (fatxx_pfaxx(pdr, clno, (CLUSTERTYPE) 0x0)) /* Mark it as free */ + { + /* If freeing in the contiguous region reset the hint if we + free space earlier than it. */ + if (clno >= pdr->free_contig_base && clno <= pdr->free_contig_pointer) + pdr->free_contig_pointer = clno; + pdr->known_free_clusters = pdr->known_free_clusters + 1; + return(TRUE); + } + else + { + rtfs_set_errno(current_errno); + return(FALSE); + } +} + +/**************************************************************************** + PC_FLUSHFAT - Write any dirty FAT blocks to disk + + Description + Given a valid drive number. Write any fat blocks to disk that + have been modified. Updates all copies of the fat. + + Returns + Returns FALSE if driveno is not an open drive. Or a write failed. + +****************************************************************************/ + +/* Consult the dirty fat block list and write any. write all copies + of the fat */ +/* Note: The caller locks the fat before calling this routine */ +BOOLEAN fatxx_flushfat(int driveno) /*__fatfn__*/ +{ + DDRIVE *pdr; + + pdr = pc_drno2dr(driveno); + + if (!pdr) + { + return(FALSE); + } + + if (!pdr->fat_is_dirty) + return(TRUE); + + /* write the alloc hints into the info block (no-op on non-FAT32 systems) */ + if (!fat_flushinfo(pdr)) + return(FALSE); + + if (pc_flush_fat_blocks(pdr)) + { + pdr->fat_is_dirty = FALSE; + return(TRUE); + } + else + return(FALSE); +} + +/**************************************************************************** + PC_FREECHAIN - Free a cluster chain associated with an inode. + + Description + Trace the cluster chain starting at cluster and return all the clusters to + the free state for re-use. The FAT is not flushed. + + Returns + Nothing. + +****************************************************************************/ +/* Note: The caller locks the fat before calling this routine */ + +BOOLEAN fatxx_freechain(DDRIVE *pdr, CLUSTERTYPE cluster, dword min_clusters_to_free, dword max_clusters_to_free) /*__fatfn__*/ +{ + CLUSTERTYPE nextcluster; + dword clusters_freed; + + if (max_clusters_to_free==0) + return(TRUE); + + if ((cluster < 2) || (cluster > pdr->maxfindex) ) + { + rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_pfaxx: bad cluster value internal error */ + return (FALSE); + } + clusters_freed = 0; + + nextcluster = fatxx_clnext(pdr , cluster); + while (cluster != 0xffffffff) + { + if (!nextcluster) + return (FALSE); /* Clnext set errno */ + /* check if we are about to free more than we should */ + if (clusters_freed >= max_clusters_to_free) + { + rtfs_set_errno(PEINVALIDCLUSTER); + return (FALSE); + } + if (fatxx_pfaxx(pdr, cluster, (CLUSTERTYPE) 0x0)) /* Mark it as free */ + { + /* If freeing in the contiguous region reset the hint if we + free space earlier than it. */ + if (cluster >= pdr->free_contig_base && cluster <= pdr->free_contig_pointer) + pdr->free_contig_pointer = cluster; + pdr->known_free_clusters = pdr->known_free_clusters + 1; + } + else + return(FALSE); /* pfaxx set errno */ + clusters_freed += 1; + cluster = nextcluster; + if (nextcluster != 0xffffffff) + nextcluster = fatxx_clnext(pdr , nextcluster); + } + if (clusters_freed < min_clusters_to_free) + { + rtfs_set_errno(PEINVALIDCLUSTER); + return (FALSE); + } + return(TRUE); +} + + +/**************************************************************************** + PC_CL_TRUNCATE - Truncate a cluster chain. + + Description + Trace the cluster chain starting at cluster until l_cluster is reached. + Then terminate the chain and free from l_cluster on. + The FAT is not flushed. + + If cluster == l_cluster does nothing. This condition should be handled + higher up by updating dirents appropriately and then calling fatxx_freechain. + + Returns + The last cluster in the chain after truncation. +****************************************************************************/ +/* Note: The caller locks the fat before calling this routine */ +CLUSTERTYPE fatxx_cl_truncate_dir(DDRIVE *pdr, CLUSTERTYPE cluster, CLUSTERTYPE l_cluster)/*__fatfn__*/ +{ + CLUSTERTYPE nextcluster; + long range_check; + int current_errno; + if ((cluster < 2) || (cluster > pdr->maxfindex) ) + { /* Don't set errno in this function, it is a cleanup */ + return (0); + } + /* This is a cleanup routine, an earlier event is the interesting errno + to the application, so we restore errno if we fail */ + current_errno = get_errno(); + + nextcluster = fatxx_clnext(pdr , cluster); + + range_check = 0; + while (nextcluster != 0xffffffff && ++range_check < MAX_CLUSTERS_PER_DIR) + { + if (!nextcluster) /* fatxx_clnext detected error */ + { /* Don't set errno in this function, it is a cleanup */ + rtfs_set_errno(current_errno); + return (0); + } + if (nextcluster == l_cluster) + { + if (!fatxx_pfaxxterm(pdr, cluster)) /* Terminate the chain */ + break; /* Error break to ret 0 */ + /* We don't know the size so free between 0 and max */ + if (!fatxx_freechain(pdr, l_cluster, 0, MAX_CLUSTERS_PER_DIR)) + { + rtfs_set_errno(current_errno); + return(0); + } + return(cluster); + } + else + { + cluster = nextcluster; + nextcluster = fatxx_clnext(pdr , nextcluster); + } + } + rtfs_set_errno(current_errno); + return(0); +} + +#endif /* (RTFS_SUBDIRS) */ + +/****************************************************************************** + PC_PFAXXTERM - Write a terminating value to the FAT at clno. + + Description + Given a DDRIVE,cluster number and value. Write the value 0xffffffff or + 0xffff in the fat at clusterno. Handle 32, 16 and 12 bit fats correctly. + + Returns + FALSE if an io error occurred during fat swapping, else TRUE + +*****************************************************************************/ + +/* Given a clno & fatval Put the value in the table at the index (clno) */ +/* Note: The caller locks the fat before calling this routine */ +BOOLEAN fatxx_pfaxxterm(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__*/ +{ +#if (FAT32) + if (pdr->fasize == 8) + return(fatxx_pfaxx(pdr, clno, 0x0ffffffful)); + else +#endif + return(fatxx_pfaxx(pdr, clno, 0xffff)); + +} +/****************************************************************************** + PC_PFAXX - Write a value to the FAT at clno. + + Description + Given a DDRIVE,cluster number and value. Write the value in the fat + at clusterno. Handle 16 and 12 bit fats correctly. + + Returns + No if an io error occurred during fat swapping, else TRUE. + +*****************************************************************************/ + +/* Given a clno & fatval Put the value in the table at the index (clno) */ +/* Note: The caller locks the fat before calling this routine */ +BOOLEAN fatxx_pfaxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE value) /*__fatfn__*/ +{ + union align1 { + byte wrdbuf[4]; /* Temp storage area */ + word fill[2]; + } u; + CLUSTERTYPE nibble,index,offset, t; + + if ((clno < 2) || (clno > pdr->maxfindex) ) + { + rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_pfaxx: bad cluster value internal error */ + return (FALSE); + } + pdr->fat_is_dirty = TRUE; + if (pdr->fasize == 3) /* 3 nibble ? */ + { + nibble = (CLUSTERTYPE)(clno * 3); + index = (CLUSTERTYPE)(nibble >> 2); + offset = (CLUSTERTYPE)(clno & 0x03); + /* Read the first word */ + if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], FALSE )) + return(FALSE); +/* + | W0 | W1 | W2 | + A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1 + xx xx xx +*/ + if (offset == 0) /* (A2 << 8) | A1 A2 */ + { + /* Low nibble of b[1] is hi nibble of value */ + u.wrdbuf[1] &= 0xf0; + t = (CLUSTERTYPE)((value >> 8) & 0x0f); + u.wrdbuf[1] |= (byte) t; + /* b[0] is lo byte of value */ + t = (CLUSTERTYPE)(value & 0x00ff); + u.wrdbuf[0] = (byte) t; + if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE )) + return (FALSE); + } +/* + | W0 | W1 | W2 | + A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1 + xx xx xx +*/ + + else if (offset == 1) /* (B1 B2 << 4) | B0 */ + { + /* Hi nibble of b[1] is lo nibble of value */ + u.wrdbuf[1] &= 0x0f; + t = (CLUSTERTYPE)((value << 4) & 0x00f0); + u.wrdbuf[1] |= (byte)t; + if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE )) + return (FALSE); + /* b[0] is hi byte of value */ + if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], FALSE )) + return(FALSE); + t = (CLUSTERTYPE)((value >> 4) & 0x00ff); + u.wrdbuf[0] = (byte) t; + if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], TRUE )) + return (FALSE); + } +/* + | W0 | W1 | W2 | + A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1 + xx xx xx +*/ + + else if (offset == 2) /*(C2 << 8) | C1 C2 */ + { + /* b[1] = low byte of value */ + t = (CLUSTERTYPE)(value & 0x00ff); + u.wrdbuf[1] = (byte) t; + if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE )) + return (FALSE); + /* lo nibble of b[0] == hi nibble of value */ + if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], FALSE )) + return(FALSE); + u.wrdbuf[0] &= 0xf0; + t = (CLUSTERTYPE)((value >> 8) & 0x0f); + u.wrdbuf[0] |= (byte) t; + if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], TRUE )) + return (FALSE); + } +/* + | W0 | W1 | W2 | + A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1 + xx xx xx +*/ + + else if (offset == 3) /* (D2 D1) << 4 | D0 */ + { + /* Hi nibble b[0] == low nible of value */ + u.wrdbuf[0] &= 0x0f; + t = (CLUSTERTYPE)((value << 4) & 0x00f0); + u.wrdbuf[0] |= (byte) t; + t = (CLUSTERTYPE)((value >> 4) & 0x00ff); + u.wrdbuf[1] = (byte) t; + if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE )) + return (FALSE); + } + } +#if (FAT32) /* FAT32 only supported if FAT swapping is enabled */ + else if (pdr->fasize == 8) + { + fr_DWORD(&u.wrdbuf[0],value); /*X*/ + /* Now put the values back into the FAT */ + if (!fatxx_pfpdword( pdr, clno, (dword *) &u.wrdbuf[0] )) + { + return (FALSE); + } + } +#endif /* FAT32 */ + else /* 16 BIT entries */ + { + fr_WORD((byte *) &u.wrdbuf[0],(word)value); /*X*/ + /* Now put the values back into the FAT */ + if (!fatxx_fword( pdr, clno, (word *) &u.wrdbuf[0], TRUE )) + { + return (FALSE); + } + } + return (TRUE); +} +#endif /* (RTFS_WRITE) */ + +/*************************************************************************** + PC_CLNEXT - Return the next cluster in a cluster chain + + + Description + Given a DDRIVE and a cluster number, return the next cluster in the + chain containing clno. Return 0 on end of chain. + + Returns + Return a new cluster number or 0xffffffff on end of chain. + +****************************************************************************/ + +/* Return the next cluster in a chain or 0xffffffff */ +CLUSTERTYPE fatxx_clnext(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__ */ +{ + CLUSTERTYPE nxt; +#if (FAT32) + dword _Oxffffffful; +#endif + /* Get the value at clno. return 0 on any io errors */ + if (! fatxx_faxx(pdr,clno,&nxt) ) + return (0); + + if (pdr->fasize == 3) /* 3 nibble ? */ + { + if ( (0xff7 < nxt) && (nxt <= 0xfff) ) + nxt = 0xffffffff; /* end of chain */ + } + else +#if (FAT32) + if (pdr->fasize == 8) + { + _Oxffffffful = 0x0ffffffful; + nxt &= _Oxffffffful; + if ( nxt == 0x0ffffffful ) + nxt = 0xffffffff; /* end of chain */ + } + else +#endif + { +#if (FAT32) + if ( (nxt >= (CLUSTERTYPE)0xfff7) && (nxt <= (CLUSTERTYPE)0xffff) ) +#else + /* If fat32 is not defined the nxt is always <= 0xffff. picky compilers + notice this and emit a warning */ + if (nxt >= (CLUSTERTYPE)0xfff7) +#endif + nxt = 0xffffffff; /* end of chain */ + } + if (nxt != 0xffffffff && (nxt < 2 || nxt > pdr->maxfindex) ) + { + rtfs_set_errno(PEINVALIDCLUSTER); + return (0); + } + return(nxt); +} + +/*************************************************************************** + PC_FAXX - Get the value store in the FAT at clno. + + Description + Given a DDRIVE and a cluster number. Get the value in the fat at clusterno + (the next cluster in a chain.) Handle 16 and 12 bit fats correctly. + + Returns + Returns the the value at clno. In pvalue. + If any error occured while FAT swapping return FALSE else return TRUE. +***************************************************************************/ + +/* Retrieve a value from the fat */ +BOOLEAN fatxx_faxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE *pvalue) /*__fatfn__*/ +{ + CLUSTERTYPE nibble,index, offset, result; + byte c; + union align1 { + byte wrdbuf[4]; /* Temp storage area */ + word fill[2]; + } u; + union align2 { + byte wrdbuf2[4]; /* Temp storage area */ + word fill[2]; + } u2; + if ((clno < 2) || (clno > pdr->maxfindex) ) + { + rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_faxx: bad cluster value internal error */ + return (FALSE); + } + result = 0; + if (pdr->fasize == 3) /* 3 nibble ? */ + { + nibble = (word)(clno * 3); + index = (word)(nibble >> 2); + offset = (word)(clno & 0x03); + /* Read the first word */ + if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], FALSE )) + return(FALSE); +/* + | W0 | W1 | W2 | + A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1 + xx xx xx +*/ + if (offset == 0) /* (A2 << 8) | A1 A2 */ + { + /* BYTE 0 == s Low byte, byte 1 low nibble == s high byte */ + u.wrdbuf[1] &= 0x0f; + result = u.wrdbuf[1]; + result <<= 8; + result |= u.wrdbuf[0]; + } +/* + | W0 | W1 | W2 | + A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1 + xx xx xx +*/ + + else if (offset == 1) /* (B1 B2 << 4) | B0 */ + { + /* BYTE 2 == High byte Byte 1 high nibb == low nib */ + if (!fatxx_fword( pdr, (word)(index+1), (word *) &u2.wrdbuf2[0], FALSE )) + return(FALSE); + c = (byte) (u.wrdbuf[1] >> 4); + result = u2.wrdbuf2[0]; + result <<= 4; + result |= c; + } +/* + | W0 | W1 | W2 | + A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1 + xx xx xx +*/ + else if (offset == 2) /*(C2 << 8) | C1 C2 */ + { + if (!fatxx_fword( pdr, (word)(index+1), (word *) &u2.wrdbuf2[0], FALSE )) + return(FALSE); + /* BYTE 1 == s Low byte, byte 2 low nibble == s high byte */ + result = (word) (u2.wrdbuf2[0] & 0x0f); + result <<= 8; + result |= u.wrdbuf[1]; + } +/* + | W0 | W1 | W2 | + A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1 + xx xx xx +*/ + + else if (offset == 3) /* (D2 D1) << 4 | D0 */ + { + result = u.wrdbuf[1]; + result <<= 4; + c = u.wrdbuf[0]; + c >>= 4; + result |= c; + } + } + else if (pdr->fasize == 8) /* 32 BIT fat. ret the value at 4 * clno */ + { +#if (FAT32) +#if KS_LITTLE_ENDIAN + if (!fatxx_pfgdword( pdr, clno, (dword *) &result )) + return (FALSE); +#else + if ( fatxx_pfgdword( pdr, clno, (dword *) &u.wrdbuf[0] )) + result = (CLUSTERTYPE) to_DWORD(&u.wrdbuf[0]); + else + return (FALSE); +#endif +#else + return (FALSE); +#endif + } + else /* 16 BIT fat. ret the value at 2 * clno */ + { + if ( fatxx_fword( pdr, clno, (word *) &u.wrdbuf[0], FALSE )) + result = (CLUSTERTYPE) to_WORD(&u.wrdbuf[0]); /*X*/ /* And use the product as index */ + else + return (FALSE); + } + *pvalue = result; + return (TRUE); +} + +/****************************************************************************** + PC_GET_CHAIN - Return as many contiguous clusters as possible. + + + Description + Starting at start_cluster return the number of contiguous clusters + allocated in the chain containing start_cluster or n_clusters, + whichever is less. + + Returns + Returns the number of contiguous clusters found. Or zero on an error. + This function should always return at least one. (start_cluster). Unless + an error occurs. + + The word at *pnext_cluster is filled with on of the following: + . If we went beyond a contiguous section it contains + the first cluster in the next segment of the chain. + . If we are still in a section it contains + the next cluster in the current segment of the chain. + . If we are at the end of the chain it contains the last cluster + in the chain. + +****************************************************************************/ +/* Note: The caller locks the fat before calling this routine */ + +CLUSTERTYPE fatxx_get_chain(DDRIVE *pdr, CLUSTERTYPE start_cluster, CLUSTERTYPE *pnext_cluster, CLUSTERTYPE n_clusters, int *end_of_chain) +{ + CLUSTERTYPE clno, next_cluster; + CLUSTERTYPE n_contig; + CLUSTERTYPE value; + + value = 0; + clno = start_cluster; + n_contig = 1; + *pnext_cluster = 0; + + /* Get each FAT entry. If its value points to the next contiguous entry + continue. Otherwise we have reached the end of the contiguous chain. + At which point we return the number of contig s found and by reference + the address of the FAT entry beginning the next chain segment. + */ + *end_of_chain = 0; + for (;;) + { + next_cluster = fatxx_clnext(pdr, clno); + if (!next_cluster) /* clnext detected an error */ + return(0); + /* check for end markers set next cluster to the last + cluster in the chain if we are at the end */ + if (next_cluster == 0xffffffff) /* clnext detected end */ + { + *end_of_chain = 1; + value = clno; + break; + } + else if (next_cluster == ++clno) + { + value = next_cluster; + if (n_contig >= n_clusters) + break; + n_contig++; + } + else /* (next_cluster != ++clno) and we know it is not an error condition */ + { + value = next_cluster; + break; + } + } + *pnext_cluster = value; + return (n_contig); +} + +/*************************************************************************** + PC_FATSW - Map in a page of the FAT +****************************************************************************/ + +/* Swap in the page containing index */ +/* Note: The caller locks the fat before calling this routine */ +byte * fatxx_pfswap(DDRIVE *pdr, CLUSTERTYPE index, BOOLEAN for_write) /*__fatfn__*/ +{ + dword block_offset_in_fat; + dword flags; + + if (pdr->fasize == 8) + block_offset_in_fat = (dword)(index >> 7); + else + block_offset_in_fat = (word) (index >> 8); + + if (block_offset_in_fat >= pdr->secpfat) /* Check range */ + return (0); + if (for_write) + flags = 0x80000000ul; + else + flags = 0x00000000ul; + return (pc_map_fat_block(pdr,pdr->fatblock+block_offset_in_fat, flags)); +} + +/* Put or get a WORD value into the fat at index */ +BOOLEAN fatxx_fword(DDRIVE *pdr, CLUSTERTYPE index, word *pvalue, BOOLEAN putting) /*__fatfn__*/ +{ + word *ppage; + word offset; + /* Make sure we have access to the page. Mark it for writing (if a put) */ + ppage = (word *)fatxx_pfswap(pdr,index,putting); + + if (!ppage) + return(FALSE); + else + { + /* there are 256 entries per page */ + offset = (word) (index & 0xff); + if (putting) + ppage[offset] = *pvalue; + else + *pvalue = ppage[offset]; + } + return(TRUE); +} + +FAT_DRIVER fatxx_d; + +static BOOLEAN init_fat(DDRIVE *pdr) +{ + FAT_DRIVER *pfd; +/* int driveno; + driveno = pdr->driveno;*/ + pfd = &fatxx_d; + pfd->fatop_alloc_chain = fatxx_alloc_chain; + pfd->fatop_clnext = fatxx_clnext; + pfd->fatop_clrelease_dir = fatxx_clrelease_dir; + pfd->fatop_faxx = fatxx_faxx; + pfd->fatop_flushfat = fatxx_flushfat; + pfd->fatop_freechain = fatxx_freechain; + pfd->fatop_cl_truncate_dir = fatxx_cl_truncate_dir; + pfd->fatop_get_chain = fatxx_get_chain; + pfd->fatop_pfaxxterm = fatxx_pfaxxterm; + pfd->fatop_pfaxx = fatxx_pfaxx; + pdr->fad = (void *) pfd; + return(TRUE); +} + +BOOLEAN faxx_check_free_space(DDRIVE *pdr) +{ + CLUSTERTYPE i; + CLUSTERTYPE nxt; + long freecount = 0; + + for (i = 2 ; i <= pdr->maxfindex; i++) + { + if (!FATOP(pdr)->fatop_faxx(pdr, i, &nxt)) /* Any (ifree) */ + return(FALSE); + if (nxt == 0) + freecount++; + } + pdr->known_free_clusters = freecount; + return(TRUE); +} + +BOOLEAN init_fat32(DDRIVE *pdr) +{ + return(init_fat(pdr) && faxx_check_free_space(pdr)); +} +BOOLEAN init_fat16(DDRIVE *pdr) +{ + return(init_fat(pdr) && faxx_check_free_space(pdr)); +} +BOOLEAN init_fat12(DDRIVE *pdr) +{ + return(init_fat(pdr) && faxx_check_free_space(pdr)); +} + + + diff --git a/build/libraries/fatfs/ARM7/rtkernfn.c b/build/libraries/fatfs/ARM7/rtkernfn.c new file mode 100644 index 0000000..fc4e5c7 --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtkernfn.c @@ -0,0 +1,522 @@ +/* +* rtkernfn.c - Miscelaneous portable functions +* +* ERTFS portable process management and other functions. +* This file is portable but it requires interaction with the +* porting layer functions in portrtfs.c. +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +*/ + +#include + + +BOOLEAN rtfs_resource_init(void) /*__fn__*/ +{ + + prtfs_cfg->critical_semaphore = rtfs_port_alloc_mutex(); + if (!prtfs_cfg->critical_semaphore) + return(FALSE); + +/* Note: cfg_NDRIVES semaphores are allocated and assigned to the individual + drive structure within routine pc_ertfs_init() */ + + /* Initialize a user table */ + rtfs_memset((byte *)prtfs_cfg->rtfs_user_table, 0, sizeof(RTFS_SYSTEM_USER)*prtfs_cfg->cfg_NUM_USERS); + + return(TRUE); +} + +PRTFS_SYSTEM_USER rtfs_get_system_user(void) +{ +int i; +dword t; + + t = rtfs_port_get_taskid(); + if (prtfs_cfg->cfg_NUM_USERS == 1) + return(&prtfs_cfg->rtfs_user_table[0]); + + for (i = 0; i < prtfs_cfg->cfg_NUM_USERS; i++) + { + if (t == prtfs_cfg->rtfs_user_table[i].task_handle) + return(&prtfs_cfg->rtfs_user_table[i]); + } + /* Did not find one.. so assign one from between 1 and n */ + for (i = 1; i < prtfs_cfg->cfg_NUM_USERS; i++) + { + if (!prtfs_cfg->rtfs_user_table[i].task_handle) + { +return_it: + rtfs_memset((byte *)&prtfs_cfg->rtfs_user_table[i], 0, sizeof(RTFS_SYSTEM_USER)); + prtfs_cfg->rtfs_user_table[i].task_handle = t; + return(&prtfs_cfg->rtfs_user_table[i]); + } + } + /* We are out of user structures so use element 0 */ + i = 0; + goto return_it; +} + +void pc_free_user(void) /*__fn__*/ +{ +int i; +PRTFS_SYSTEM_USER s; + s = rtfs_get_system_user(); + if (s) + { + for (i = 0; i < prtfs_cfg->cfg_NDRIVES; i++) + { + if (s->lcwd[i]) + { + pc_freeobj((DROBJ *) s->lcwd[i]); + s->lcwd[i] = 0; + } + } + rtfs_memset((byte *)s, 0, sizeof(*s)); + } +} + +/* pc_free_all_users() - Run down the user list releasing drive resources + * + * This routine is called by RTFS when it closes a drive. + * The routine must release the current directory object for that drive + * for each user. If a user does not have a CWD for the drive it should + * not call pc_freeobj. + * + * In the reference port we cycle through our array of user structures + * to provide the enumeration. Other implementations are equally valid. + */ + +void pc_free_all_users(int driveno) /*__fn__*/ +{ +int i; + if (prtfs_cfg->cfg_NUM_USERS == 1) + { + if (prtfs_cfg->rtfs_user_table[0].lcwd[driveno]) + { + pc_freeobj((DROBJ *)prtfs_cfg->rtfs_user_table[0].lcwd[driveno]); + prtfs_cfg->rtfs_user_table[0].lcwd[driveno] = 0; + } + } + else + { + for (i = 0; i < prtfs_cfg->cfg_NUM_USERS; i++) + { + if (prtfs_cfg->rtfs_user_table[i].task_handle && prtfs_cfg->rtfs_user_table[i].lcwd[driveno]) + { + pc_freeobj((DROBJ *)prtfs_cfg->rtfs_user_table[i].lcwd[driveno]); + prtfs_cfg->rtfs_user_table[i].lcwd[driveno] = 0; + } + } + } +} + + +/* int rtfs_set_driver_errno() - set device driver errno for the calling task + + Saves driver errno for the calling task in array based on callers taskid. + + Note: This routine must not be called from the interrupt service layer + + Returns nothing +*/ +void rtfs_set_driver_errno(dword error) /*__fn__*/ +{ + rtfs_get_system_user()->rtfs_driver_errno = error; +} + + +/* ******************************************************************** + +dword rtfs_get_driver_errno() - get device driver errno for the calling task + + Returns device driver errno for the calling task in array based on + callers taskid. +*/ + +dword rtfs_get_driver_errno(void) /*__fn__*/ +{ + return(rtfs_get_system_user()->rtfs_driver_errno); +} + + +/* int rtfs_set_errno() - set errno for the calling task + + Saves errno for the calling task in array based on callers taskid. + + Returns -1 +*/ +int rtfs_set_errno(int error) /*__fn__*/ +{ + rtfs_get_system_user()->rtfs_errno = error; + return(-1); +} + + +/* ******************************************************************** + +int get_errno() - get errno for the calling task + + Returns errno for the calling task in array based on callers taskid. +*/ + +int get_errno(void) /*__fn__*/ +{ + return(rtfs_get_system_user()->rtfs_errno); +} + +/* Miscelaneous functions */ + +void pc_report_error(int error_number) /*__fn__*/ +{ + RTFS_PRINT_STRING_1(USTRING_PORTKERN_03, 0); /* "pc_report_error was called with error" */ + RTFS_PRINT_LONG_1((dword) error_number, PRFLG_NL); +} + +/* This routine will be called if an IO error occurs. It must return + either CRITICAL_ERROR_ABORT to have the operation aborted and + the drive to be unmounted or CRITICAL_ERROR_RETRY to force a retry + of the operation. + + This routine prompts the user to Abort or Retry. If console input + is not implemented it return Abort. This routine may be modified + to take corrective action (such as ask the user to reinsert the media) + before return Retry or Abort +*/ + +KS_CONSTANT int med_st[] = +{ + USTRING_SYS_NULL, /* "" */ + USTRING_CRITERR_02, /* "BAD_FORMAT" */ + USTRING_CRITERR_03, /* "CRERR_NO_CARD" */ + USTRING_CRITERR_04, /* "CRERR_BAD_CARD" */ + USTRING_CRITERR_05, /* "CRERR_CHANGED_CARD" */ + USTRING_CRITERR_06, /* "CRERR_CARD_FAILURE" */ +}; + +int critical_error_handler(int driveno, int media_status, dword sector) +{ +byte inbuf[20]; +byte *p; +DDRIVE *pdr; +BOOLEAN needs_flush; + +/* If you do not have console input you may return abort unconditionally */ +/* return (CRITICAL_ERROR_ABORT); */ + + RTFS_ARGSUSED_PVOID((void *) sector); + pdr = pc_drno_to_drive_struct(driveno); + + if (pdr->fat_is_dirty || pc_test_all_fil(pdr)) + needs_flush = TRUE; + else + needs_flush = FALSE; + + RTFS_PRINT_STRING_1(USTRING_CRITERR_07,0); /* "Media status == " */ + RTFS_PRINT_STRING_1(med_st[media_status],PRFLG_NL); + RTFS_PRINT_STRING_2(USTRING_CRITERR_08, pdr->volume_label, PRFLG_NL); /* "Volume == " */ + + if (needs_flush) + RTFS_PRINT_STRING_1(USTRING_CRITERR_09, PRFLG_NL); /* "Volume is dirty" */ + else + RTFS_PRINT_STRING_1(USTRING_CRITERR_10, PRFLG_NL); /* "Volume is clean" */ + + /* default to ABORT. If rtfs_print_prompt_user() is not implemented + then ABORT will be the default behavior */ + p = inbuf; + CS_OP_ASSIGN_ASCII(p,'A'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + return (CRITICAL_ERROR_ABORT); /* ctr modified */ +// for (;;) +// { +// /* "Type A to abort R to Retry" */ +// rtfs_print_prompt_user(UPROMPT_CRITERR, inbuf); +// if (CS_OP_CMP_ASCII(inbuf,'A') || CS_OP_CMP_ASCII(inbuf,'a')) +// return (CRITICAL_ERROR_ABORT); +// if (CS_OP_CMP_ASCII(inbuf,'R') || CS_OP_CMP_ASCII(inbuf,'r')) +// return (CRITICAL_ERROR_RETRY); +// } +} + +/************************************************************************** + PC_NUM_DRIVES - Return total number of drives in the system + + Description + This routine returns the number of drives in the system + + Returns + The number +*****************************************************************************/ +int pc_num_drives(void) /* __fn__ */ +{ + return(prtfs_cfg->cfg_NDRIVES); +} + +/************************************************************************** + PC_NUSERFILES - Return total number of uses allowed in the system + + Description + This routine returns the number of user in the system + + Returns + The number +*****************************************************************************/ +int pc_num_users(void) /* __fn__ */ +{ + return(prtfs_cfg->cfg_NUM_USERS); +} + +/************************************************************************** + PC_NUSERFILES - Return total number of userfiles alloed in the system + + Description + This routine returns the number of user files in the system + + Returns + The number +*****************************************************************************/ + +int pc_nuserfiles(void) /* __fn__ */ +{ + return(prtfs_cfg->cfg_NUSERFILES); +} + +/************************************************************************** + PC_VALIDATE_DRIVENO - Verify that a drive number is <= prtfs_cfg->cfg_NDRIVES + + Description + This routine is called when a routine is handed a drive number and + needs to know if it is within the number of drives set during + the congiguration. + + Returns + TRUE if the drive number is valid or FALSE. +*****************************************************************************/ + +BOOLEAN pc_validate_driveno(int driveno) /* __fn__ */ +{ + if ((driveno < 0) || (driveno > 25) || (!prtfs_cfg->drno_to_dr_map[driveno]) ) + return(FALSE); + else + return(TRUE); +} + +/************************************************************************** + PC_MEMORY_INIT - Initialize and allocate File system structures. + + THIS ROUTINE MUST BE CALLED BEFORE ANY FILE SYSTEM ROUTINES !!!!!! + IT IS CALLED BY THE PC_ERTFS_INIT() Function + + Description + This routine must be called before any file system routines. Its job + is to allocate tables needed by the file system. We chose to implement + memory management this way to provide maximum flexibility for embedded + system developers. In the reference port we use malloc to allocate the + various chunks of memory we need, but we could just have easily comiled + the tables into the BSS section of the program. + + Use whatever method makes sense in you system. + + Note the total number of bytes allocated by this routine is: + (sizeof(DDRIVE) * prtfs_cfg->cfg_NDRIVES) + (sizeof(PC_FILE)*NUSERFILES) + + (sizeof(BLKBUFF)*NBLKBUFFS)+ (sizeof(DROBJ)*NDROBJS) + + (sizeof(FINODE)*NFINODES) + + + Returns + TRUE on success or no ON Failure. +*****************************************************************************/ +BOOLEAN pc_memory_init(void) /*__fn__*/ +{ + int i,j,l; + DROBJ *pobj; + FINODE *pfi; + DDRIVE *pdrive; + + /* Call the kernel level initialization code */ + if (!rtfs_resource_init()) + return(FALSE); +/* + =!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!= + We simply assign our pointers to the placeholders in + the BSS + =!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!= + */ + /* Initialize the drive structures */ + l =sizeof(DDRIVE); l *= prtfs_cfg->cfg_NDRIVES; + rtfs_memset(prtfs_cfg->mem_drives_structures, (byte) 0, (int)l); + + /* Initialize the directory block buffer array */ + if (!pc_initialize_block_pool( + &prtfs_cfg->buffcntxt, prtfs_cfg->cfg_NBLKBUFFS, + prtfs_cfg->mem_block_pool, prtfs_cfg->cfg_BLK_HASHTBLE_SIZE, + prtfs_cfg->mem_block_hash_table)) + return(FALSE); + + /* Initialize buffer pools for each drive */ + for (pdrive=prtfs_cfg->mem_drives_structures,i = 0; + i < prtfs_cfg->cfg_NDRIVES; i++, pdrive++) + { + /* Use the globally shared block buffer pool. + this can be overriden through the API to assign a private buffer pool to the drive */ + pdrive->pbuffcntxt = &prtfs_cfg->buffcntxt; + + /* Initialize the fat block buffer array */ + if (!pc_initialize_fat_block_pool( + &pdrive->fatcontext, + prtfs_cfg->cfg_FAT_BUFFER_SIZE[i], prtfs_cfg->fat_buffers[i], + prtfs_cfg->cfg_FAT_HASHTBL_SIZE[i], prtfs_cfg->fat_hash_table[i], + prtfs_cfg->fat_primary_cache[i], prtfs_cfg->fat_primary_index[i])) + return(FALSE); + + } + /* make a NULL terminated freelist of the DROBJ pool using + pdrive as the link. This linked freelist structure is used by the + DROBJ memory allocator routine. */ + pobj = prtfs_cfg->mem_drobj_freelist = prtfs_cfg->mem_drobj_pool; + pobj->is_free = TRUE; + for (i = 0,j = 1; i < prtfs_cfg->cfg_NDROBJS-1; i++, j++) + { + pobj = prtfs_cfg->mem_drobj_freelist + j; + pobj->is_free = TRUE; + prtfs_cfg->mem_drobj_freelist[i].pdrive = (DDRIVE *) pobj; + } + prtfs_cfg->mem_drobj_freelist[prtfs_cfg->cfg_NDROBJS-1].pdrive = 0; + + /* Make a NULL terminated FINODE freelist using + pnext as the link. This linked freelist is used by the FINODE + memory allocator routine */ + pfi = prtfs_cfg->mem_finode_freelist = prtfs_cfg->mem_finode_pool; + for (i = 0; i < prtfs_cfg->cfg_NFINODES-1; i++) + { + pfi->is_free = TRUE; + pfi++; + prtfs_cfg->mem_finode_freelist->pnext = pfi; + prtfs_cfg->mem_finode_freelist++; + prtfs_cfg->mem_finode_freelist->pnext = 0; + } + /* Mark all user files free */ + for (i = 0; i < prtfs_cfg->cfg_NUSERFILES; i++) + prtfs_cfg->mem_file_pool[i].is_free = TRUE; + + prtfs_cfg->mem_finode_freelist = prtfs_cfg->mem_finode_pool; + + return(TRUE); +} + +/************************************************************************** + PC_MEMORY_DROBJ - Allocate a DROBJ structure + Description + If called with a null pointer, allocates and zeroes the space needed to + store a DROBJ structure. If called with a NON-NULL pointer the DROBJ + structure is returned to the heap. + + Returns + If an ALLOC returns a valid pointer or NULL if no more core. If a free + the return value is the input. + +*****************************************************************************/ + +DROBJ *pc_memory_drobj(DROBJ *pobj) /*__fn__*/ +{ +DROBJ *preturn; + preturn = 0; + if (pobj) + { + OS_CLAIM_FSCRITICAL() + if (!pobj->is_free) + { + pobj->is_free = TRUE; + /* Free it by putting it at the head of the freelist + NOTE: pdrive is used to link the freelist */ + pobj->pdrive = (DDRIVE *) prtfs_cfg->mem_drobj_freelist; + prtfs_cfg->mem_drobj_freelist = pobj; + } + OS_RELEASE_FSCRITICAL() + } + else + { + /* Alloc: return the first structure from the freelist */ + OS_CLAIM_FSCRITICAL() + preturn = prtfs_cfg->mem_drobj_freelist; + if (preturn) + { + prtfs_cfg->mem_drobj_freelist = (DROBJ *) preturn->pdrive; + rtfs_memset(preturn, (byte) 0, sizeof(DROBJ)); + OS_RELEASE_FSCRITICAL() + } + else + { + OS_RELEASE_FSCRITICAL() + rtfs_set_errno(PERESOURCE); /* pc_memory_drobj: out drobj of resources */ + pc_report_error(PCERR_DROBJALLOC); + } + } + return(preturn); +} + + +/************************************************************************** + PC_MEMORY_FINODE - Allocate a FINODE structure + Description + If called with a null pointer, allocates and zeroes the space needed to + store a FINODE structure. If called with a NON-NULL pointer the FINODE + structure is returned to the heap. + + Returns + If an ALLOC returns a valid pointer or NULL if no more core. If a free + the return value is the input. + +*****************************************************************************/ + +FINODE *pc_memory_finode(FINODE *pinode) /*__fn__*/ +{ +FINODE *preturn; +BLKBUFF *pfile_buffer; + + if (pinode) + { + pfile_buffer = 0; + OS_CLAIM_FSCRITICAL() + if (!pinode->is_free) + { + /* Free it by putting it at the head of the freelist */ + pfile_buffer = pinode->pfile_buffer; + pinode->pfile_buffer = 0; + pinode->is_free = TRUE; + pinode->pnext = prtfs_cfg->mem_finode_freelist; + prtfs_cfg->mem_finode_freelist = pinode; + } + OS_RELEASE_FSCRITICAL() + if (pfile_buffer) + pc_free_scratch_blk(pfile_buffer); + preturn = pinode; + } + else + { + /* Alloc: return the first structure from the freelist */ + OS_CLAIM_FSCRITICAL() + preturn = prtfs_cfg->mem_finode_freelist; + if (preturn) + { + prtfs_cfg->mem_finode_freelist = preturn->pnext; + /* Zero the structure */ + rtfs_memset(preturn, (byte) 0, sizeof(FINODE)); + OS_RELEASE_FSCRITICAL() + } + else + { + OS_RELEASE_FSCRITICAL() + rtfs_set_errno(PERESOURCE); /* pc_memory_finode: out finode of resources */ + pc_report_error(PCERR_FINODEALLOC); + } + } + return(preturn); +} + + diff --git a/build/libraries/fatfs/ARM7/rtlowl.c b/build/libraries/fatfs/ARM7/rtlowl.c new file mode 100644 index 0000000..1e7a935 --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtlowl.c @@ -0,0 +1,711 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* RTLOWL.C - Low level functions that don't directly manipulate the fat. + + Routines in this file include: + + pc_i_dskopen - Mount a drive if you can + pc_read_partition_table + - Load a drive structure with partition info + pc_gblk0 - Read block zero and set up internal structures. + pc_clzero - Write zeroes to a cluster on disk. + pc_drno2dr - Convert a drive number to a drive structure. + pc_dskfree - Free resources associated with a drive. + pc_sec2cluster - Convert a sector number to a cluster value. + pc_sec2index - Convert a sector number to a cluster offset. + pc_cl2sector - Convert a cluster value to a sector number. + pc_pfinode_cluster - Assign a cluster value to a finode + pc_finode_cluster - Get the cluster value from a finode +*/ + +#include + +/****************************************************************************** + PC_I_DSKOPEN - Open a disk for business. + + Description + Called by lower level code in chkmedia to open the disk + + + Returns + Returns TRUE if the disk was successfully initialized. +****************************************************************************/ + +int pc_log_base_2(word n) /*__fn__*/ +{ +int log; + + log = 0; + if (n <= 1) + return(log); + + while(n) + { + log += 1; + n >>= 1; + } + return((int)(log-1)); +} + +/* +* Note: This routine is called with the drive already locked so +* in several cases there is no need for critical section code handling +* This is a helper function for pc_i_dskopen() +*/ + +BOOLEAN pc_init_drv_fat_info(DDRIVE *pdr, struct pcblk0 *pbl0); + +BOOLEAN pc_i_dskopen(int driveno) /*__fn__*/ +{ + DDRIVE *pdr; + struct pcblk0 bl0; + BOOLEAN ret_val; + int partition_status; + + if (!prtfs_cfg) + { + /* Failed: pc_meminit() must not have been called */ + pc_report_error(PCERR_INITCORE); + return (FALSE); + } + + /* Check drive number */ + if (!pc_validate_driveno(driveno)) + { + rtfs_set_errno(PEINVALIDDRIVEID); + return(FALSE); + } + + pdr = pc_drno_to_drive_struct(driveno); + + /* Do not do anything on reopens */ + if (pdr->mount_valid) + { + return(TRUE); + } + else + { + /* Zero the structure so all of our initial values are right */ + OS_CLAIM_FSCRITICAL() + { + byte *p1, *p2; + p1 = (byte *) pdr; + p2 = (byte *) &pdr->begin_user_area; + while(p1 < p2) *p1++ = 0; + /* Set driveno now because we overwrote it */ + pdr->driveno = (word)driveno; + } + OS_RELEASE_FSCRITICAL() + } + + /* Set this to true now so check media does not try to mount */ + pdr->mount_valid = TRUE; + partition_status = 0; + if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED) + { + partition_status = pc_read_partition_table(driveno, pdr); + if (partition_status != READ_PARTION_OK) + { + /* If we read a block but didn't see a partition table signature + and this drive structure is for the first partition number + then fall through and see if there is a BPB at block 0. */ + if (partition_status == READ_PARTION_NO_TABLE && pdr->partition_number == 0) + { + rtfs_set_errno(0); /* Clear errno, we'll set it later */ + pdr->partition_base = 0; /* Is BPB at 0 ? */ + } + else + { + pc_report_error(PCERR_INITDEV); + return_error: + pdr->mount_valid = FALSE; + return(FALSE); + } + } + } + + /* Read block 0 */ + if (!pc_gblk0((word) driveno, &bl0 )) + { + /* pc_gblk0 set errno */ + pc_report_error(PCERR_INITREAD); + goto return_error; + } + + /* Verify that we have a good dos formatted disk */ + if ( (bl0.jump != (byte) 0xE9) && (bl0.jump !=(byte) 0xEB) ) + { + /* If we were checking for an MBR at physical block 0 with a driver + set for partitioned devices, set PEINVALIDMBR, otherwise + set PEINVALIDBPB */ + if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED && partition_status == READ_PARTION_NO_TABLE) + rtfs_set_errno(PEINVALIDMBR); + else + rtfs_set_errno(PEINVALIDBPB); /* pc_i_dskopen Unkown values in Bios Parameter block */ + pc_report_error(PCERR_INITMEDI); + goto return_error; + } + + /* set up the drive structur from block 0 */ + pdr->bytspsector = bl0.bytspsector; /* bytes/sector */ + pdr->secpalloc = bl0.secpalloc; /* sectors / cluster */ + pdr->numroot = bl0.numroot; /* Maximum number of root entries */ + pdr->numsecs = (BLOCKT) bl0.numsecs; /* Total sectors on the disk */ + pdr->mediadesc = bl0.mediadesc; /* Media descriptor byte */ + pdr->secreserved = bl0.secreserved; /* sectors reserved */ + pdr->secptrk = bl0.secptrk; /* sectors per track */ + pdr->numhead = bl0.numhead; /* number of heads */ + pdr->numhide =bl0.numhide; /* # hidden sectors */ + + copybuff(pdr->volume_label, &bl0.vollabel[0], 11); + pdr->volume_label[11] = 0; + + pdr->volume_serialno = bl0.volid; + +/* Check if running on a DOS (4.0) huge partition */ + /* If traditional total # sectors is zero, use value in extended BPB */ + if (pdr->numsecs == 0L) + pdr->numsecs = bl0.numsecs2; /* (4.0) */ + + /* derive some things */ + pdr->bytespcluster = (int) (512 * pdr->secpalloc); + /* bits to mask in to calculate byte offset in cluster from file pointer. + AND file pointer with this to get byte offset in cluster a shift right + 9 to get block offset in cluster */ + pdr->byte_into_cl_mask = (dword) pdr->bytespcluster; + pdr->byte_into_cl_mask -= 1L; + /* save away log of sectors per alloc */ + pdr->log2_secpalloc = (int)pc_log_base_2((word)pdr->secpalloc); + + /* Get + pdr->secpfat pdr->numfats pdr->fatblock + pdr->rootblock pdr->secproot pdr->firstclblock + pdr->maxfindex pdr->infosec pdr->fasize + pdr->free_contig_base + pdr->free_contig_pointer + pdr->known_free_clusters + */ + pc_init_drv_fat_info(pdr, &bl0); + + /* Load the fat driver functions and initilialize the fat */ + /* Init_fat will set errno */ + if (pdr->fasize == 8) + ret_val = init_fat32(pdr); + else if (pdr->fasize == 4) + ret_val = init_fat16(pdr); + else if (pdr->fasize == 3) + ret_val = init_fat12(pdr); + else + { + rtfs_set_errno(PEINVALIDBPB); /* pc_i_dskopen Unkown values in Bios Parameter block */ + ret_val = 0; + } + if (!ret_val) + { + pc_report_error(PCERR_FATREAD); + goto return_error; + } + pdr->mount_valid = TRUE; + /* Save Unique id for this mount */ + prtfs_cfg->drive_opencounter += 1; + pdr->drive_opencounter = prtfs_cfg->drive_opencounter; + return(TRUE); +} + +/****************************************************************************** + pc_read_partition_table() - Load a drive structure with partition info + + Description + Read the partition table from a disk. If one is found then check the + entry in the table that is specified by pdr->partition_number. If the + entry is valid then load the fields + pdr->partition_base,pdr->partition_size and pdr->partition_type; + + Returns + The following values. + + READ_PARTION_OK Partition read succesfully + READ_PARTION_ERR Internal error (could not allocate buffers ?) + READ_PARTION_NO_TABLE No partition table found + READ_PARTION_NO_ENTRY Request entry not found + READ_PARTION_IOERROR Device IO error + +****************************************************************************/ +int pc_read_partition_table(int driveno, DDRIVE *pdr) +{ + PTABLE *ppart; + BLKBUFF *buf; + byte *pbuf; + word i; + int ret_val; + + /* Grab some working space */ + buf = pc_scratch_blk(); + if (!buf) + { + return(READ_PARTION_ERR); + } + + /* Read block zero */ + if (!devio_read(driveno, 0 , buf->data , 1, TRUE)) + { + /* Failed reading Master boot record */ + rtfs_set_errno(PEIOERRORREADMBR); + ret_val = READ_PARTION_IOERROR; + goto done; + } + /* Copy the table to a word alligned buffer */ + pbuf = buf->data; + pbuf += 0x1be; /* The info starts at buf[1be] */ + /* Don't use sizeof here since the structure does not pack to exact size */ + copybuff(buf->data, pbuf, 0x42); + ppart = (PTABLE *) buf->data; + + if (to_WORD((byte *) &ppart->signature) != 0xAA55) /*X*/ + { + rtfs_set_errno(PEINVALIDMBR); + ret_val = READ_PARTION_NO_TABLE; + goto done; + } +#if (SUPPORT_EXTENDED_PARTITIONS) +{ + /* Special code to support extended partitions */ + /* since most applications don't use these we compile it + conditionally to save code space */ + dword extended_base, ltemp; + int j, skip_count; + for (j = 0; j < 4; j++) + { + if (ppart->ents[j].p_typ == 0x5 || ppart->ents[j].p_typ == 0xF) + { + if (j <= (int)pdr->partition_number) + { + /* the partition is inside an extended partition */ + /* Get the relative start and size */ + pdr->partition_base = to_DWORD ((byte *) &ppart->ents[j].r_sec); + extended_base = pdr->partition_base; + pdr->partition_size = to_DWORD ((byte *) &ppart->ents[j].p_size); + skip_count = (int)pdr->partition_number - j; + for (;;) + { + /* Read the partition information in the extended partition */ + if (!devio_read(driveno, pdr->partition_base , buf->data , 1, TRUE)) + { + /* Failed reading Master boot record */ + rtfs_set_errno(PEIOERRORREADMBR); + ret_val = READ_PARTION_IOERROR; + goto done; + } + /* Copy the table to a word alligned buffer */ + pbuf = buf->data; + pbuf += 0x1be; /* The info starts at buf[1be] */ + /* Don't use sizeof here since the structure does not pack to exact size */ + copybuff(buf->data, pbuf, 0x42); + ppart = (PTABLE *) buf->data; + + if (to_WORD((byte *) &ppart->signature) != 0xAA55) /*X*/ + { + rtfs_set_errno(PEINVALIDMBR); + ret_val = READ_PARTION_NO_TABLE; + goto done; + } + if (skip_count==0) + { + if (pc_validate_partition_type(ppart->ents[0].p_typ)) + { + pdr->partition_type = ppart->ents[0].p_typ; + ltemp = pdr->partition_base; + pdr->partition_base = ltemp + to_DWORD ((byte *) &ppart->ents[0].r_sec); + pdr->partition_size = to_DWORD ((byte *) &ppart->ents[0].p_size); + ret_val = READ_PARTION_OK; + goto done; + } + else + { + rtfs_set_errno(PEINVALIDMBROFFSET); + ret_val = READ_PARTION_NO_TABLE; + goto done; + } + } + else + { + if (ppart->ents[1].p_typ != 0x5 && ppart->ents[1].p_typ != 0xF) + { + rtfs_set_errno(PEINVALIDMBROFFSET); + ret_val = READ_PARTION_NO_TABLE; + goto done; + } + pdr->partition_base = extended_base + to_DWORD ((byte *) &ppart->ents[1].r_sec); + pdr->partition_size = to_DWORD ((byte *) &ppart->ents[1].p_size); + skip_count -= 1; + } + } + } + } + } +} + /* Fall through to here if not in an extended partition */ +#endif /* (SUPPORT_EXTENDED_PARTITIONS) */ + i = (word)pdr->partition_number; + + if (pc_validate_partition_type(ppart->ents[i].p_typ)) + { + /* Get the relative start and size */ + pdr->partition_base = to_DWORD ((byte *) &ppart->ents[i].r_sec); + pdr->partition_size = to_DWORD ((byte *) &ppart->ents[i].p_size); + pdr->partition_type = ppart->ents[i].p_typ; + ret_val = READ_PARTION_OK; + } + else + { + rtfs_set_errno(PEINVALIDMBROFFSET); + ret_val = READ_PARTION_NO_TABLE; + } + +done: + pc_free_scratch_blk(buf); + return(ret_val); +} + + +/**************************************************************************** + PC_GBLK0 - Read block 0 and load values into a a structure + + Description + Given a valid drive number, read block zero and convert + its contents from intel to native byte order. + + Returns + Returns TRUE if all went well. + +****************************************************************************/ + +/* read block zero */ +BOOLEAN pc_gblk0(word driveno, struct pcblk0 *pbl0) /*__fn__*/ +{ + BLKBUFF *buf; + byte *b; + /* Zero fill pbl0 so we do not get any surprises */ + rtfs_memset(pbl0, (byte) 0,sizeof(struct pcblk0)); + /* Grab a buffer to play with */ + buf = pc_scratch_blk(); + if (!buf) + { + rtfs_set_errno(PERESOURCEBLOCK); /* pc_gblk0 couldn't allocate a buffer */ + return(FALSE); + } + b = buf->data; /* Now we do not have to use the stack */ + + /* get 1 block starting at 0 from driveno */ + /* READ */ + if (!devio_read(driveno, 0L ,b,1, FALSE)) + { + rtfs_set_errno(PEIOERRORREADBPB); /* pc_gblk0 failed reading Bios Parameter block */ + pc_free_scratch_blk(buf); + return(FALSE); + } + /* Now load the structure from the buffer */ + pbl0->jump = b[0]; + copybuff( &pbl0->oemname[0],b+3,8); + pbl0->oemname[8] = CS_OP_ASCII('\0'); + pbl0->secpalloc = b[0xd]; + pbl0->numfats = b[0x10]; + pbl0->mediadesc = b[0x15]; + pbl0->physdrv = b[0x24]; /* Physical Drive No. (4.0) */ + pbl0->xtbootsig = b[0x26]; /* Extended signt 29H if 4.0 stuf valid */ + /* BUG FIX 12-1-99 - Add KS_LITTLE_ODD_PTR_OK flag to split between + big endian and little endian system. The top section works on little + endian systems that do not require even alligned word accesses like + the x86 but for example on Little endian ARM systems these assignments + derefrencing a pointer to word at an odd address which gives bad data .*/ + pbl0->bytspsector = to_WORD(b+0xb); /*X*/ + pbl0->secreserved = to_WORD(b+0xe); /*X*/ + pbl0->numroot = to_WORD(b+0x11); /*X*/ + pbl0->numsecs = to_WORD(b+0x13); /*X*/ + pbl0->secpfat = to_WORD(b+0x16); /*X*/ + pbl0->secptrk = to_WORD(b+0x18); /*X*/ + pbl0->numhead = to_WORD(b+0x1a); /*X*/ + pbl0->numhide = to_WORD(b+0x1c); /*X*/ + pbl0->numhide2 = to_WORD(b+0x1e); /*X*/ + pbl0->numsecs2 = to_DWORD(b+0x20);/*X*/ /* # secs if > 32M (4.0) */ + pbl0->volid = to_DWORD(b+0x27);/*X*/ /* Unique number per volume (4.0) */ + copybuff( &pbl0->vollabel[0],b+0x2b,11); /* Volume label (4.0) */ + + if (pbl0->numroot == 0 && !pc_gblk0_32(driveno, pbl0, b)) + { + pc_free_scratch_blk(buf); + rtfs_set_errno(PEIOERRORREADINFO32); /* pc_gblk0_32 failed reading Bios Parameter block */ + return(FALSE); + } + pc_free_scratch_blk(buf); + return(TRUE); +} + + +#if (RTFS_WRITE) +#if (RTFS_SUBDIRS) +/*************************************************************************** + PC_CLZERO - Fill a disk cluster with zeroes + + Description + Write zeroes into the cluster at clusterno on the drive pointed to by + pdrive. Used to zero out directory and data file clusters to eliminate + any residual data. + + Returns + Returns FALSE on a write erro. + +****************************************************************************/ + +/* Write zeros to all blocks in a cluster */ +BOOLEAN pc_clzero(DDRIVE *pdrive, CLUSTERTYPE cluster) /*__fn__*/ +{ + BLKBUFF *pbuff; + CLUSTERTYPE i; + BLOCKT currbl; + + currbl = pc_cl2sector(pdrive , cluster); + if (!currbl) + return (FALSE); + + /*Init and write a block for each block in cl. Note: init clears the core */ + for (i = 0; i < pdrive->secpalloc; i++, currbl++ ) + { + pbuff = pc_init_blk( pdrive , currbl); + if (!pbuff) + { + return (FALSE); + } + if (!pc_write_blk ( pbuff ) ) + { + pc_discard_buf(pbuff); + return (FALSE); + } + pc_release_buf(pbuff); + } + return (TRUE); +} +#endif +#endif + +/**************************************************************************** + PC_DRNO2DR - Convert a drive number to a pointer to DDRIVE + + Description + Given a drive number look up the DDRIVE structure associated with it. + + Returns + Returns NULL if driveno is not an open drive. + +****************************************************************************/ + +DDRIVE *pc_drno_to_drive_struct(int driveno) /*__fn__*/ +{ +DDRIVE *pdr; + + pdr = 0; + /* Check drive number */ + if (pc_validate_driveno(driveno)) + { + pdr = prtfs_cfg->drno_to_dr_map[driveno]; + } + return(pdr); +} + +DDRIVE *pc_drno2dr(int driveno) /*__fn__*/ +{ +DDRIVE *pdr; +DDRIVE *pretval; + + pdr = pc_drno_to_drive_struct(driveno); + pretval = 0; + + OS_CLAIM_FSCRITICAL() + /* Check drive number */ + if (pdr) + { + if (pdr->mount_valid) + { + pretval = pdr; + } + } + OS_RELEASE_FSCRITICAL() + return(pretval); +} + +/*************************************************************************** + PC_DSKFREE - Deallocate all core associated with a disk structure + + Description + Given a valid drive number. If the drive open count goes to zero, free the + file allocation table and the block zero information associated with the + drive. If unconditional is true, ignore the open count and release the + drive. + If open count reaches zero or unconditional, all future accesses to + driveno will fail until re-opened. + + Returns + Returns FALSE if driveno is not an open drive. + +****************************************************************************/ + + +/* free up all core associated with the drive + called by close. A drive restart would consist of + pc_dskfree(driveno, TRUE), pc_dskopen() */ +BOOLEAN pc_dskfree(int driveno) /*__fn__*/ +{ + DDRIVE *pdr; + + /* Note this will fail unless mount_valid is true */ + pdr = pc_drno2dr(driveno); + if (!pdr) + { + return(FALSE); + } + + if (pdr->mount_valid) + { + /* Free the current working directory for this drive for all users */ + pc_free_all_users(driveno); + /* Free all files, finodes & blocks associated with the drive */ + pc_free_all_fil(pdr); + pc_free_all_i(pdr); + /* Free all drobj structures that have not yet been accessed */ + pc_free_all_drobj(pdr); + pc_free_all_blk(pdr); + /* Clear the fat cache */ + pc_free_all_fat_blocks(&pdr->fatcontext); + } + + pdr->mount_valid = FALSE; + pdr->mount_abort = FALSE; + return (TRUE); +} + + + +/**************************************************************************** + PC_SEC2CLUSTER - Convert a block number to its cluster representation. + + Description + Convert blockno to its cluster representation if it is in cluster space. + + Returns + Returns 0 if the block is not in cluster space, else returns the + cluster number associated with block. + +****************************************************************************/ + +#if (RTFS_SUBDIRS) +/* Cluster<->sector conversion routines */ +/* Convert sector to cluster. 0 == s error */ +CLUSTERTYPE pc_sec2cluster(DDRIVE *pdrive, BLOCKT blockno) /*__fn__*/ +{ + BLOCKT ltemp; + BLOCKT answer; + + if ((blockno >= pdrive->numsecs) || (pdrive->firstclblock > blockno)) + return (0); + else + { + /* (2 + (blockno - pdrive->firstclblock)/pdrive->secpalloc) */ + ltemp = blockno - pdrive->firstclblock; + answer = ltemp; + answer = (BLOCKT) answer >> pdrive->log2_secpalloc; + answer += 2; + return ((CLUSTERTYPE)answer); + } +} +#endif + +/**************************************************************************** + PC_SEC2INDEX - Calculate the offset into a cluster for a block. + + Description + Given a block number offset from the beginning of the drive, calculate + which block number within a cluster it will be. If the block number + coincides with a cluster boundary, the return value will be zero. If it + coincides with a cluster boundary + 1 block, the value will be 1, etc. + + + Returns + 0,1,2 upto blockspcluster -1. + +***************************************************************************/ + +#if (RTFS_SUBDIRS) +/* Convert sector to index into a cluster . No error detection */ +word pc_sec2index(DDRIVE *pdrive, BLOCKT blockno) /*__fn__*/ +{ + BLOCKT answer; + + /* ((blockno - pdrive->firstclblock) % pdrive->secpalloc) ); */ + + answer = blockno - pdrive->firstclblock; + answer = answer % pdrive->secpalloc; + + return ( (word) answer); +} + +#endif + + +/*************************************************************************** + PC_CL2SECTOR - Convert a cluster number to block number representation. + + Description + Convert cluster number to a blocknumber. + + Returns + Returns 0 if the cluster is out of range. else returns the + block number of the beginning of the cluster. + + +****************************************************************************/ + +/* Convert cluster. to sector */ +BLOCKT pc_cl2sector(DDRIVE *pdrive, CLUSTERTYPE cluster) /*__fn__*/ +{ + BLOCKT blockno; + dword t; + + if (cluster < 2) + return (BLOCKEQ0); + else + { + t = cluster - 2; + t = t << pdrive->log2_secpalloc; + blockno = pdrive->firstclblock + t; + } + if (blockno >= pdrive->numsecs) + return (BLOCKEQ0); + else + return (blockno); +} +/* Round a size up to it's next cluster boundary and return + the length in clusters */ +dword pc_chain_length(DDRIVE *pdrive, dword byte_length) +{ +dword ltemp,chain_length; + ltemp = byte_length + pdrive->byte_into_cl_mask; + if (ltemp > byte_length) + { + chain_length = ltemp & ~(pdrive->byte_into_cl_mask); + chain_length >>= (int) (pdrive->log2_secpalloc + 9); + } + else + { + ltemp = (byte_length - pdrive->bytespcluster)+ pdrive->byte_into_cl_mask; + chain_length = ltemp & ~(pdrive->byte_into_cl_mask); + chain_length >>= (int) (pdrive->log2_secpalloc + 9); + chain_length += 1; + } + return(chain_length); +} diff --git a/build/libraries/fatfs/ARM7/rtnvfat.c b/build/libraries/fatfs/ARM7/rtnvfat.c new file mode 100644 index 0000000..5ec97f0 --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtnvfat.c @@ -0,0 +1,441 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* RTNVFAT.C - Contains routines specific to the non-vfat implementation */ + +#include + +#if (!VFAT) + +/*************************************************************************** + PC_FINDIN - Find a filename in the same directory as the argument. + + Description + Look for the next match of filename or pattern filename:ext in the + subdirectory containing pobj. If found update pobj to contain the + new information (essentially getnext.) Called by pc_get_inode(). + + Note: Filename and ext must be right filled with spaces to 8 and 3 bytes + respectively. Null termination does not matter. + + Note the use of the action variable. This is used so we do not have to + use match patterns in the core code like *.* and .. + GET_INODE_MATCH Must match the pattern exactly + GET_INODE_WILD Pattern may contain wild cards + GET_INODE_STAR Like he passed *.* (pattern will be null) + GET_INODE_DOTDOT Like he past .. (pattern will be null + + Returns + Returns TRUE if found or FALSE. + +****************************************************************************/ +/* Find filename in the directory containing pobj. If found, load the inode +section of pobj. If the inode is already in the inode buffers we free the current inode +and stitch the existing one in, bumping its open count */ +BOOLEAN pc_findin( DROBJ *pobj, byte *filename, byte *fileext, int action) /*__fn__*/ +{ + BLKBUFF *rbuf; + DIRBLK *pd; + DOSINODE *pi; + FINODE *pfi; + BOOLEAN matchfound; + BOOLEAN dowildcard; + + if (action == GET_INODE_WILD) + dowildcard = TRUE; + else + dowildcard = FALSE; + + rtfs_set_errno(0); /* Clear it here just in case */ + /* For convenience. We want to get at block info here */ + pd = &pobj->blkinfo; + + /* Read the data */ + pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + + while (rbuf) + { + pi = (DOSINODE *) &rbuf->data[0]; + + /* Look at the current inode */ + pi += pd->my_index; + + /* And look for a match */ + while ( pd->my_index < INOPBLOCK ) + { + matchfound = FALSE; + /* End of dir if name is 0 */ + if (!pi->fname[0]) + { + rtfs_set_errno(PENOENT); /* pc_findin: file or dir not found */ + pc_release_buf(rbuf); + return(FALSE); + } + if (pi->fattribute != CHICAGO_EXT && pi->fname[0] != PCDELETE) + { + if (action == GET_INODE_STAR) + matchfound = TRUE; + else if (action == GET_INODE_DOTDOT) + { + if (pi->fname[0] == CS_OP_ASCII('.') && pi->fname[1] == CS_OP_ASCII('.')) + matchfound = TRUE; /* 8.3, not lfn */ + } + else + { + matchfound = + ( pc_patcmp_8(pi->fname, (byte*) filename, dowildcard) && + pc_patcmp_3(pi->fext, (byte*) fileext, dowildcard ) ); + } + if (matchfound && pi->fattribute & AVOLUME && + action != GET_INODE_STAR && action != GET_INODE_WILD) + { + /* Don't match volume labels if we are finding a specific match. */ + matchfound = FALSE; + } + } + if (matchfound) + { + /* We found it */ + /* See if it already exists in the inode list. + If so.. we use the copy from the inode list */ + pfi = pc_scani(pobj->pdrive, rbuf->blockno, pd->my_index); + + if (pfi) + { + pc_freei(pobj->finode); + pobj->finode = pfi; + } + else /* No inode in the inode list. Copy the data over + and mark where it came from */ + { + pfi = pc_alloci(); + if (pfi) + { + pc_freei(pobj->finode); /* Release the current */ + pobj->finode = pfi; + pc_dos2inode(pobj->finode , pi ); + pc_marki(pobj->finode , pobj->pdrive , pd->my_block, + pd->my_index ); + } + else + { + pc_release_buf(rbuf); + return (FALSE); + } + } + /* Free, no error */ + pc_release_buf(rbuf); + return (TRUE); + } /* if (match) */ + pd->my_index++; + pi++; + } + /* Not in that block. Try again */ + pc_release_buf(rbuf); + /* Update the objects block pointer */ + if (!pc_next_block(pobj)) + break; + pd->my_index = 0; + pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + } + if (!get_errno()) + rtfs_set_errno(PENOENT); /* pc_findin: file or dir not found */ + return (FALSE); +} + +/*************************************************************************** + PC_INSERT_INODE - Insert a new inode into an existing directory inode. + +Description + Take mom , a fully defined DROBJ, and pobj, a DROBJ with a finode + containing name, ext, etc, but not yet stitched into the inode buffer + pool, and fill in pobj and its inode, write it to disk and make the inode + visible in the inode buffer pool. (see also pc_mknode() ) + + +Returns + Returns TRUE if all went well, FALSE on a write error, disk full error or + root directory full. + +**************************************************************************/ +/* Note: the parent directory is locked before this routine is called */ + +BOOLEAN pc_insert_inode(DROBJ *pobj , DROBJ *pmom, byte attr, CLUSTERTYPE initcluster, byte *filename, byte *fileext) +{ + BLKBUFF *pbuff; + DIRBLK *pd; + DOSINODE *pi; + int i; + CLUSTERTYPE cluster; + DDRIVE *pdrive; + DATESTR crdate; + + pc_init_inode( pobj->finode, filename, fileext, + attr, initcluster, /*size*/ 0L ,pc_getsysdate(&crdate) ); + + /* Set up pobj */ + pdrive = pobj->pdrive = pmom->pdrive; + pobj->isroot = FALSE; + pd = &pobj->blkinfo; + + /* Now get the start of the dir */ + pd->my_block = pd->my_frstblock = pc_firstblock(pmom); + + if (!pd->my_block) + { + rtfs_set_errno(PEINVALIDBLOCKNUMBER); /* pc_insert_inode: Internal error, invalid block id */ + return (FALSE); + } + else + pd->my_index = 0; + + /* Read the data */ + pobj->pblkbuff = pbuff = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + if (!pbuff) + return(FALSE); + while (pbuff) + { + i = pd->my_index = 0; + pi = (DOSINODE *) &pbuff->data[0]; + /* look for a slot */ + while ( i < INOPBLOCK ) + { + /* End of dir if name is 0 */ + if ( (pi->fname[0] == CS_OP_ASCII('\0')) || (pi->fname[0] == PCDELETE) ) + { + pd->my_index = (word)i; + /* Update the DOS disk */ + pc_ino2dos( pi, pobj->finode ); + /* Write the data */ + if (pc_write_blk(pbuff)) + { + /* Mark the inode in the inode buffer */ + pc_marki(pobj->finode , pobj->pdrive , pd->my_block, + pd->my_index ); + pc_release_buf(pbuff); + return(TRUE); + } + else + { + pc_discard_buf(pbuff); + return(FALSE); + } + } + i++; + pi++; + } + /* Not in that block. Try again */ + pc_release_buf(pbuff); + /* Update the objects block pointer */ + rtfs_set_errno(0); /* Clear errno to be safe */ + if (!pc_next_block(pobj)) + { + if (get_errno()) + return(FALSE); + else + break; + } + pobj->pblkbuff = pbuff = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + } + + cluster = pc_grow_dir(pdrive, pmom); + if (!cluster) + return (FALSE); + /* Do not forget where the new item is */ + pd->my_block = pc_cl2sector(pobj->pdrive , cluster); + pd->my_index = 0; + + /* Zero out the cluster */ + if (!pc_clzero( pobj->pdrive , cluster ) ) + goto clean_and_fail; + /* Copy the item into the first block */ + pbuff = pc_init_blk( pobj->pdrive , pd->my_block); + if (!pbuff) + goto clean_and_fail; + pc_ino2dos ( (DOSINODE *) &pbuff->data[0] , pobj->finode ) ; + /* Write it out */ + if ( !pc_write_blk ( pbuff ) ) + { + pc_discard_buf(pbuff); + goto clean_and_fail; + } + + /* We made a new slot. Mark the inode as belonging there */ + pc_marki(pobj->finode , pobj->pdrive , pd->my_block, pd->my_index ); + pc_release_buf(pbuff); + return (TRUE); +clean_and_fail: + pc_truncate_dir(pdrive, pmom, cluster); + return (FALSE); +} + +/**************************************************************************** +PC_NIBBLEPARSE - Nibble off the left most part of a pathspec + + Description + Take a pathspec (no leading D:). and parse the left most element into + filename and files ext. (SPACE right filled.). + + Returns + Returns a pointer to the rest of the path specifier beyond file.ext + ****************************************************************************/ + /* Parse a path. Return NULL if problems or a pointer to the next */ +byte *pc_nibbleparse(byte *filename, byte *fileext, byte *path) /* __fn__*/ +{ + byte *p; + BLKBUFF *scratch; + byte *tbuf; + byte *t; + + p = path; + + if (!p) /* Path must exist */ + return (0); + scratch = pc_scratch_blk(); + if (!scratch) + return(0); + tbuf = scratch->data; + t = tbuf; + + while (CS_OP_IS_NOT_EOS(p)) + { + if (CS_OP_CMP_ASCII(p,'\\')) + { + CS_OP_INC_PTR(p); + break; + } + else + { + CS_OP_CP_CHR(t, p); + CS_OP_INC_PTR(t); + CS_OP_INC_PTR(p); + } + } + CS_OP_TERM_STRING(t); + + if (pc_ascii_fileparse(filename, fileext, tbuf)) + { + pc_free_scratch_blk(scratch); + return (p); + } + else + { + pc_free_scratch_blk(scratch); + return (0); + } +} + +BOOLEAN pc_parsepath(byte *topath, byte *filename, byte *fileext, byte *path) /*__fn__*/ +{ + byte *pfile, *pto, *pfr, *p, *pslash, *pcolon; + int i; + + /* Check the path length - compare the string length in bytes + against the max path in chars. This is correct since in + ASCII charlen==bytelen, in JIS charlen does not equal bytelen + but we don't want the string to be > the char len */ + if (rtfs_cs_strlen_bytes(path) > EMAXPATH_CHARS) + return(FALSE); + + pslash = pcolon = 0; + pfr = path; + pto = topath; + /* Copy input path to output keep note colon and backslash positions */ + + for (i = 0; CS_OP_IS_NOT_EOS(pfr); i++,CS_OP_INC_PTR(pfr),CS_OP_INC_PTR(pto)) + { + CS_OP_CP_CHR(pto, pfr); + if (CS_OP_CMP_ASCII(pto,'\\')) + pslash = pto; + else if (CS_OP_CMP_ASCII(pto,':')) + { + if (i != 1) /* A: B: C: .. x: y: z: only */ + return (FALSE); + pcolon = pto; + CS_OP_INC_PTR(pcolon); /* Look one past */ + } + } + CS_OP_TERM_STRING(pto); + + if (pslash) + { + pfile = pslash; + CS_OP_INC_PTR(pfile); + } + else if (pcolon) + pfile = pcolon; + else + pfile = topath; + + if (!pc_ascii_fileparse(filename, fileext, pfile)) + return (FALSE); + /* Terminate path: + If X:\ or \ leave slash on. Else zero it + */ + p = topath; /* Default */ + if (!pslash) + { + if (pcolon) p = pcolon; + } + else /* if slash. and at 0 or right after colon leave else zero it */ + { + p = pslash; + /* \ or A:\ */ + if (p == topath || p == pcolon) + { + CS_OP_INC_PTR(p); /* (leave it in path) */ + } + } + CS_OP_TERM_STRING(p); + return(TRUE); +} +/* Byte oriented */ +BOOLEAN _illegal_lfn_char(byte ch) +{ + RTFS_ARGSUSED_INT((int) ch); + return(FALSE); +} + +static BOOLEAN pc_allspace(byte *p, int i) /* __fn__*/ +{while (i--) if (*p++ != ' ') return (FALSE); return (TRUE); } +BOOLEAN pc_isdot(byte *fname, byte *fext) /* __fn__*/ +{ + return (BOOLEAN)((*fname == '.') && + pc_allspace(fname+1,7) && pc_allspace(fext,3) ); +} +BOOLEAN pc_isdotdot(byte *fname, byte *fext) /* __fn__*/ +{ + return (BOOLEAN)( (*fname == '.') && (*(fname+1) == '.') && + pc_allspace(fname+2,6) && pc_allspace(fext,3) ); +} +BOOLEAN pc_delete_lfn_info(DROBJ *pobj) +{ + RTFS_ARGSUSED_PVOID((void *) pobj); + return(TRUE); +} +void pc_zero_lfn_info(FINODE *pdir) +{ + RTFS_ARGSUSED_PVOID((void *) pdir); +} +BOOLEAN pc_get_lfn_filename(DROBJ *pobj, byte *path) +{ + RTFS_ARGSUSED_PVOID((void *) pobj); + RTFS_ARGSUSED_PVOID((void *) path); + return(FALSE); +} +dword scan_for_bad_lfns(DROBJ *pmom, int delete_bad_lfn) /*__fn__*/ +{ + RTFS_ARGSUSED_PVOID((void *) pmom); + RTFS_ARGSUSED_INT(delete_bad_lfn); + return(0); +} + +#endif /* #if (!VFAT) */ + + + diff --git a/build/libraries/fatfs/ARM7/rttermin.c b/build/libraries/fatfs/ARM7/rttermin.c new file mode 100644 index 0000000..abc5ab5 --- /dev/null +++ b/build/libraries/fatfs/ARM7/rttermin.c @@ -0,0 +1,235 @@ +/* +* rttermin.c - Portable portion of terminal IO routines +* +* EBS - ERTFS +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* +* Module description: +* This file contains terminal IO routines used by the sample programs +* and by routines that print diagnostics. +* +* +* The rest of the routines in this module are portable with the possible +* exception of these two routines: +* +* void rtfs_print_format_dir +* void rtfs_print_format_stat +* +* These routines may require porting if sprintf() is not available to you. +* They are called only by the test shell prgram (tstsh.c) and are used to create attractive +* formatted output for the DIR and STAT commands. They rely on sprintf to format the output +* to provide a system specific console output routine. A define is provided +* in this file named SYS_SUPPORTS_SPRINTF, if this is set to one the routines format the output +* using sprintf, otherwise they print a fixd string. If sprintf is not available to you set +* SYS_SUPPORTS_SPRINTF to zero. +* +* +* The following portable routines are also provided in this file. +* +* rtfs_print_string_1(int stringid,int flags) +* rtfs_print_string_2(int stringid,byte *pstr2, int flags) +* +* These two routines are used to print string values to the console. They are portable, +* relying on the routine rtfs_port_puts(() to provide a system specific console output routine. +* If no output is desired define the macros RTFS_PRINT_STRING_1 and RTFS_PRINT_STRING_2 as +* no-ops in portterm.h. +* +* rtfs_print_long_1 +* +* This routine is used to print long integers values to the console. It relies on +* rtfs_port_puts(() to provide a system specific console output routine. If no output +* is desired define the macro RTFS_PRINT_LONG_1 as a no-op in portterm.h. +* +* +* rtfs_print_prompt_user() - +* +* This routine is called when the ERTFS demo programs and critical error handlr routine +* requires console input from the user. It takes as input a prompt id (this is a numeric +* handle to the prompt strings in the prompts string table prompt_table[] +* in portstr.c and the address of a buffer where to place the console +* input. +* +* This routine displays the prompt by calling rtfs_print_one_string() and then +* calls the target specific routine tm_gets_rtfs() to recieve the console +* input. +* +* Note: If in your system console input is not available you may still +* use the demo and test applications by modifying this routine so that +* it returns specific strings to simulate user input. The values to return +* must be relevant to the value of prompt_id. Here are several prompt_id +* values that should be responded to if no console input is available. +* +* +* UPROMPT_CRITERR - This is the prompt_id argument when a drive IO error +* occurs. You should return "A" in the return buffer. This will cause the +* IO operation to fail and return an error to the API. +* +* UPROMPT_TSTSH - If you returned "S" for UPROMPT_RTFSDEM1 then this +* prompt will be called after the test shell starts and repeatedly +* thereafter. Return strings for this prompt as if you were typing +* input to the command shell. +* +* For example a sequence of strings might fill. +* +* FILLFILE TEST.DAT MYPATTERN 100 +* CAT TEST.DAT +* DELETE TEST.BAT +* +* If you do not wish to use the interactive test programs you need +* not implement this function. +* If sprintf is not available to you set SYS_SUPPORTS_SPRINTF to zero. +*/ +#define SYS_SUPPORTS_SPRINTF 0 +#include + +#if (SYS_SUPPORTS_SPRINTF) +#include /* For sprintf */ +#endif + + +void rtfs_print_one_string(byte *pstr,int flags); +byte *pc_ltoa(dword num, byte *dest); + +void rtfs_print_string_1(int stringid,int flags) +{ + rtfs_print_one_string(rtfs_strtab_user_string(stringid),flags); +} + +void rtfs_print_string_2(int stringid,byte *pstr2, int flags) +{ + rtfs_print_string_1(stringid,0); + rtfs_print_one_string(pstr2,flags); +} + +void rtfs_print_long_1(dword l,int flags) +{ +byte buffer[16]; + rtfs_print_one_string(pc_ltoa(l, buffer),flags); +} + +void rtfs_print_prompt_user(int prompt_id, byte *buf) +{ +byte inbuff[80]; + rtfs_print_one_string(rtfs_strtab_user_prompt(prompt_id),0); + rtfs_port_tm_gets(inbuff); +#if (INCLUDE_CS_UNICODE) + map_ascii_to_unicode(buf, inbuff); +#else + rtfs_strcpy(buf, inbuff); +#endif +} + +/* Porting may be required */ +static byte *gotoeos(byte *p) { while(*p) p++; return(p);} +void rtfs_print_format_dir(byte *display_buffer, DSTAT *statobj) +{ +#if (SYS_SUPPORTS_SPRINTF) + byte *dirstr; + byte *p; + int year; + + if (statobj->fattribute & AVOLUME) + dirstr = (byte *)""; + else if (statobj->fattribute & ADIRENT) + dirstr = (byte *)""; + else + dirstr = (byte *)" "; + + p = display_buffer; + *p = 0; + + sprintf((char *)p,"%-8s.", (char *)&(statobj->fname[0])); + sprintf((char *)gotoeos(p),"%-3s", (char *)&(statobj->fext[0])); +// sprintf((char *)gotoeos(p)," %10lu ", statobj->fsize); + sprintf((char *)gotoeos(p)," %10u ", statobj->fsize); + + sprintf((char *)gotoeos(p),"%5s", dirstr); + sprintf((char *)gotoeos(p)," %02d",(statobj->fdate >> 5 ) & 0xf); /* Month */ + sprintf((char *)gotoeos(p),"-%02d",(statobj->fdate & 0x1f)); /* Day */ + year = 80 +(statobj->fdate >> 9) & 0xff; /* Year */ + if (year >= 100) + year -= 100; + sprintf((char *)gotoeos(p),"-%02d", year); /* Year */ + sprintf((char *)gotoeos(p)," %02d",(statobj->ftime >> 11) & 0x1f); /* Hour */ + sprintf((char *)gotoeos(p),":%02d",(statobj->ftime >> 5) & 0x3f); /* Minute */ + + /* if lfnmae is null we know that the first two bytes are both 0 */ + if (statobj->lfname[0] || statobj->lfname[1]) + { + /* For vfat systems display the attributes and the long file name + seperately. This is a trick since the attribute are ASCII and the + LFN is UNICODE. If we print seperately we will see them both correctly */ + sprintf((char *)gotoeos(p), " - "); + rtfs_print_one_string(display_buffer, 0); + rtfs_print_one_string(statobj->lfname,PRFLG_NL); + } + else + rtfs_print_one_string(display_buffer,PRFLG_NL); +#else /* #if (SYS_SUPPORTS_SPRINTF) */ + rtfs_print_one_string((byte*)"SPRINTF NOT SUPPORTED",PRFLG_NL); +#endif + +} + +/* Porting may be required */ +void rtfs_print_format_stat(byte *display_buffer, ERTFS_STAT *st) +{ +#if (SYS_SUPPORTS_SPRINTF) +byte *p; + p = display_buffer; + *p = 0; + sprintf((char *)gotoeos(p),"DRIVENO: %02d SIZE: %7ld", st->st_dev, st->st_size); + sprintf((char *)gotoeos(p)," DATE:%02d-%02d",(st->st_atime.date >> 5 ) & 0xf,/* Month */(st->st_atime.date & 0x1f)/* Day */); + sprintf((char *)gotoeos(p),"-%02d",80 +(st->st_atime.date >> 9) & 0xff); /* Year */ + sprintf((char *)gotoeos(p)," TIME:%02d:%02d\n",(st->st_atime.time >> 11) & 0x1f,/* Hour */(st->st_atime.time >> 5) & 0x3f); /* Minute */ + sprintf((char *)gotoeos(p),"OPTIMAL BLOCK SIZE: %7ld FILE size (BLOCKS): %7ld",st->st_blksize,st->st_blocks); + rtfs_print_one_string(display_buffer,PRFLG_NL); +#else /* #if (SYS_SUPPORTS_SPRINTF) */ + rtfs_print_one_string((byte*)"SPRINTF NOT SUPPORTED",PRFLG_NL); +#endif + +} + +void rtfs_print_one_string(byte *pstr,int flags) +{ + + rtfs_port_puts(CS_OP_FORMAT_OUTPUT(pstr)); + if (flags & PRFLG_NL) + rtfs_port_puts((byte *)"\n"); + if (flags & PRFLG_CR) + rtfs_port_puts((byte *)"\r"); +} +/* Portable */ +byte *pc_ltoa(dword num, byte *dest) /*__fn__*/ +{ +byte buffer[33]; /* MAXINT can have 32 digits max, base 2 */ +int digit; +byte *olddest = dest; +byte * p; + + p = &(buffer[32]); + + *p = '\0'; + + /* Convert num to a string going from dest[31] backwards */ + /* Nasty little ItoA algorithm */ + do + { + digit = (int) (num % 10); + + *(--p) = + (byte)(digit<10 ? (byte)(digit + '0') : (byte)((digit-10) + 'a')); + num /= 10; + } + while (num); + + /* Now put the converted string at the beginning of the buffer */ + while((*dest++=*p++)!='\0'); + return (olddest); +} + diff --git a/build/libraries/fatfs/ARM7/rtutbyte.c b/build/libraries/fatfs/ARM7/rtutbyte.c new file mode 100644 index 0000000..180e633 --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtutbyte.c @@ -0,0 +1,121 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* rtutbyte.c - Character set independent string manipulation routines */ + +#include +#include +#include + +/* Byte oriented */ +byte *pc_strchr(byte *string, byte ch) +{ + for(;string && *string!=0; string++) + { + if(ch == *string) return(string); + } + return(0); +} +BOOLEAN _illegal_alias_char(byte ch) /*__fn__*/ +{ + if (pc_strchr(rtfs_strtab_user_string(USTRING_SYS_BADALIAS), ch)) + return(TRUE); + else + return(FALSE); +} + + +/* Byte oriented */ +int rtfs_strcpy(byte * targ, byte * src) /*__fn__*/ +{ + int loop_cnt=0; + do + { + targ[loop_cnt] = src[loop_cnt]; + } while(src[loop_cnt++]); + return loop_cnt; +} + +/* Byte oriented */ +/* compares 2 strings; returns 0 if they match */ +int rtfs_strcmp(byte * s1, byte * s2) /*__fn__*/ +{ + int index=0; + while (s1[index] && s2[index] && s1[index] == s2[index]) + { + index++; + } + if (!s1[index] && !s2[index]) return 0; + if (s1[index] < s2[index]) return -1; + else return 1; +} + +/* Byte oriented */ +int rtfs_strlen(byte * string) /*__fn__*/ +{ + int len=0; + while (string[len] != 0) len++; + return len; +} + +/* Byte oriented string functions character set independent */ +/**************************************************************************** +COPYBUF - Copy one buffer to another +Description +Essentially strncpy. Copy size BYTES from from to to. +Returns +Nothing +****************************************************************************/ +void copybuff(void *vto, void *vfrom, int size) /* __fn__*/ +{ + OSAPI_CPUCOPY8( vfrom, vto, size); +/* byte *to = (byte *) vto; + byte *from = (byte *) vfrom; + while (size--) + *to++ = *from++;*/ +} + +/****************************************************************************** +PC_CPPAD - Copy one buffer to another and right fill with spaces +Description +Copy up to size characters from from to to. If less than size +characters are transferred before reaching \0 fill to with SPACE +characters until its length reaches size. + + Note: to is NOT ! Null terminated. + + ASCII character function only. Unicode not required + + Returns + Nothing + +*****************************************************************************/ +/* Byte oriented */ +void pc_cppad(byte *to, byte *from, int size) /* __fn__*/ +{ + rtfs_memset(to, ' ', size); + while (size--) + if (*from) + *to++ = *from++; +} + +/* ******************************************************************** */ + +/* Byte oriented */ +void rtfs_memset(void *pv, byte b, int n) /*__fn__*/ +{ byte *p; p = (byte *) pv; while(n--) {*p++=b;} } + +/* Byte oriented see rtfs_cs_strcat for char oriented */ +byte * rtfs_strcat(byte * targ, byte * src) /*__fn__*/ +{ + int ret_val; + ret_val = rtfs_strlen(targ); + rtfs_strcpy(targ + ret_val, src); + return targ; +} + diff --git a/build/libraries/fatfs/ARM7/rtutil.c b/build/libraries/fatfs/ARM7/rtutil.c new file mode 100644 index 0000000..64cfc2a --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtutil.c @@ -0,0 +1,304 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* RTUTIL.C - String manipulation and byte order conversion routines */ + + +#include + +/****************************************************************************** +PC_PARSEDRIVE - Get a drive number from a path specifier + + Description + Take a path specifier in path and extract the drive number from it. + If the second character in path is ':' then the first char is assumed + to be a drive specifier and 'A' is subtracted from it to give the + drive number. If the drive number is valid, driveno is updated and + a pointer to the text just beyond ':' is returned. Otherwise null + is returned. + If the second character in path is not ':' then the default drive number + is put in driveno and path is returned. + + Unicode character function. Unicode is required + + Returns + Returns NULL on a bad drive number otherwise a pointer to the first + character in the rest of the path specifier. + ***************************************************************************/ + + /* Get the drive number form the path. Make sure one is provided */ + /* and that it is valid. */ + /* returns -1 if not valid otherwise return the driveno */ + + /* UNICODE/ASCI/JIS Version */ +int pc_path_to_driveno(byte *path) /* __fn__*/ +{ + byte *p; + int drivenumber; + p = path; + if (CS_OP_IS_EOS(p)) + return(-2); + CS_OP_INC_PTR(p); + if (CS_OP_CMP_ASCII(p,':')) + { + drivenumber = CS_OP_ASCII_INDEX(path,'A'); + if (drivenumber < 0 || drivenumber > 25) + { + rtfs_set_errno(PEINVALIDDRIVEID); /* pc_path_to_driveno: invalid drive number */ + drivenumber = -1; + } + } + else + drivenumber = -2; + return(drivenumber); +} + +/* Parse drive part from a string - return -2 if no 'A:' -1 if A: +and not valid drive id */ +int pc_parse_raw_drive(byte *path) /* __fn__*/ +{ + int dno; + dno = pc_path_to_driveno(path); + if (dno < 0 || !pc_validate_driveno(dno)) + return (-1); + else + return(dno); +} + +/* Extract drive no from D: or use defualt. return the rest of the string +or NULL if a bad drive no is requested */ +byte *pc_parsedrive(int *driveno, byte *path) /* __fn__*/ +{ + byte *p = path; + int dno; + + /* get drive no */ + dno = pc_path_to_driveno(path); + if (dno == -2) /* D: Not specified. Use current default */ + { + dno = pc_getdfltdrvno(); + /* Make sure default drive number is valid */ + if (!pc_validate_driveno(dno)) + return(0); + } + else if (dno == -1) /* D: specified but bogus */ + return(0); + else /* D: Specified. validate */ + { + if (!pc_validate_driveno(dno)) + return(0); + /* SKIP D and : */ + CS_OP_INC_PTR(p); + CS_OP_INC_PTR(p); + } + + *driveno = dno; + return (p); +} + +/*************************************************************************** + PC_MPATH - Build a path sppec from a filename and pathname + + Description + Fill in to with a concatenation of path and filename. If path + does not end with a path separator, one will be placed between + path and filename. + + TO will be null terminated. + + Returns + A pointer to 'to'. + +*****************************************************************************/ +byte *pc_mpath(byte *to, byte *path, byte *filename) /* __fn__*/ +{ + byte *retval = to; + byte *p; + byte c[2]; + + c[0] = c[1] = 0; + p = path; + while (CS_OP_IS_NOT_EOS(p)) + { + if (CS_OP_CMP_ASCII(p,' ')) + break; + else + { + CS_OP_CP_CHR(c, p); + CS_OP_CP_CHR(to, p); + CS_OP_INC_PTR(p); + CS_OP_INC_PTR(to); + } + } + /* Put \\ on the end f not there already, but not if path was null */ + if (p != path && !CS_OP_CMP_ASCII(c,'\\')) + { + CS_OP_ASSIGN_ASCII(to,'\\'); + CS_OP_INC_PTR(to); + } + + p = filename; + while (CS_OP_IS_NOT_EOS(p)) + { + CS_OP_CP_CHR(to, p); + CS_OP_INC_PTR(p); + CS_OP_INC_PTR(to); + } + CS_OP_TERM_STRING(to); + return (retval); +} + +/* Byte oriented - search comma separated list in set for filename */ +BOOLEAN pc_search_csl(byte *set, byte *string) +{ +byte *p; + if (!set || !string) + return(FALSE); + while(*set) + { + p = string; + while (*set && *p && (*set == *p)) + { + set++; + p++; + } + if ((*set == 0 || *set == ',') && *p == 0) + return(TRUE); + while (*set && *set != ',') set++; + while (*set == ',') set++; + if (!(*set)) + break; + } + return(FALSE); +} + +BOOLEAN name_is_reserved(byte *filename) /* __fn__*/ +{ + return ( + pc_search_csl(rtfs_strtab_user_string(USTRING_SYS_UCRESERVED_NAMES), filename) || + pc_search_csl(rtfs_strtab_user_string(USTRING_SYS_LCRESERVED_NAMES), filename) + ); +} + +/* ******************************************************************** */ +/* KEEP COMPILER HAPPY ROUTINES */ +/* ******************************************************************** */ +/* Used to keep the compiler happy */ +void RTFS_ARGSUSED_PVOID(void * p) /*__fn__*/ +{ + p = p; +} + +/* Used to keep the compiler happy */ +void RTFS_ARGSUSED_INT(int i) /*__fn__*/ +{ + i = i; +} + +/***************************************************************************** +PC_CNVRT - Convert intel byte order to native byte order. + + Summary + #include + + dword to_DWORD (from) Convert intel style 32 bit to native 32 bit + byte *from; + + word to_WORD (from) Convert intel style 16 bit to native 16 bit + byte *from; + + void fr_WORD (to,from) Convert native 16 bit to 16 bit intel + byte *to; + word from; + + void fr_DWORD (to,from) Convert native 32 bit to 32 bit intel + byte *to; + dword from; + + Description + This code is known to work on 68K and 808x machines. It has been left + as generic as possible. You may wish to hardwire it for your CPU/Code + generator to shave off a few bytes and microseconds, be careful though + the addresses are not guaranteed to be word aligned in fact to_WORD AND + fr_WORD's arguments are definately NOT word alligned when working on odd + number indeces in 12 bit fats. (see pc_faxx and pc_pfaxx(). + + Note: Optimize at your own peril, and after everything else is debugged. + + Bit shift operators are used to convert intel ordered storage + to native. The host byte ordering should not matter. + + Returns + + Example: + See other sources. + + ***************************************************************************** +*/ + +/* Convert a 32 bit intel item to a portable 32 bit */ +dword to_DWORD ( byte *from) /*__fn__*/ +{ + dword res; +#if (KS_LITTLE_ENDIAN && KS_LITTLE_ODD_PTR_OK) + res = ((dword) *((dword *)from)); +#else + dword t; + t = ((dword) *(from + 3)) & 0xff; + res = (t << 24); + t = ((dword) *(from + 2)) & 0xff; + res |= (t << 16); + t = ((dword) *(from + 1)) & 0xff; + res |= (t << 8); + t = ((dword) *from) & 0xff; + res |= t; +#endif + return(res); +} + +/* Convert a 16 bit intel item to a portable 16 bit */ +word to_WORD ( byte *from) /*__fn__*/ +{ + word nres; +#if (KS_LITTLE_ENDIAN && KS_LITTLE_ODD_PTR_OK) + nres = ((word) *((word *)from)); +#else + word t; + t = (word) (((word) *(from + 1)) & 0xff); + nres = (word) (t << 8); + t = (word) (((word) *from) & 0xff); + nres |= t; +#endif + return(nres); +} + +/* Convert a portable 16 bit to a 16 bit intel item */ +void fr_WORD ( byte *to, word from) /*__fn__*/ +{ +#if (KS_LITTLE_ENDIAN && KS_LITTLE_ODD_PTR_OK) + *((word *)to) = from; +#else + *to = (byte) (from & 0x00ff); + *(to + 1) = (byte) ((from >> 8) & 0x00ff); +#endif +} + +/* Convert a portable 32 bit to a 32 bit intel item */ +void fr_DWORD ( byte *to, dword from) /*__fn__*/ +{ +#if (KS_LITTLE_ENDIAN && KS_LITTLE_ODD_PTR_OK) + *((dword *)to) = from; +#else + *to = (byte) (from & 0xff); + *(to + 1) = (byte) ((from >> 8) & 0xff); + *(to + 2) = (byte) ((from >> 16) & 0xff); + *(to + 3) = (byte) ((from >> 24) & 0xff); +#endif +} + + diff --git a/build/libraries/fatfs/ARM7/rtvfat.c b/build/libraries/fatfs/ARM7/rtvfat.c new file mode 100644 index 0000000..376f96a --- /dev/null +++ b/build/libraries/fatfs/ARM7/rtvfat.c @@ -0,0 +1,1449 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* RTVFAT.C - Contains routines specific to the vfat implementation */ + +#include + +#if (VFAT) + +#define FIRST_NAMESEG 0x40 +#define NAMESEG_ORDER 0x3F + +/* LFNINODE - This is an image of lfn extended names in a subdirectory */ +/* Note: lfn file names are null terminated unicode 00, the lfninode is + padded mod 13 with ffff */ +typedef struct lfninode { + /* The first LNF has 0x40 + N left */ + byte lfnorder; /* 0x45, 0x04, 0x03, 0x02, 0x01 they are stored in + reverse order */ + byte lfname1[10]; + byte lfnattribute; /* always 0x0F */ + byte lfnres; /* reserved */ + byte lfncksum; /* All lfninode in one dirent have the same chksum */ + byte lfname2[12]; + word lfncluster; /* always 0x0000 */ + byte lfname3[4]; + } LFNINODE; + +void pcdel2lfi(LFNINODE *lfi, int nsegs); +BOOLEAN pc_deleteseglist(DDRIVE *pdrive, SEGDESC *s); +byte *text2lfi(byte *lfn, LFNINODE *lfi, int nsegs, byte ncksum, byte order); +BOOLEAN pc_seglist2disk(DDRIVE * pdrive, SEGDESC *s, byte *lfn); +byte *lfi2text(byte *lfn, int *current_lfn_length, LFNINODE *lfi, int nsegs); +byte *pc_seglist2text(DDRIVE * pdrive, SEGDESC *s, byte *lfn); +void pc_zeroseglist(SEGDESC *s); +void pc_zeroseglist(SEGDESC *s); +void pc_addtoseglist(SEGDESC *s, BLOCKT my_block, int my_index); +void pc_reduceseglist(SEGDESC *s); +BOOLEAN pc_patcmp_vfat_8_3(byte *pat, byte *name, BOOLEAN dowildcard); + +/*************************************************************************** + PC_FINDIN - Find a filename in the same directory as the argument. + + Description + Look for the next match of filename or pattern filename:ext in the + subdirectory containing pobj. If found update pobj to contain the + new information (essentially getnext.) Called by pc_get_inode(). + + Note: Filename and ext must be right filled with spaces to 8 and 3 bytes + respectively. Null termination does not matter. + + Note the use of the action variable. This is used so we do not have to + use match patterns in the core code like *.* and .. + GET_INODE_MATCH Must match the pattern exactly + GET_INODE_WILD Pattern may contain wild cards + GET_INODE_STAR Like he passed *.* (pattern will be null) + GET_INODE_DOTDOT Like he past .. (pattern will be null + + Returns + Returns TRUE if found or FALSE. + +****************************************************************************/ +/* Find filename in the directory containing pobj. If found, load the inode +section of pobj. If the inode is already in the inode buffers we free the current inode +and stitch the existing one in, bumping its open count */ +BOOLEAN pc_findin( DROBJ *pobj, byte *filename, byte *fileext, int action) /*__fn__*/ +{ + BLKBUFF *rbuf; + DIRBLK *pd; + DOSINODE *pi; + FINODE *pfi; + BOOLEAN matchfound; + BOOLEAN dowildcard; + BLKBUFF *scratch; + byte *lfn; + byte sfn[26]; /* Leave room in case unicode */ + LFNINODE *lfn_node; + SEGDESC s; + byte lastsegorder; + byte *p; + + RTFS_ARGSUSED_PVOID((void *) fileext); + rtfs_set_errno(0); /* Clear it here just in case */ + scratch = pc_scratch_blk(); + if (!scratch) + return(FALSE); + lfn = (byte *)scratch->data; + + pc_zeroseglist(&s); + + if (action == GET_INODE_WILD) + dowildcard = TRUE; + else + dowildcard = FALSE; + + /* For convenience. We want to get at block info here */ + pd = &pobj->blkinfo; + + /* Read the data */ + pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + + lastsegorder = 0; + while (rbuf) + { + pi = (DOSINODE *) &rbuf->data[0]; + + /* Look at the current inode */ + pi += pd->my_index; + + /* And look for a match */ + while ( pd->my_index < INOPBLOCK ) + { + matchfound = FALSE; + /* End of dir if name is 0 */ + if (!pi->fname[0]) + { + rtfs_set_errno(PENOENT); /* pc_findin: file or dir not found */ + pc_free_scratch_blk(scratch); + pc_release_buf(rbuf); + return(FALSE); + } + + if (pi->fname[0] == PCDELETE) + { + pc_zeroseglist(&s); + } + else + { + /* Support long file names */ + if (pi->fattribute == CHICAGO_EXT) + { + lfn_node = (LFNINODE *) pi; + if (lfn_node->lfnorder & FIRST_NAMESEG) + { + pc_addtoseglist(&s, pd->my_block, pd->my_index); + /* .. we could build up the name here too */ + lastsegorder = lfn_node->lfnorder; + s.ncksum = lfn_node->lfncksum; + } + else + { + if (s.nsegs)/* if a chain already exists */ + { + if ( ((lfn_node->lfnorder & NAMESEG_ORDER) == (lastsegorder & NAMESEG_ORDER) - 1) && + (lfn_node->lfncksum == s.ncksum) ) + { + /* Add new segment to lfn chain */ + lastsegorder = lfn_node->lfnorder; + pc_addtoseglist(&s, pd->my_block, pd->my_index); + } + else + { + /* disconnect chain... segments do not match */ + lastsegorder = 0; + pc_zeroseglist(&s); + } + } + } + } + else + { + /* Note: Patcmp wo not match on deleted */ + if (s.nsegs) + { + if (action == GET_INODE_STAR) + matchfound = TRUE; + else if (action == GET_INODE_DOTDOT) + matchfound = FALSE; /* 8.3, not lfn */ + else + { + p = (byte*)pc_seglist2text(pobj->pdrive, &s,lfn); + if (!p) + matchfound = FALSE; /* 8.3, not lfn */ + else + matchfound = pc_patcmp_vfat(filename, p ,dowildcard); + } + } + else + matchfound = FALSE; /* 8.3, not lfn */ + + if (matchfound) + matchfound = (BOOLEAN)(pc_cksum( (byte*) pi ) == s.ncksum); + else + { + if (action == GET_INODE_STAR) + matchfound = TRUE; + else if (action == GET_INODE_DOTDOT) + { + if (pi->fname[0] == CS_OP_ASCII('.') && pi->fname[1] == CS_OP_ASCII('.')) + matchfound = TRUE; /* 8.3, not lfn */ + } + else + { + pc_cs_mfile(sfn,pi->fname,pi->fext); + matchfound = pc_patcmp_vfat_8_3(filename, sfn, dowildcard); + } + } + if (matchfound && pi->fattribute & AVOLUME && + action != GET_INODE_STAR && action != GET_INODE_WILD) + { + /* Don't match volume labels if we are finding a specific match. */ + matchfound = FALSE; + } + if (matchfound) + { + /* We found it */ + /* See if it already exists in the inode list. + If so.. we use the copy from the inode list */ + pfi = pc_scani(pobj->pdrive, rbuf->blockno, pd->my_index); + + if (pfi) + { + pc_freei(pobj->finode); + pobj->finode = pfi; + } + else /* No inode in the inode list. Copy the data over + and mark where it came from */ + { + pfi = pc_alloci(); + if (pfi) + { + pc_freei(pobj->finode); /* Release the current */ + pobj->finode = pfi; + pc_dos2inode(pobj->finode , pi ); + pc_marki(pobj->finode , pobj->pdrive , pd->my_block, + pd->my_index ); + if (s.nsegs) + { + if (pc_cksum( (byte*) pi ) != s.ncksum) + { + pc_zeroseglist(&s); + lastsegorder = 0; + } + pobj->finode->s = s; + } + else + pc_zeroseglist(&pobj->finode->s); + } + else + { + pc_free_scratch_blk(scratch); + pc_release_buf(rbuf); + return (FALSE); + } + } + /* Free, no error */ + pc_free_scratch_blk(scratch); + pc_release_buf(rbuf); + return (TRUE); + } /* if (match) */ + else /* disconnect chain... segments do not match */ + { + pc_zeroseglist(&s); + } + } /* else (CHICAGO_EXT) */ + } /* if (!PCDELETE) */ + pd->my_index++; + pi++; + } + /* Not in that block. Try again */ + pc_release_buf(rbuf); + /* Update the objects block pointer */ + if (!pc_next_block(pobj)) + break; + pd->my_index = 0; + pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + } + + if (!get_errno()) + rtfs_set_errno(PENOENT); /* pc_findin: file or dir not found */ + pc_free_scratch_blk(scratch); + return (FALSE); +} + +/*************************************************************************** + PC_INSERT_INODE - Insert a new inode into an existing directory inode. + +Description + Take mom , a fully defined DROBJ, and pobj, a DROBJ with a finode + containing name, ext, etc, but not yet stitched into the inode buffer + pool, and fill in pobj and its inode, write it to disk and make the inode + visible in the inode buffer pool. (see also pc_mknode() ) + + +Returns + Returns TRUE if all went well, FALSE on a write error, disk full error or + root directory full. + +**************************************************************************/ +/* Note: the parent directory is locked before this routine is called */ + +BOOLEAN pc_insert_inode(DROBJ *pobj , DROBJ *pmom, byte attr, CLUSTERTYPE initcluster, byte *filename, byte *fileext) +{ + BLKBUFF *pbuff; + DIRBLK *pd; + DOSINODE *pi; + int i; + CLUSTERTYPE cluster; + DDRIVE *pdrive; + DATESTR crdate; + byte vffilename[9],vffileext[4]; + byte cksum; + BOOLEAN end_of_dir; + int n_segs; + + cluster = 0; + /* How many segments do we need */ + n_segs = (rtfs_cs_strlen(filename) + 12 )/13; + + RTFS_ARGSUSED_PVOID((void *) fileext); + if (!pc_malias(vffilename,vffileext, (byte *)filename,pmom)) + return (FALSE); /* pc_malias set errno */ + + pc_init_inode( pobj->finode, vffilename, vffileext, + attr, initcluster, /*size*/ 0L ,pc_getsysdate(&crdate) ); + + /* Set up pobj */ + pdrive = pobj->pdrive = pmom->pdrive; + pobj->isroot = FALSE; + pd = &pobj->blkinfo; + + /* Now get the start of the dir */ + pd->my_block = pd->my_frstblock = pc_firstblock(pmom); + + if (!pd->my_block) + { + rtfs_set_errno(PEINVALIDBLOCKNUMBER); /* pc_insert_inode: Internal error, invalid block id */ + return (FALSE); + } + else + pd->my_index = 0; + + /* Read the data */ + pobj->pblkbuff = pbuff = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + if (!pbuff) + return(FALSE); + /* If the alias contains Kanji E5 this goes on disk as 05 so we have to + checksum it with 05 */ + if (pobj->finode->fname[0] == 0xe5) + { + pobj->finode->fname[0] = 0x05; + cksum = pc_cksum((byte*)pobj->finode); + pobj->finode->fname[0] = 0xe5; + } + else + cksum = pc_cksum((byte*)pobj->finode); + end_of_dir = FALSE; + pc_zeroseglist(&pobj->finode->s); + pobj->finode->s.ncksum = cksum; + while (pbuff) + { + i = pd->my_index = 0; + pi = (DOSINODE *) &pbuff->data[0]; + /* look for a slot */ + while ( i < INOPBLOCK ) + { + if (pi->fname[0] == CS_OP_ASCII('\0')) + end_of_dir = TRUE; + if (pi->fname[0] == PCDELETE || end_of_dir) + { + /* Note that we fake an allocation of n_segs + 1 segments. + this scheme makes sure that there is room for the DOSINODE + immediately after the segment list. We reduce the segment + count before we write the segments */ + if (pobj->finode->s.nsegs != (n_segs +1)) + { + pc_addtoseglist(&pobj->finode->s, pd->my_block, i); + } + /* Write the lfn and dos inode if we have the space */ + if (pobj->finode->s.nsegs == (n_segs +1)) + { + /* Write the long file name entries to their respective + locations */ + /* Drop the segment count by one before we write. + with this scheme we fall out of the loop and + put the DOSINODE in the correct location */ + pc_reduceseglist(&pobj->finode->s); + if (!pc_seglist2disk(pobj->pdrive, &pobj->finode->s, filename)) + { + pc_discard_buf(pbuff); + goto clean_and_fail; + } + pd->my_index = i; + /* Update the DOS disk */ + pc_ino2dos( pi, pobj->finode ); + /* Write the data */ + /* Mark the inode in the inode buffer */ + if (pc_write_blk(pbuff)) + { + pc_marki(pobj->finode , pobj->pdrive , pd->my_block, + pd->my_index ); + pc_release_buf(pbuff); + return(TRUE); + } + else + { + pc_release_buf(pbuff); + return(FALSE); + } + } + } + else + { + pc_zeroseglist(&pobj->finode->s); + } + i++; + pi++; + } + /* Not in that block. Try again */ + pc_release_buf(pbuff); + /* Update the objects block pointer */ + rtfs_set_errno(0); /* Should already be 0 but don't chance it */ + if (!pc_next_block(pobj)) + { + if (get_errno()) /* The zero was an error return */ + return (FALSE); + /* Ok:There are no slots in mom. We have to make one. And copy our stuff in */ + cluster = pc_grow_dir(pobj->pdrive, pmom); + if (!cluster) + return (FALSE); + + /* Do not forget where the new item is */ + pd->my_block = pc_cl2sector(pobj->pdrive , cluster); + pd->my_index = 0; + + /* Zero out the cluster */ + if (!pc_clzero( pobj->pdrive , cluster ) ) + goto clean_and_fail; + + /* Copy the item into the first block */ + pobj->pblkbuff = pbuff = pc_init_blk( pobj->pdrive , pd->my_block); + if (!pbuff) + goto clean_and_fail; + } + else /* 02-06-2003 - Added Else, had been lost */ + pobj->pblkbuff = pbuff = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + } + +clean_and_fail: + if (cluster) + pc_truncate_dir(pdrive, pmom, cluster); + return (FALSE); +} + + +/**************************************************************************** +PC_NIBBLEPARSE - Nibble off the left most part of a pathspec + + Description + Take a pathspec (no leading D:). and parse the left most element into + filename and files ext. (SPACE right filled.). + + Returns + Returns a pointer to the rest of the path specifier beyond file.ext + ****************************************************************************/ + /* Parse a path. Return NULL if problems or a pointer to the next */ +byte *pc_nibbleparse(byte *filename, byte *fileext, byte *path) /* __fn__*/ +{ + byte *p; + byte *t = filename; + + RTFS_ARGSUSED_PVOID((void *)fileext); + + p = path; + + if (!p) /* Path must exist */ + return (0); + while (CS_OP_IS_NOT_EOS(p)) + { + if (CS_OP_CMP_ASCII(p,'\\')) + { + CS_OP_INC_PTR(p); + break; + } + else + { + CS_OP_CP_CHR(t, p); + CS_OP_INC_PTR(t); + CS_OP_INC_PTR(p); + } + } + CS_OP_TERM_STRING(t); + + return (p); +} + + + +/***************************************************************************** +PC_CKSUM - Compute checksum on string + + Description + Computes a single UTINY checksum based on the first 11 characters in + the input string (test). This value is used to link LFNINODE's on the + disk directory table. + Returns + The value of the checksum +*****************************************************************************/ + +byte pc_cksum(byte *test) /*__fn__*/ +{ + byte sum,i; + + for (sum = i = 0; i < 11; i++) + { + sum = (byte)((((sum & 0x01)<<7)|((sum & 0xFE)>>1)) + test[i]); + } + + return(sum); +} + +void pcdel2lfi(LFNINODE *lfi, int nsegs) /* __fn__ */ +{ + for (;nsegs;nsegs--, lfi--) + lfi->lfnorder = PCDELETE; +} + + +BOOLEAN pc_deleteseglist(DDRIVE *pdrive, SEGDESC *s) /* __fn__ */ +{ +BLKBUFF *rbuf; +LFNINODE *lfi; +int ntodo_0,ntodo_1,ntodo_2; + + if (!s->nsegs) + return(TRUE); + /* Read segblock[0] and copy text */ + rbuf = pc_read_blk(pdrive, s->segblock[0]); + if (!rbuf) + return(FALSE); + lfi = (LFNINODE *) &rbuf->data[0]; + lfi += s->segindex; + /* If the lfn segments span two or more blocks then segblock[0] contains the + last block, segblock[1] contains next to last, segblock[3] contains + the last if there are three. We delete all of the segments up to + and including segindex in last block value stored in segblock[0]. */ + if (s->nsegs > s->segindex+1) + ntodo_0 = s->segindex+1; + else + ntodo_0 = s->nsegs; + + if (ntodo_0 > 0) /* Test just in case should never be <= 0 */ + pcdel2lfi(lfi, ntodo_0); + if ( !pc_write_blk(rbuf) ) + { + pc_discard_buf(rbuf); + return (FALSE); + } + else + { + pc_release_buf(rbuf); + } + if (s->segblock[1]) + { + rbuf = pc_read_blk(pdrive, s->segblock[1]); + if (!rbuf) + return (FALSE); + lfi = (LFNINODE *) &rbuf->data[0]; + lfi += 15; /* The last index */ + + /* Delete the next N segments. Clip it at 16 since there are only + 16 segments per block */ + ntodo_1 = s->nsegs - ntodo_0; + if (ntodo_1 > 16) + ntodo_1 = 16; + + if (ntodo_1 > 0) /* Test just in case should never be <= 0 */ + pcdel2lfi(lfi, ntodo_1); + if ( !pc_write_blk ( rbuf ) ) + { + pc_discard_buf(rbuf); + return (FALSE); + } + else + pc_release_buf(rbuf); + if (s->segblock[2]) + { + rbuf = pc_read_blk(pdrive, s->segblock[2]); + if (!rbuf) + return (FALSE); + lfi = (LFNINODE *) &rbuf->data[0]; + lfi += 15; /* The last index */ + + ntodo_2 = s->nsegs - (ntodo_1 + ntodo_0); + /* Delete the next N segments. Clip it at 16 since there are only + 16 segments per block - this should not happen but just to be safe*/ + if (ntodo_2 > 16) + ntodo_2 = 16; + + if (ntodo_2 > 0) /* Test just in case should never be <= 0 */ + pcdel2lfi(lfi, ntodo_2); + if ( !pc_write_blk ( rbuf ) ) + { + pc_discard_buf(rbuf); + return (FALSE); + } + else + pc_release_buf(rbuf); + } + } + return(TRUE); +} + +byte *text2lfi(byte *lfn, LFNINODE *lfi, int nsegs, byte ncksum, byte order) /* __fn__ */ +{ + int n; + BOOLEAN end_of_lfn = FALSE; + byte *pfi; + + for (;nsegs && !end_of_lfn;nsegs--, lfi--, order++) + { + pfi = lfi->lfname1; + for(n=0; n<10; n += 2, pfi += 2) + { + if (end_of_lfn) + *pfi = *(pfi+1) = 0xff; + else + { + CS_OP_TO_LFN(pfi, lfn); + CS_OP_INC_PTR(lfn); + if ((*pfi== 0) && (*(pfi+1) == 0)) + end_of_lfn = TRUE; + } + } + pfi = lfi->lfname2; + for(n=0; n<12; n += 2, pfi += 2) + { + if (end_of_lfn) + *pfi = *(pfi+1) = 0xff; + else + { + CS_OP_TO_LFN(pfi, lfn); + CS_OP_INC_PTR(lfn); + if ((*pfi== 0) && (*(pfi+1) == 0)) + end_of_lfn = TRUE; + } + } + pfi = lfi->lfname3; + for(n=0; n<4; n += 2, pfi += 2) + { + if (end_of_lfn) + *pfi = *(pfi+1) = 0xff; + else + { + CS_OP_TO_LFN(pfi, lfn); + CS_OP_INC_PTR(lfn); + if ((*pfi== 0) && (*(pfi+1) == 0)) + end_of_lfn = TRUE; + } + } + if (CS_OP_IS_EOS(lfn)) + { + end_of_lfn = TRUE; + } + if (end_of_lfn) + order |= FIRST_NAMESEG; + lfi->lfnorder = order; + lfi->lfnattribute = 0x0F; + lfi->lfnres = 0; + lfi->lfncksum = ncksum; + lfi->lfncluster = 0x0000; + } + return(lfn); +} + +BOOLEAN pc_seglist2disk(DDRIVE * pdrive, SEGDESC *s, byte *lfn) /* __fn__*/ +{ +BLKBUFF *rbuf; +LFNINODE *lfi; +int ntodo_0, ntodo_1, ntodo_2; +byte *psegtext; + if (!s->nsegs) + return(FALSE); + /* Read segblock[0] and copy text */ + rbuf = pc_read_blk(pdrive, s->segblock[0]); + if (!rbuf) + return(FALSE); + lfi = (LFNINODE *) &rbuf->data[0]; + lfi += s->segindex; + /* If the lfn segments span two or more blocks then segblock[0] + contains the last block and segblock[1] contains the next to last + if there are 3 blocks segblock[2] contains the first */ + if (s->nsegs > s->segindex+1) + ntodo_0 = s->segindex+1; + else + ntodo_0 = s->nsegs; + psegtext = lfn; /* Initialize the variable so compile doesn't complain */ + if (ntodo_0 > 0) /* should always be true */ + psegtext = text2lfi(lfn, lfi, ntodo_0, s->ncksum, 1); + if ( !pc_write_blk ( rbuf ) ) + { + pc_discard_buf(rbuf); + return (FALSE); + } + else + pc_release_buf(rbuf); + + if (s->segblock[1]) + { + rbuf = pc_read_blk(pdrive, s->segblock[1]); + if (!rbuf) + return(FALSE); + lfi = (LFNINODE *) &rbuf->data[0]; + lfi += 15; /* The last index */ + /* Do 16 more segments (means more blocks to follow) or whatever is + left */ + ntodo_1 = s->nsegs - ntodo_0; + if (ntodo_1 > 16) + ntodo_1 = 16; + if (ntodo_1 > 0) /* Should always be true */ + psegtext = text2lfi(psegtext, lfi, ntodo_1, s->ncksum, (byte) (ntodo_0+1)); + if ( !pc_write_blk ( rbuf ) ) + { + pc_discard_buf(rbuf); + return (FALSE); + } + else + pc_release_buf(rbuf); + + if (s->segblock[2]) + { + rbuf = pc_read_blk(pdrive, s->segblock[2]); + if (!rbuf) + return(FALSE); + lfi = (LFNINODE *) &rbuf->data[0]; + lfi += 15; /* The last index */ + /* Do 16 more segments (means more blocks to follow) or whatever is + left */ + ntodo_2 = s->nsegs - (ntodo_1 + ntodo_0); + if (ntodo_2 > 16) /* Should always be <= 16 */ + ntodo_2 = 16; + if (ntodo_2 > 0) /* Should always be true */ + psegtext = text2lfi(psegtext, lfi, ntodo_2, s->ncksum, (byte) (ntodo_1+ntodo_0+1)); + if ( !pc_write_blk ( rbuf ) ) + { + pc_discard_buf(rbuf); + return (FALSE); + } + else + pc_release_buf(rbuf); + } + } + return(TRUE); +} + + + +byte *lfi2text(byte *lfn, int *current_lfn_length, LFNINODE *lfi, int nsegs) /* __fn__ */ +{ + int n; + byte *pfi; + + for (;nsegs;nsegs--, lfi--) + { + pfi = (byte *) lfi->lfname1; + for(n=0; n<10; n += 2,pfi+=2) + { + if((*pfi==0x00)&&(*(pfi+1)==0x00)) + goto lfi2text_eos; + if (*current_lfn_length == FILENAMESIZE_CHARS) + return(0); + CS_OP_LFI_TO_TXT(lfn, pfi); + CS_OP_INC_PTR(lfn); + *current_lfn_length += 1; + } + pfi = (byte *) lfi->lfname2; + for(n=0; n<12; n += 2,pfi+=2) + { + if((*pfi==0x00)&&(*(pfi+1)==0x00)) + goto lfi2text_eos; + if (*current_lfn_length == FILENAMESIZE_CHARS) + return(0); + CS_OP_LFI_TO_TXT(lfn, pfi); + CS_OP_INC_PTR(lfn); + *current_lfn_length += 1; + } + pfi = (byte *) lfi->lfname3; + for(n=0; n<4; n += 2,pfi+=2) + { + if((*pfi==0x00)&&(*(pfi+1)==0x00)) + goto lfi2text_eos; + if (*current_lfn_length == FILENAMESIZE_CHARS) + return(0); + CS_OP_LFI_TO_TXT(lfn, pfi); + CS_OP_INC_PTR(lfn); + *current_lfn_length += 1; + } + CS_OP_TERM_STRING(lfn); + } + return(lfn); +lfi2text_eos: + CS_OP_TERM_STRING(lfn); + return(lfn); +} + +byte *pc_seglist2text(DDRIVE * pdrive, SEGDESC *s, byte *lfn) /* __fn__ */ +{ +BLKBUFF *rbuf; +LFNINODE *lfi; +byte *p; +int ntodo_0,ntodo_1,ntodo_2; +int current_lfn_length; + + CS_OP_TERM_STRING(lfn); + p = lfn; + if (!s->nsegs) + goto sl2_done; + /* Read segblock[0] and copy text */ + rbuf = pc_read_blk(pdrive, s->segblock[0]); + if (!rbuf) + goto sl2_done; + lfi = (LFNINODE *) &rbuf->data[0]; + lfi += s->segindex; + /* If the lfn segments span two or blocks then segblock[0] contains the + last block and segblock[1] contains next to last and if their are + three segblock[2] contains the first. */ + if (s->nsegs > s->segindex+1) + ntodo_0 = s->segindex+1; + else + ntodo_0 = s->nsegs; + current_lfn_length = 0; + p = lfi2text(p, ¤t_lfn_length, lfi, ntodo_0); + pc_release_buf(rbuf); + if (!p) + return(0); + if (s->segblock[1]) + { + rbuf = pc_read_blk(pdrive, s->segblock[1]); + if (!rbuf) + goto sl2_done; + lfi = (LFNINODE *) &rbuf->data[0]; + lfi += 15; /* The last index */ + /* Read the next N segments. Clip it at 16 since there are only + 16 segments per block */ + ntodo_1 = s->nsegs - ntodo_0; + if (ntodo_1 > 16) + ntodo_1 = 16; + if (ntodo_1) + p = lfi2text(p, ¤t_lfn_length, lfi, ntodo_1); + pc_release_buf(rbuf); + if (!p) + return(0); + + if (s->segblock[2]) + { + rbuf = pc_read_blk(pdrive, s->segblock[2]); + if (!rbuf) + goto sl2_done; + lfi = (LFNINODE *) &rbuf->data[0]; + lfi += 15; /* The last index */ + /* Read the next N segments. Clip it at 16 since there are only + 16 segments per block */ + ntodo_2 = s->nsegs - (ntodo_1 + ntodo_0); + if (ntodo_2 > 16) + ntodo_2 = 16; + if (ntodo_2) + p = lfi2text(p, ¤t_lfn_length, lfi, ntodo_2); + pc_release_buf(rbuf); + if (!p) + return(0); + } + } +sl2_done: + return(lfn); +} + +void pc_zeroseglist(SEGDESC *s) /* __fn__ */ +{ +/* Note: we do not zero the checksum field here */ + s->nsegs = 0; + s->segblock[0] = + s->segblock[1] = + s->segblock[2] = + s->segindex = 0; +} +void pc_addtoseglist(SEGDESC *s, BLOCKT my_block, int my_index) /*__fn__*/ +{ + s->nsegs += 1; + /* The block list is a LIFO stack so if it's empty start it + otherwise ripple copy in */ + if (!s->segblock[0]) + { + s->segblock[0] = my_block; + } + else if ( s->segblock[0] != my_block && + s->segblock[1] != my_block && + s->segblock[2] != my_block) + { + s->segblock[2] = s->segblock[1]; + s->segblock[1] = s->segblock[0]; + s->segblock[0] = my_block; + } + s->segindex = my_index; +} + +/* This function is used by pc_insert_inode(). That function builds a seglist + that is 1 segment longer then the lfn. The extra segment is where the + dosinode will be placed. Before we write the the lfn to disk we call this + function to reduce the segment list by one. */ + +void pc_reduceseglist(SEGDESC *s) /*__fn__ */ +{ + if (s->nsegs) /* This should always be true */ + { + s->nsegs -= 1; + if (s->segblock[2] && s->segindex == 0) + { + s->segblock[0] = s->segblock[1]; + s->segblock[1] = s->segblock[2]; + s->segblock[2] = 0; + s->segindex = INOPBLOCK-1; + } + else if (s->segblock[1] && s->segindex == 0) + { + s->segblock[0] = s->segblock[1]; + s->segblock[1] = s->segblock[2] = 0; + s->segindex = INOPBLOCK-1; + } + else + { + if (s->segindex) /* This should always be true */ + s->segindex -= 1; + } + } +} + +/**************************************************************************** +PC_PARSEPATH - Parse a path specifier into path,file,ext + + Description + Take a path specifier in path and break it into three null terminated + strings topath,filename and file ext. + The result pointers must contain enough storage to hold the results. + Filename and fileext are BLANK filled to [8,3] spaces. + + Rules: + + SPEC PATH FILE EXT + B:JOE B: 'JOE ' ' ' + B:\JOE B:\ 'JOE ' ' ' + B:\DIR\JOE B:\DIR 'JOE ' ' ' + B:DIR\JOE B:DIR 'JOE ' ' ' + Returns + Returns TRUE. + + + ****************************************************************************/ +BOOLEAN pc_parsepath(byte *topath, byte *filename, byte *fileext, byte *path) /*__fn__*/ +{ + int i,keep_slash; + byte /**lastchar,*/*pfile,*pslash,*pcolon,*p,*pto,*pfilespace; + RTFS_ARGSUSED_PVOID((void *)fileext); + + /* Check the path length, compare it EMAXPATH_CHARS (255) the + maximum filename length for VFAT */ + if (rtfs_cs_strlen(path) > EMAXPATH_CHARS) + return(FALSE); + + pslash = pfile = 0; + p = path; + pcolon = 0; + keep_slash = 0; + /* if A:\ or \ only keep slash */ + i = 0; + while (CS_OP_IS_NOT_EOS(p)) + { + if (CS_OP_CMP_ASCII(p,'\\')) + pslash = p; + else if (CS_OP_CMP_ASCII(p,':')) + { + if (i != 1) /* A: B: C: .. x: y: z: only */ + return (FALSE); + pcolon = p; + } + CS_OP_INC_PTR(p); + i++; + } + if (pslash == path) + keep_slash = 1; + else if (pcolon && pslash) + { + CS_OP_INC_PTR(pcolon); + if (pslash == pcolon) + keep_slash = 1; + } + + + /*lastchar = 0;*/ + p = path; + /* Find the file section, after the colon or last backslash */ + while (CS_OP_IS_NOT_EOS(p)) + { + if (CS_OP_CMP_ASCII(p,'\\') || CS_OP_CMP_ASCII(p,':') ) + { + CS_OP_INC_PTR(p); + pfile = p; + } + else + { + CS_OP_INC_PTR(p); + } + } + + /* Now copy the path. Up to the file or NULL if no file */ + pto = topath; + if (pfile) + { + p = path; + while (p < pfile) + { + /* Don't put slash on the end if more than one */ + if (p == pslash && !keep_slash) + break; + CS_OP_CP_CHR(pto, p); + CS_OP_INC_PTR(pto); + CS_OP_INC_PTR(p); + } + } + CS_OP_TERM_STRING(pto); + /* Now copy the file portion or the whole path to file if no path portion */ + pto = filename; + if (pfile) + p = pfile; + else + p = path; + + /* Skip leading spaces */ + while (CS_OP_CMP_ASCII(p,' ')) + CS_OP_INC_PTR(p); + /* Check the file length */ + if (rtfs_cs_strlen(p) > FILENAMESIZE_CHARS) + return(FALSE); + + pfilespace = 0; + while (CS_OP_IS_NOT_EOS(p)) + { + CS_OP_CP_CHR(pto, p); + CS_OP_INC_PTR(p); + if (CS_OP_CMP_ASCII(pto,' ')) + { + if (!pfilespace) + pfilespace = pto; + } + else + pfilespace = 0; + CS_OP_INC_PTR(pto); + } + /* If the trailing character is a space NULL terminate */ + if (pfilespace) + {CS_OP_TERM_STRING(pfilespace);} + else + CS_OP_TERM_STRING(pto); + return(TRUE); +} +/****************************************************************************** +PC_PATCMP - Compare a pattern with a string + + Description + Compare size bytes of p against pattern. Applying the following rules. + If size == 8. + (To handle the way dos handles deleted files) + if p[0] = DELETED, never match + if pattern[0] == DELETED, match with 0x5 + + '?' in pattern always matches the current char in p. + '*' in pattern always matches the rest of p. + Returns + Returns TRUE if they match + +****************************************************************************/ +BOOLEAN pc_patcmp_vfat(byte *in_pat, byte *name, BOOLEAN dowildcard) /*__fn__*/ +{ + byte *pat, *p, *pp, *pn, *pp2, *pn2; + byte star[4]; + BOOLEAN res = FALSE; + + /* Convert *.* to just * */ + p = pat = in_pat; + if (dowildcard && CS_OP_CMP_ASCII(p,'*')) + { + CS_OP_INC_PTR(p); + if (CS_OP_CMP_ASCII(p,'.')) + { + CS_OP_INC_PTR(p); + if (CS_OP_CMP_ASCII(p,'*')) + { + CS_OP_INC_PTR(p); + if (CS_OP_IS_EOS(p)) + { + /* Change *.* to * but since the argument may have been + const we do it in a private buffer */ + p = pat = star; + CS_OP_ASSIGN_ASCII(p,'*'); + CS_OP_INC_PTR(p); + CS_OP_TERM_STRING(p); + } + } + } + } + /* * matches everything */ + p = pat; + if (dowildcard && CS_OP_CMP_ASCII(p,'*')) + { + CS_OP_INC_PTR(p); + if (CS_OP_IS_EOS(p)) + return(TRUE); + } + + for (pp=pat,pn=name;CS_OP_IS_NOT_EOS(pp); CS_OP_INC_PTR(pn),CS_OP_INC_PTR(pp)) + { + if(CS_OP_CMP_ASCII(pp,'*') && dowildcard) + { + pp2 = pp; + CS_OP_INC_PTR(pp2); + if (CS_OP_IS_EOS(pp2)) + return(TRUE); /* '*' at end */ + pn2 = pn; + /* We hit star. Now go past it and see if there is another + exact match. IE: a*YYY matches abcdefgYYY but not abcdefgXXX */ + for (;!res && CS_OP_IS_NOT_EOS(pn2); CS_OP_INC_PTR(pn2)) + { + res = res+pc_patcmp_vfat(pp2,pn2,TRUE); + } + return(res); + } + + else if (CS_OP_CMP_CHAR(pp, pn)) + ; + else if (CS_OP_CMP_ASCII(pp,'?') && dowildcard) + ; + else if (CS_OP_CMP_CHAR_NC(pp, pn)) + ; + else + return(FALSE); + } + if(CS_OP_IS_EOS(pn)) + return(TRUE); + else + return(FALSE); +} + +BOOLEAN pc_patcmp_vfat_8_3(byte *pat, byte *name, BOOLEAN dowildcard) /*__fn__*/ +{ + byte save_char; + BOOLEAN ret_val; + if (CS_OP_CMP_ASCII(name,PCDELETE)) + return (FALSE); + save_char = *name; + if (*name == 0x05) + *name = 0xe5; + ret_val = pc_patcmp_vfat(pat, name, dowildcard); + *name = save_char; + return(ret_val); +} + +/***************************************************************************** +PC_MALIAS - Create a unique alias for input_file + + Description + Fills fname and fext with a valid short file name alias that is unique + in the destination directory (dest). Not to be confused with pc_galias, + which finds the currently used short file name alias for an existing + file. + Returns + TRUE if a unique alias could be found, FALSE otherwise +*****************************************************************************/ + +BOOLEAN pc_malias(byte *fname, byte *fext, byte *input_file, DROBJ *dest) /*__fn__*/ +{ + int try; + DROBJ *pobj; + BOOLEAN aliasunique; + byte alias[26]; + byte ascii_alias[26]; + + /* See if already a valid alias. If so we use */ + /* Note: This always fails for unicode */ + if (pc_cs_malias(alias, input_file, -1)) /*__fn__*/ + { + pc_ascii_str2upper(alias,alias); + pobj = pc_get_inode(0,dest,alias,0,GET_INODE_MATCH); + if(pobj) + { + pc_freeobj(pobj); + } + else + { + if (get_errno() != PENOENT) + return(FALSE); + rtfs_set_errno(0); /* Clear PENOENT */ + pc_ascii_fileparse(fname,fext,(byte *)input_file); + pc_ascii_str2upper(fname,fname); + pc_ascii_str2upper(fext,fext); + return(TRUE); + } + } + + + /* Loop building up alias names and testing if unique */ + try=0; /* i=0; i is the alias index */ + do + { + try++; + if (!pc_cs_malias(alias, input_file, try)) + { + /* Ran out of valid alias names. Use PENOSPC as errno */ + rtfs_set_errno(PENOSPC); + return(FALSE); + } + + pobj = pc_get_inode(0,dest,alias,0,GET_INODE_MATCH); + if(pobj) + { + aliasunique = FALSE; + pc_freeobj(pobj); + } + else + { + if (get_errno() != PENOENT) + return(FALSE); + aliasunique = TRUE; + } + } + while(!aliasunique); + + rtfs_set_errno(0); /* Clear PENOENT */ + /* Parse the alias into 8.3 */ + CS_OP_CS_TO_ASCII_STR(ascii_alias,alias); + pc_ascii_fileparse(fname,fext,ascii_alias); + + return(TRUE); +} +/* Byte oriented */ +static BOOLEAN pc_allspace(byte *p, int i) /* __fn__*/ +{while (i--) if (*p++ != ' ') return (FALSE); return (TRUE); } + +BOOLEAN _illegal_lfn_char(byte ch) +{ + if (pc_strchr(rtfs_strtab_user_string(USTRING_SYS_BADLFN), ch)) + return(TRUE); + else + return(FALSE); +} + +BOOLEAN pc_isdot(byte *fname, byte *fext) /* __fn__*/ +{ + RTFS_ARGSUSED_PVOID((void *)fext); + return (BOOLEAN)((*fname == '.') && + ((*(fname+1) == '\0') || (pc_allspace((fname+1),10))) ); +} +BOOLEAN pc_isdotdot(byte *fname, byte *fext) /* __fn__*/ +{ + RTFS_ARGSUSED_PVOID((void *)fext); + return (BOOLEAN)( (*fname == '.') && (*(fname+1) == '.') && + ((*(fname+2) == '\0') || (pc_allspace((fname+2),9)) ) ); +} + +BOOLEAN pc_delete_lfn_info(DROBJ *pobj) +{ + return(pc_deleteseglist(pobj->pdrive, &pobj->finode->s)); +} +void pc_zero_lfn_info(FINODE *pdir) +{ + pc_zeroseglist(&pdir->s); +} +BOOLEAN pc_get_lfn_filename(DROBJ *pobj, byte *path) +{ + if (pobj->finode->s.nsegs) + { + if (pc_seglist2text(pobj->pdrive, &pobj->finode->s, path)) + return(TRUE); + else + return(FALSE); + } + else + return(FALSE); +} + +/* scan_for_bad_lfns(DROBJ *pmom) + * + * Scan through a directory and count all Win95 long file name errors. + * + * Errors detected: + * Bad lfn checksums + * Bad lfn sequence numbers + * Stray lfn chains + * Incomplete lfn chains + * + * If gl.delete_bad_lfn is set, free up the directory space used up by + * invalid long file name information by deleting invalid chains + * + * Returns: number of invalid lfn chains found + * + * This fn is called by chkdsk. It is based on pc_findin + */ + +dword scan_for_bad_lfns(DROBJ *pmom, int delete_bad_lfn) /*__fn__*/ +{ +DROBJ *pobj; +BLKBUFF *rbuf; +DIRBLK *pd; +DOSINODE *pi; +LFNINODE *lfn_node; +SEGDESC s; +byte lastsegorder; +dword bad_lfn_count; + + pc_zeroseglist(&s); + lastsegorder = 0; + + /* pobj will be used to scan through the directory */ + pobj = pc_mkchild(pmom); + if (!pobj) + return(0); + + bad_lfn_count = 0; + + /* For convenience. We want to get at block info here */ + pd = &pobj->blkinfo; + + /* Read the data */ + pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + + while (rbuf) + { + pi = (DOSINODE *) &rbuf->data[0]; + + /* Look at the current inode */ + pi += pd->my_index; + + while ( pd->my_index < INOPBLOCK ) + { + /* End of dir if name is 0 */ + if (!pi->fname[0]) + { + pc_release_buf(rbuf); + pc_freeobj(pobj); + return(bad_lfn_count); + } + + if (pi->fname[0] == PCDELETE) + { + if (s.nsegs) + { + /* lfn chain interrupted by empty dir entry */ + bad_lfn_count++; + if (delete_bad_lfn) + pc_deleteseglist(pobj->pdrive, &s); + pc_zeroseglist(&s); + lastsegorder = 0; + } + } + else + { + if (pi->fattribute == CHICAGO_EXT) + { + /* Found a piece of an lfn */ + lfn_node = (LFNINODE *) pi; + + if (lfn_node->lfnorder & FIRST_NAMESEG) + { + if (s.nsegs) /* if a chain already exists */ + { + /* lfn chain begins in the middle of another one; + Delete the one that was interrupted, keep the + new one. */ + bad_lfn_count++; + if (delete_bad_lfn) + pc_deleteseglist(pobj->pdrive, &s); + pc_zeroseglist(&s); + } + lastsegorder = lfn_node->lfnorder; + pc_addtoseglist(&s, pd->my_block, pd->my_index); + s.ncksum = lfn_node->lfncksum; + } + else + { + /* optimization - the current segment should first be + linked onto the chain in each of the branches + below */ + pc_addtoseglist(&s, pd->my_block, pd->my_index); + if ((s.nsegs - 1) && + (lfn_node->lfnorder & NAMESEG_ORDER) == (lastsegorder & NAMESEG_ORDER) - 1 && + lfn_node->lfncksum == s.ncksum ) + { + /* Sequence number and checksum match; Add new + segment to lfn chain */ + lastsegorder = lfn_node->lfnorder; + } + else + { + /* New segment has a checksum or sequence number + that doesn't match the current chain; Delete + the whole chain plus new segment */ + bad_lfn_count++; + if (delete_bad_lfn) + pc_deleteseglist(pobj->pdrive, &s); + pc_zeroseglist(&s); + lastsegorder = 0; + } + } + } + else + /* if (pi->fattribute != CHICAGO_EXT) */ + { + /* Found a file entry - make sure our lfn chain matches + (if we have one) */ + if (s.nsegs) /* if a chain has been built up */ + { + if (pc_cksum((byte*)pi) != s.ncksum || + (lastsegorder & NAMESEG_ORDER) != 1) + { + /* chain's checksum doesn't match the DOSINODE, + or the last sequence number isn't 1; Delete + the lfn chain */ + bad_lfn_count++; + if (delete_bad_lfn) + pc_deleteseglist(pobj->pdrive, &s); + } + /* We want to release this chain, whether good or bad */ + pc_zeroseglist(&s); + lastsegorder = 0; + } + } /* if (!CHICAGO_EXT) */ + } /* if (!PCDELETE) */ + pd->my_index++; + pi++; + } + /* Current block is clean; go to next one */ + pc_release_buf(rbuf); + /* Update the objects block pointer */ + if (!pc_next_block(pobj)) + break; + pd->my_index = 0; + pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block); + } + + pc_freeobj(pobj); + return (bad_lfn_count); +} + +#endif + + diff --git a/build/libraries/fatfs/ARM7/winhdisk.c b/build/libraries/fatfs/ARM7/winhdisk.c new file mode 100644 index 0000000..10ab5c2 --- /dev/null +++ b/build/libraries/fatfs/ARM7/winhdisk.c @@ -0,0 +1,452 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright Peter Van Oudenaren , 1993 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* winhdisk.c - Simulate disk drive in a file on the host operating system + +Summary + + Description + Provides a device driver that reads and writes data to a file on the + host disk. This version of the driver creates a file named + HOSTDISK.DAT. This is targeted for hosts runnig Win95/NT but it can + be migrated to other hosts. + + + The format routine requires an initialized external variable + named: + extern word host_disk_size; + that tells it what size in blocks the file should be formatted to. + + + This driver is used primarilly for the MKROM utility but it is + fully functional and can be used for other purposes. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtfs.h" +#include "portconf.h" /* For included devices */ + +#if (INCLUDE_HOSTDISK) + +/* hostdisk - Device driver that uses a DOS file as a virtual disk. + + Description + Provides a virtual volume that resides in a (host) DOS file. This + volume can be used to prototype applications. It can also be used to build + the image of a rom disk which can then be accessed through the + rom disk driver. (see winmkrom.c) + + To enable this driver set INCLUDE_HOSTDISK to 1 in portconf.h +*/ + + + +#define DEFAULT_HOST_DISK_SIZE 10240 /* 5M, FAT16 */ + +#define WINDOWS_HOSTDISK_PRINTF printf +#define WINDOWS_HOSTDISK_SPRINTF sprintf + + +void calculate_hcn(long n_blocks, PDEV_GEOMETRY pgeometry); + + +#define MAXSEGMENTS_PER_UNIT 16 +#define MAX_UNITS 8 +struct file64 { +char basename[255]; +int num_segments; +HANDLE segment_handles[MAXSEGMENTS_PER_UNIT]; +}; +BOOLEAN flush_all_writes; +struct file64 sixty_four_bit_volumes[MAX_UNITS]; + +/* there are 0x200000 blokcs per gigabyte segment */ + +#define BLOCKS_PER_GIG 0x200000 + +int alloc_64bit_unit() +{ +int i; + for (i = 0 ;i < MAX_UNITS; i++) + { + if (!sixty_four_bit_volumes[i].num_segments) + return(i); + } + return(-1); +} +int open_64bit_volume(int unit, byte *basename) +{ +dword i; +char segment_file_name[256]; + + rtfs_strcpy((byte *) &(sixty_four_bit_volumes[unit].basename[0]), basename); + sixty_four_bit_volumes[unit].num_segments = 0; + for (i = 0;;i++) + { + WINDOWS_HOSTDISK_SPRINTF(segment_file_name,"%s_SEGMENT_%x.HDK",basename,i); + /* reopen the file segments */ + sixty_four_bit_volumes[unit].segment_handles[i] = + CreateFile(TEXT(segment_file_name), // file to open + GENERIC_READ|GENERIC_WRITE, + 0, // share for reading + NULL, // default security + OPEN_EXISTING, // existing file only + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING, + NULL); // no attr. template + + if (sixty_four_bit_volumes[unit].segment_handles[i] == INVALID_HANDLE_VALUE) + { + if (i == 0) + return(-1); /* Nothing there */ + else + return(0); + } + else + sixty_four_bit_volumes[unit].num_segments += 1; + } +} +int create_64bit_volume(int unit, byte *basename, dword nblocks) +{ +dword i; +char segment_file_name[256]; + rtfs_strcpy((byte *)sixty_four_bit_volumes[unit].basename, (byte *)basename); + sixty_four_bit_volumes[unit].num_segments = (nblocks + BLOCKS_PER_GIG - 1)/BLOCKS_PER_GIG; + for (i = 0 ;i < (dword)sixty_four_bit_volumes[unit].num_segments; i++) + { + WINDOWS_HOSTDISK_SPRINTF(segment_file_name,"%s_SEGMENT_%x.HDK",basename,i); + sixty_four_bit_volumes[unit].segment_handles[i] = + CreateFile(TEXT(segment_file_name), // file to open + GENERIC_READ|GENERIC_WRITE, + 0, // share for reading + NULL, // default security + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING, + NULL); // no attr. template + + if (sixty_four_bit_volumes[unit].segment_handles[i] == INVALID_HANDLE_VALUE) + return(-1); + } + return(0); +} +void close_64bit_volume(int unit) +{ +dword i; + for (i = 0 ;i < (dword)sixty_four_bit_volumes[unit].num_segments; i++) + CloseHandle(sixty_four_bit_volumes[unit].segment_handles[i]); +} + + +dword size_64bit_volume(int unit) +{ +DWORD size, s; + + if (!sixty_four_bit_volumes[unit].num_segments) + return(0); + + size = (sixty_four_bit_volumes[unit].num_segments-1) * BLOCKS_PER_GIG; + s = SetFilePointer(sixty_four_bit_volumes[unit].segment_handles[sixty_four_bit_volumes[unit].num_segments-1], + 0,0, FILE_END); + if (s == INVALID_SET_FILE_POINTER) + return(0); + size += s/512; + return((dword)size); +} + + +BOOLEAN hostdisk_io_64(int unit, dword block, void *buffer, word _count, BOOLEAN reading) /*__fn__*/ +{ +dword segment_number; +dword max_count,block_offset,count; +dword s,ltemp; +HANDLE hFile; +dword nbytes,nblocks; +byte *bbuffer; + bbuffer = (byte *) buffer; + count = (dword) _count; +// if (reading) +// printf("%d,", block); +//else +// printf("%d;", block); +//if (block < 2000) +// printf("\n"); + while (count) + { + segment_number = block/BLOCKS_PER_GIG; + block_offset = block%BLOCKS_PER_GIG; + hFile = sixty_four_bit_volumes[unit].segment_handles[segment_number]; + ltemp = (dword)(block_offset * 512); + s = SetFilePointer(hFile, + ltemp,0, FILE_BEGIN); + if (s == INVALID_SET_FILE_POINTER) + return(FALSE); + if (s != ltemp) + { + return(FALSE); + } + max_count = (BLOCKS_PER_GIG - block_offset); + if (count > max_count) + { + nblocks = max_count; + } + else + { + nblocks = (int) count; + } + block += (dword)nblocks; + count -= (dword)nblocks; + nbytes = nblocks*512; + if (reading) + { + dword nread; + if (!ReadFile(hFile,bbuffer,nbytes,&nread,0) || nread != nbytes) + return(FALSE); + } + else + { + dword nwrote; + if (!WriteFile(hFile,bbuffer,nbytes,&nwrote,0) || nwrote != nbytes) + return(FALSE); + } + bbuffer += nbytes; + } + return(TRUE); +} + + + +/* +* +* Perform io to and from the hostdisk. +* +* If the reading flag is true copy data from the hostdisk (read). +* else copy to the hostdisk. (write). +* +*/ + +extern dword simulate_disk_latency; +dword decrement(dword l); + +BOOLEAN hostdisk_io(int driveno, dword block, void *buffer, word count, BOOLEAN reading) /*__fn__*/ +{ + DDRIVE *pdr; + + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr) + return(FALSE); + return(hostdisk_io_64(pdr->logical_unit_number, block, buffer, count, reading)); +} + +/* file is already truncated, this just expands it to correct size */ +static BOOLEAN hostdisk_make_file(int driveno, dword numblocks) +{ + dword block; + BLKBUFF *buf; + BOOLEAN saved_flush_all_writes; + dword segment; + buf = pc_scratch_blk(); + if (!buf) + return(FALSE); + + rtfs_memset(buf->data, 0, 512); + block = 0; + /* seek to the end of each one gig segment of the file and write one block + (this will extend the file) */ + saved_flush_all_writes = flush_all_writes; + flush_all_writes = TRUE; /* don't change.. experimental doesn't speed things up */ + segment = 1; + while (block < numblocks-1) + { + dword ltemp; + ltemp = (segment * BLOCKS_PER_GIG) -1; + if (ltemp >= numblocks) + block = numblocks - 1; + else + block = ltemp; + if (!hostdisk_io(driveno, block, buf->data, 1, FALSE)) + { + flush_all_writes = saved_flush_all_writes; + pc_free_scratch_blk(buf); + return(FALSE); + } + segment += 1; + } + flush_all_writes = saved_flush_all_writes; + pc_free_scratch_blk(buf); + return(TRUE); +} + +int hostdisk_perform_device_ioctl(int driveno, int opcode, void * pargs) +{ +DDRIVE *pdr; + + pdr = pc_drno_to_drive_struct(driveno); + if (!pdr) + return (-1); + + switch (opcode) + { + case DEVCTL_GET_GEOMETRY: + { + DEV_GEOMETRY gc; + + rtfs_memset(&gc, 0, sizeof(gc)); + + if (pdr->logical_unit_number < 0) + return(-1); + + /* Now set the geometry section */ + /* This is a simple default condition. It is overriden by + code in MKROM when the hostdisk is being populated from + a disk sub-tree */ + gc.dev_geometry_lbas = size_64bit_volume(pdr->logical_unit_number); + if (!gc.dev_geometry_lbas) + return(-1); + calculate_hcn(gc.dev_geometry_lbas, &gc); + copybuff(pargs, &gc, sizeof(gc)); + return (0); + } + + case DEVCTL_FORMAT: + { + /* Fill the file with zeroes */ + dword l; + int fd, answer; + PDEV_GEOMETRY pgc; + byte buf[20]; + + pgc = (PDEV_GEOMETRY) pargs; + + fd = pdr->logical_unit_number; + /* The file should be open already */ + if (fd < 0) + return(-1); + + l = (dword) pgc->dev_geometry_heads; + l = (dword) l * pgc->dev_geometry_cylinders; + l = (dword) l * pgc->dev_geometry_secptrack; + if (pgc->dev_geometry_lbas) + l = pgc->dev_geometry_lbas; + + answer = '1'; + do + { + if (answer != '1') + { + WINDOWS_HOSTDISK_PRINTF("I didn't understand your choice.\n"); + } + /* Here we want to prompt the user so that he or she can + change the size of the hostdisk to several presets. */ + WINDOWS_HOSTDISK_PRINTF("What size do you want the hostdisk '%s' to be?\n", (char*)pdr->device_name); + WINDOWS_HOSTDISK_PRINTF(" 1) FAT12 (4M)\n"); /* 8192 blocks */ + WINDOWS_HOSTDISK_PRINTF(" 2) FAT16 (16M)\n"); /* 32768 blocks */ + WINDOWS_HOSTDISK_PRINTF(" 3) FAT32 (1G)\n"); /* 2097152 blocks */ + WINDOWS_HOSTDISK_PRINTF(" 4) Current (~%iM)\n", l/2048); + WINDOWS_HOSTDISK_PRINTF(" 5) Custom\n"); + WINDOWS_HOSTDISK_PRINTF(" 6) FAT32 (16G)\n"); /* 33554432 blocks */ + WINDOWS_HOSTDISK_PRINTF(": "); + rtfs_port_tm_gets(buf); + if (strlen((char*)buf) == 1) + answer = buf[0]; + else + answer = 'X'; /* wrong */ + } + while (answer < '1' || answer > '6'); + if (answer == '5') + { + l = 1; + do + { + if (l == 0) + { + WINDOWS_HOSTDISK_PRINTF("I didn't understand your choice.\n"); + } + WINDOWS_HOSTDISK_PRINTF("How many megabytes would you like the hostdisk to contain?\n"); + WINDOWS_HOSTDISK_PRINTF(": "); + rtfs_port_tm_gets(buf); + l = atoi((char*)buf); + } + while (l == 0); + l *= 2048; + } + switch (answer) + { + case '1': l = 8192; break; + case '2': l = 32768; break; + case '3': l = 2097152; break; + case '6': l = 33554432; break; + case '4': /* fall through */ + case '5': /* fall through */ + default: /* shouldn't happen */ + break; + } + WINDOWS_HOSTDISK_PRINTF("Making disk %iM.\n", l/2048); + close_64bit_volume(fd); + if (create_64bit_volume(fd, pdr->device_name, l) != 0) + return(-1); + if (!hostdisk_make_file(driveno, l)) + return(-1); + + /* Update caller's idea of geometry */ + pgc->dev_geometry_lbas = l; + calculate_hcn(pgc->dev_geometry_lbas, pgc); + + return(0); + } + break; + case DEVCTL_REPORT_REMOVE: + pdr->drive_flags &= ~DRIVE_FLAGS_INSERTED; + return(0); + case DEVCTL_CHECKSTATUS: + if (pdr->drive_flags & DRIVE_FLAGS_INSERTED) + return(DEVTEST_NOCHANGE); + else + { + pdr->drive_flags |= DRIVE_FLAGS_INSERTED; + return(DEVTEST_CHANGED); + } + case DEVCTL_WARMSTART: + { + flush_all_writes = TRUE; /* Enable flush after each write by default */ + pdr->logical_unit_number = alloc_64bit_unit(); + if (pdr->logical_unit_number < 0) + return(-1); + /* See if it already exists */ + if (open_64bit_volume(pdr->logical_unit_number, pdr->device_name) < 0) + { + pdr->drive_flags |= DRIVE_FLAGS_FORMAT; + if (create_64bit_volume(pdr->logical_unit_number, pdr->device_name, DEFAULT_HOST_DISK_SIZE) != 0) + return(-1); + if (!hostdisk_make_file(driveno, DEFAULT_HOST_DISK_SIZE)) + return(-1); + } + pdr->drive_flags |= (DRIVE_FLAGS_VALID|DRIVE_FLAGS_INSERTED|DRIVE_FLAGS_REMOVABLE); + return(0); + } + /* Fall through */ + case DEVCTL_POWER_RESTORE: + /* Fall through */ + case DEVCTL_POWER_LOSS: + /* Fall through */ + default: + break; + } + return(0); + +} + + +#endif /* (INCLUDE_HOSTDISK) */ diff --git a/build/libraries/fatfs/ARM7/winmkrom.c b/build/libraries/fatfs/ARM7/winmkrom.c new file mode 100644 index 0000000..b301ba5 --- /dev/null +++ b/build/libraries/fatfs/ARM7/winmkrom.c @@ -0,0 +1,661 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc, 1993-2002 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* +***************************************************************************** + MKROM.C - Create a rom disk from an MS-DOS sudirectory + +Summary + + MKROM subdir + + + Description + Traverses the subdirectory "subdir" and make an exact image of that + subdirectory in a file named romdisk.dat. The file romdisk.dat + can be placed directly into a rom and accessed via the rom disk + device driver. + + + + +Example: + + mkrom c:\dev\romimage + + +***************************************************************************** +*/ + +#include +#include /* For included devices */ +#if (INCLUDE_HOSTDISK) +/* Host disk is required to use the MKROM utility */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Write your own routines to create these. */ +#define BIN_VOL_LABEL 0x12345678L +#define STR_VOL_LABEL "VOLUMELABE" /* 11 chars max */ + +typedef struct _finddata_t statobj_t; + +#define MKROM_MAX_PATH_LEN 260 +#define FIXED_BLOCKS 2 /* Block zero and 1 reserved + 2 fudge */ +#define CLUSTER_FUDGE 2 + +void drno_to_string(byte *pname, int drno); /* See rtfsinit.c */ +int traverse(byte *name, int level); +int copy_files(byte *name, int level); +int make_directories(byte *name, int level); + +byte imagepath[MKROM_MAX_PATH_LEN]; + +int root_path_len = 0; /* remember how much of each path is the root */ + +long required_volume_size = 0; /* Derived in calculate_format_parms */ + +int n_subdirectories = 0; /* Total number of subdirectories */ +int n_files = 0; /* Total number of files */ +int n_root_entries = 0; /* Entries in the root */ +int n_sub_entries = 0; /* Entries in subdirectories */ +long file_cluster_count[33]; /* Count of files with */ +long subdir_cluster_count[33]; /* Count of subdirs with */ + +BOOLEAN calculate_format_parms(DDRIVE *pdr, PDEV_GEOMETRY pgeometry); + +BOOLEAN hostdisk_io(int driveno, dword block, void *buffer, word count, BOOLEAN reading); + +int domkhostdisk(int agc, byte **agv) /*__fn__*/ +{ +int j,driveno; +byte drname[8]; +byte inbuff[80]; +DDRIVE *pdr; +DEV_GEOMETRY geometry; + + + if (agc != 1) + { + printf("Usage MKHOSTDISK path\n"); + return(0); + } + else /* Get the path */ + { +#if (INCLUDE_CS_UNICODE) + /* Convert unicode string to ASCII fro accessing win9x file system */ + map_unicode_to_ascii((byte *)&imagepath[0], (byte *) *agv); +#else + strcpy((char *)&imagepath[0],(char *)*agv); +#endif + } + + n_subdirectories = 0; + n_files = 0; + n_root_entries = 0; + n_sub_entries = 0; + for (j = 0; j< 33; j++) + { + file_cluster_count[j] = 0; + subdir_cluster_count[j] = 0; + } + + driveno = -1; + pdr= prtfs_cfg->mem_drives_structures; + for (j = 0; j < prtfs_cfg->cfg_NDRIVES; j++, pdr++) + { + if (pdr->dev_table_drive_io == hostdisk_io) + { + drno_to_string(drname, pdr->driveno); + driveno = pdr->driveno; + break; + } + } + + if (driveno == -1 || !(pdr->drive_flags&DRIVE_FLAGS_VALID) ) + { + printf("Fail: No host disk device is available\n"); + return 0; + } + + /* Prepare to format the hostdisk */ + /* check media and clear change conditions */ + if (!check_drive_number_present(driveno)) + { + printf("Check media failed can't format hostdisk.\n"); + return(0); + } + /* Get media parms, but override in next section */ + if (!pc_get_media_parms(drname, &geometry)) + { + printf("Drive %s get media failed.\n", drname); + return(0); + } + /* How long is the path to the root.. We'll strip this off of the + front of the strings when we build RTFS files in the file image */ + root_path_len = rtfs_strlen(imagepath); + + + printf("Populating host disk at %s from native subdirectory %s\n", drname, imagepath); + + traverse(imagepath, 0); + if (!calculate_format_parms(pdr, &geometry)) + return(0); + printf("\n"); + printf("\n"); + printf("Host Disk Image Will Contain\n"); + printf("\t %10.10d Files\n", n_files); + printf("\t %10.10d Subdirectories\n", n_subdirectories); + printf("\t requiring %10.10ld blocks\n", required_volume_size); + printf("\n"); + printf("\n"); + printf("Type Y to create the Host Disk Image, any other key to exit\n"); + gets((char *)inbuff); + printf("\n"); + if (inbuff[0] != 'Y' && inbuff[0] != 'y') + return (0); + + if (!pc_format_media(drname, &geometry)) + { + printf("Media format failed\n"); + return(0); + } + if (!pc_format_volume(drname, &geometry)) + { + printf("Format volume failed\n"); + return(0); + } + + pc_set_default_drive(drname); + + printf("Creating subdirectories\n"); + if (make_directories(imagepath, 0) < 0) + { + printf("Can not create subdirectories \n"); + return(0); + } + + printf("Copying files\n"); + if (copy_files(imagepath, 0) < 0) + { + printf("Can not copy files\n"); + printf("Try adding extra blocks to the comand line\n"); + printf("Increment extra blocks untill it works\n"); + printf("For example: mkrom \\romdata 4\n"); + return(0); + } + printf("Host disk at %s is populated from native subdirectory %s\n", drname, imagepath); + return(0); +} + +/* Calculate number of clusters needed given cluster size */ +long calc_n_clusters(long clsize) +{ +long n_cl = 0; +int i; + i = (int)clsize; + n_cl += file_cluster_count[i]; + n_cl += subdir_cluster_count[i]; + return(n_cl); +} + +long calc_best_cluster_size(void) +{ +long min_clusters; +long best_size; +long try_size; +long l; + + best_size = 1; + min_clusters = calc_n_clusters(best_size); + for (try_size = 2; try_size <= 32; try_size *= 2) + { + l = calc_n_clusters(try_size); + /* Take the minimum but limit it so we don't generate huge cluster + sizes on small volumes. We need at least a block anyway */ + if (l < min_clusters && min_clusters > 256) + { + min_clusters = l; + best_size = try_size; + } + } + return(best_size); +} + +void calculate_hcn(long n_blocks, PDEV_GEOMETRY pgeometry) +{ +long cylinders; /*- Must be < 1024 */ +long heads; /*- Must be < 256 */ +long secptrack; /*- Must be < 64 */ +long residual_h; +long residual_s; + + secptrack = 1; + while (n_blocks/secptrack > (1023L*255L)) + secptrack += 1; + residual_h = (n_blocks+secptrack-1)/secptrack; + heads = 1; + while (residual_h/heads > 1023L) + heads += 1; + residual_s = (residual_h+heads-1)/heads; + cylinders = residual_s; + pgeometry->dev_geometry_cylinders = (dword) cylinders; + pgeometry->dev_geometry_heads = (int) heads; + pgeometry->dev_geometry_secptrack = (int) secptrack; + printf("n_blocks == %ld hcn == %ld\n", n_blocks, (cylinders*heads*secptrack) ); +} + +BOOLEAN calculate_format_parms(DDRIVE *pdr, PDEV_GEOMETRY pgeometry) +{ +long sec_p_alloc; +long n_clusters; +long nibs_p_cluster; +long nibs_p_fat; +long blocks_p_fat; +long blocks_p_root; +long blocks_p_heap; +long blocks_p_volume; + + rtfs_memset(pgeometry, 0, sizeof(*pgeometry)); + + sec_p_alloc = calc_best_cluster_size(); + n_clusters = calc_n_clusters(sec_p_alloc); + n_clusters += CLUSTER_FUDGE; + blocks_p_heap = n_clusters * sec_p_alloc; + + if (n_clusters > 0xffffL) + { + nibs_p_cluster = 8; + printf("Fat32 not supported for this function\n"); + return(FALSE); + } + else if (n_clusters < 4087) + nibs_p_cluster = 3; + else + nibs_p_cluster = 4; + + nibs_p_fat = nibs_p_cluster * (n_clusters+2); /* Plus 2 for two reserved entries */ + blocks_p_fat = (nibs_p_fat+1023)/1024; + blocks_p_root = (n_root_entries + 15)/16; + n_root_entries = blocks_p_root*16; + + blocks_p_volume = FIXED_BLOCKS + blocks_p_heap + blocks_p_root + (2*blocks_p_fat); + required_volume_size = blocks_p_volume; + + calculate_hcn(blocks_p_volume, pgeometry); + + rtfs_strcpy(&pgeometry->fmt.oemname[0], + rtfs_strtab_user_string(USTRING_SYS_OEMNAME) ); + pgeometry->fmt.physical_drive_no = (byte) pdr->driveno; + pgeometry->fmt.binary_volume_label = BIN_VOL_LABEL; + rtfs_strcpy(pgeometry->fmt.text_volume_label, + rtfs_strtab_user_string(USTRING_SYS_VOLUME_LABEL) ); + pgeometry->fmt.secpalloc = (byte) sec_p_alloc; + pgeometry->fmt.numfats = (byte) 2; + pgeometry->fmt.secptrk = (word) pgeometry->dev_geometry_secptrack; + pgeometry->fmt.numhead = (word) pgeometry->dev_geometry_heads; + pgeometry->fmt.numcyl = (word) pgeometry->dev_geometry_cylinders; + pgeometry->fmt.mediadesc = (byte) 0xF8; + pgeometry->fmt.secreserved = (word) 1; + pgeometry->fmt.numhide = 0; + pgeometry->fmt.secpfat = (word) blocks_p_fat; + pgeometry->fmt.numroot = (word) n_root_entries; + pgeometry->fmt.mediadesc = (byte) 0xF8; + pgeometry->fmt_parms_valid = TRUE; + return(TRUE); +} + + +int traverse(byte *name, int level) +{ +statobj_t statobj; +byte path[MKROM_MAX_PATH_LEN]; +byte *pkind; +long n_blocks; +long n_clusters; +int i; +int entries; +long dd; + + for (i = 0; i < level; i++) + printf(" "); + printf("Processing directory %s\n", name); + strcpy((char *)path, (char *)name); + strcat((char *)path, "\\*.*"); + + /* Print them all */ + if ((dd = _findfirst((char *)path, &statobj)) < 0) + { + return(0); + } + else + { + entries = 0; + do + { + entries++; +#if (VFAT) /* Small piece of compile time VFAT vs's NONVFAT code */ + entries += (rtfs_strlen((byte *)statobj.name) + 12)/13; /* Account for lfn data */ +#endif + if ( (strcmp(".", statobj.name)==0) || + (strcmp("..", statobj.name)==0) ) + pkind = (byte *)"d"; + else if( statobj.attrib & _A_SUBDIR ) + { + n_subdirectories += 1; + pkind = (byte *)"d"; + } + else + { + n_files += 1; + n_blocks = (statobj.size+511)/512; + for (i = 1; i <= 32; i *= 2) + { + n_clusters = (n_blocks+i-1)/i; + file_cluster_count[i] += n_clusters; + } + pkind = (byte *)"-"; + } + /* matches unix output. */ + for (i = 0; i < level; i++) + printf(" "); + printf("%s %8ld %-12s\r", pkind, statobj.size,statobj.name); + } while (_findnext(dd, &statobj) == 0); + _findclose(dd); + } + + /* Track root entries and blocks used by dir ents */ + if (level == 0) + n_root_entries += entries; + else + { + n_sub_entries += entries; + n_blocks = (entries + 15)/16; /* Blocks occupied */ + for (i = 1; i <= 32; i *= 2) + { + n_clusters = (n_blocks+i-1)/i; + subdir_cluster_count[i] += n_clusters; + } + } + /* Now traverse */ + if ((dd=_findfirst((char *)path, &statobj))<0) + { + return(0); + } + else + { + do + { + if( statobj.attrib & _A_SUBDIR ) + { + if ( (strcmp(".", statobj.name)!=0) && + (strcmp("..", statobj.name)!=0) ) + { + strcpy((char *)path, (char *)name); + strcat((char *)path, "\\"); + strcat((char *)path, statobj.name); + traverse(path, level+1); + strcpy((char *)path, (char *)name); + strcat((char *)path, "\\*.*"); + } + } + } while (_findnext(dd, &statobj) == 0); + _findclose(dd); + } + return(1); +} + + +int make_directories(byte *name, int level) +{ +statobj_t statobj; +byte path[MKROM_MAX_PATH_LEN]; +byte *p; +long dd; +#if (INCLUDE_CS_UNICODE || !VFAT) +byte newpath[MKROM_MAX_PATH_LEN]; +#endif + + if (level) + { + p = name + root_path_len; +#if (!VFAT) /* Small piece of compile time VFAT vs's NONVFAT code */ + /* USE upper case for 8.3 */ + pc_str2upper(newpath, p); + p = newpath; +#endif + printf("Creating directory %s\n", p); +#if (INCLUDE_CS_UNICODE) + map_ascii_to_unicode(newpath, p); + if (!pc_mkdir(newpath) ) +#else + if (!pc_mkdir(p) ) +#endif + { + printf("Failed creating directory %s\n", p); + return(-1); + } + } + + strcpy((char *)path, (char *)name); + strcat((char *)path, (char *)"\\*.*"); + + if ((dd = _findfirst((char *)path, &statobj))<0) + { + return(0); + } + else + { + do + { + if( statobj.attrib & _A_SUBDIR ) + { + if ( (strcmp(".", statobj.name)!=0) && + (strcmp("..", statobj.name)!=0) ) + { + strcpy((char *)path, (char *)name); + strcat((char *)path, (char *)"\\"); + strcat((char *)path, (char *)statobj.name); + if (make_directories(path, level+1) != 0) + return(-1); + strcpy((char *)path, (char *)name); + strcat((char *)path, (char *)"\\*.*"); + } + } + } while (_findnext(dd, &statobj) == 0); + _findclose(dd); + } + return(0); +} + +/* Copy Native file *from to dos path *to */ +BOOLEAN copy_one_file(byte *path, byte *filename) /* __fn__ */ +{ +static byte dos_path[MKROM_MAX_PATH_LEN]; +static byte buf[1024]; +PCFD fd; +int res,res2; +int fi; +BOOLEAN retval = TRUE; +byte *to; +#if (INCLUDE_CS_UNICODE || !VFAT) +byte newpath[MKROM_MAX_PATH_LEN]; +#endif + + + strcpy((char *)dos_path, (char *)path); + strcat((char *)dos_path, (char *)"\\"); + strcat((char *)dos_path, (char *)filename); + + /* Strip the leading path when we go to rtfs */ + to = &dos_path[root_path_len]; +#if (!VFAT)/* Small piece of compile time VFAT vs's NONVFAT code */ +/* USE upper case for 8.3 */ + pc_str2upper(newpath, to); + to = newpath; +#endif + printf("Copying %s to %s\n", dos_path, to); + if ( (fi = open((char *)dos_path,O_RDONLY|O_BINARY)) < 0) /* Open binary read */ + { + printf("Cant open %s\n",dos_path); + return (FALSE); + } + else + { + /* Open the PC disk file for write */ +#if (INCLUDE_CS_UNICODE) + map_ascii_to_unicode(newpath, to); + if ((fd = po_open(newpath,(PO_BINARY|PO_RDWR|PO_CREAT|PO_TRUNC), + (PS_IWRITE | PS_IREAD) ) ) < 0) +#else + if ((fd = po_open(to,(PO_BINARY|PO_RDWR|PO_CREAT|PO_TRUNC), + (PS_IWRITE | PS_IREAD) ) ) < 0) +#endif + { + printf("Cant open %s error = %i\n",to, -1); + return (FALSE); + } + else + { + /* Read from host until EOF */ + while ( (res = read(fi,&buf[0],1024)) > 0) + { + /* Put to the drive */ + if ( (res2 = (int)po_write(fd,buf,(word)res)) != res) + { + printf("Cant write %x %x \n",res,res2); + retval = FALSE; + break; + } + } + /* Close the pc file flush buffers & and de-allocate structure */ + po_close(fd); + + /* close the native file */ + /* PORT-ME */ + close(fi); + + return (retval); + } + } +} + + +int copy_files(byte *name, int level) +{ +statobj_t statobj; +byte path[MKROM_MAX_PATH_LEN]; +long dd; + + printf("Processing directory %s\n", name); + strcpy((char *)path, (char *)name); + strcat((char *)path, (char *)"\\*.*"); + + if ((dd = _findfirst((char *)path, &statobj))>=0) + { + do + { + if(!(statobj.attrib & (_A_SUBDIR) )) + { + if (!copy_one_file(name, (byte *)statobj.name)) + return(-1); + } + } while (_findnext(dd, &statobj) == 0); + _findclose(dd); + } + + /* Now traverse */ + if ((dd = _findfirst((char *)path, &statobj)) < 0) + { + return(0); + } + else + { + do + { + if( statobj.attrib & _A_SUBDIR ) + { + if ( (strcmp(".", statobj.name)!=0) && + (strcmp("..", statobj.name)!=0) ) + { + strcpy((char *)path, (char *)name); + strcat((char *)path, (char *)"\\"); + strcat((char *)path, statobj.name); + if (copy_files((byte *)path, level+1) < 0) + return(-1); + strcpy((char *)path, (char *)name); + strcat((char *)path, (char *)"\\*.*"); + } + } + } while (_findnext(dd, &statobj) == 0); + _findclose(dd); + } + return(0); +} + +int domkrom(int agc, byte **agv) /*__fn__*/ +{ +FILE *infile, *outc; +int c, linecount=0; +long count=0; + + printf("\n MKROM - Creating drromdsk.h from hostdisk.dat\n"); + + RTFS_ARGSUSED_INT(agc); + RTFS_ARGSUSED_PVOID((void *) agv); + infile = fopen("hostdisk.dat", "rb"); + if (infile == NULL) + { + printf("MKROM could not open hostdisk.dat\n"); + return(0); + } + + outc = fopen("drromdsk.h", "wt"); + if (outc == NULL) + { + printf("MKROM could not open drromdsk.h\n"); + fclose(infile); + return(0); + } + + fprintf(outc, "/* %s */\n", "drromdsk.h"); + fprintf(outc, "/* Automatically Created from %s using MKROM */\n", "hostdisk.dat"); + fprintf(outc, "\nbyte KS_FAR %s[]=\n{\n", "romdisk_data"); + + /* write the C file */ + while ((c=fgetc(infile)) != EOF) + { + if (linecount < 16) + { + fprintf(outc, " 0x%02x, ",c); + linecount++; + } + else + { + fprintf(outc, " 0x%02x,\n", c); + linecount=0; + } + + count++; + } + + fprintf(outc, "};\n"); + + fclose(infile); + fclose(outc); + return(0); +} + +#endif /* (INCLUDE_HOSTDISK) */ + diff --git a/build/libraries/fatfs/ARM7/winsplsh.c b/build/libraries/fatfs/ARM7/winsplsh.c new file mode 100644 index 0000000..06463d2 --- /dev/null +++ b/build/libraries/fatfs/ARM7/winsplsh.c @@ -0,0 +1,378 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1996 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +*/ +/* WINSPLSH.C - Introductory text and display engine for windows based demo */ + +#include +#include /* For included devices */ + +#if (INCLUDE_HOSTDISK) + +#include +#include + +void display_introduction(void); + +void display_text(char * readme[]) +{ +int nlines; +int current_line; +int break_line; +int i,j; +byte buf[32]; +#define LINES_PER_PAGE 20 + + /* Count number of lines */ + nlines = 0; + while (readme[nlines]) + nlines++; + + current_line = 0; + for (;;) + { + j = current_line; + break_line = 0; + for (i = 0; i < LINES_PER_PAGE; i++) + { + if (readme[j]) + { + if (*readme[j] == '.')/* CDFS */ + break_line = j; + if (break_line) + printf("\n"); + else + printf("%s\n", readme[j++]); + } + else + printf("\n"); + } + printf(" \n"); + /* " for next, (P) for Previous, (Q) to run demo" */ + rtfs_print_prompt_user(UPROMPT_WINSPLSH, buf); + if (CS_OP_CMP_ASCII(buf,'Q') || CS_OP_CMP_ASCII(buf,'q')) + break; + if (CS_OP_CMP_ASCII(buf,'P') || CS_OP_CMP_ASCII(buf,'p')) + { + current_line -= LINES_PER_PAGE; + if (current_line < 0) + current_line = 0; + } + else + { + if (break_line) + current_line = break_line + 1; + else + { + if ((current_line + LINES_PER_PAGE) <= nlines) + current_line += LINES_PER_PAGE; + } + } + } +} + + +char * rtfsdemo_introduction[] = { +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ==================================================", +" = Thank you for your interest in ERTFS. =", +" = =", +" = Please Page through the following introduction =", +" = At any time press Q to run the Demo. =", +" = == =", +" = The DEMO is safe to execute on your workstation=", +" = it uses only ram disks, rom disks and a simple =", +" = user file. It does NOT bypass windows to access=", +" = your hard disk directly =", +" ==================================================", +" ", +" ", +" ", +" ", +" ERTFS is a DOS and WINDOWS compatible file system library for ", +" embedded systems. ERTFS supports all versions of DOS, 12, 16 ", +" and 32 bit File Allocation Tables. ERFTS even supports WIN95 long ", +" file names, and large FAT32 partions of 80 gigabytes and more. ", +" ", +" ", +" ERTFS has provided high performance reliable solutions for demanding ", +" embedded Applications since 1988. It is used in hundreds of applications", +" including medical devices, avionics, military, set-tops, digital cameras", +" scientific instruments and many other devices. ", +" ", +" ERTFS is portable, easy to use and has a familiar file system interface ", +" but it is also a high performance disk subsystem that has been used in ", +" many demanding and sophisticated applications including demanding video ", +" audio and scientific applications.", +" ", +/* ". page break", */ +" Advanced Features of ERTFS", +" ", +" . Callable check disk . Callable format and fdisk functions", +" . Contiguous Files . Automounts removable media ", +" . Direct device driver access . Query file extents", +" . User control over block placement. Mountable device drivers", +" . Extensive device support . Multiple partition support", +" . Supports very large volumes . CD ISO 9660 supported (optional)", +" . Includes PCMCIA Support . Clean RTOS and CPU porting layer", +" . User placement of file extents . Complete access to free space ", +" . Full Posix style API", +" ", +" Devices Supported by ERTFS", +" ", +" . ATA hard drives. Drives as large as 80 GigaBytes have been tested", +" . Linear flash. FTL is provided with ram emulation of flash", +" . PCMCIA Ram cards . RAM disk . Floppy disk . Smart Media ", +" . PCMCIA ATA disks and compact flash", +" . ROM disk, includes tool to create a rom disk image from MS-Windows ", +" . Table driven device interface supports user supplied device drivers,", +" user devices may be removable and may have partitions. ERTFS handles", +" all devices the same way.", +" ", +" ", +/* ". page break", */ +" API Calls provided with ERTFS", +" ", +" -- Initilaization --", +" pc_ertfs_init() - mount devices from device driver table. ", +" ", +" -- File Operations --", +" po_open - Open a file.", +" po_read - Read bytes from a file.", +" po_write - Write Bytes to a file.", +" po_lseek - Move the file pointer.", +" po_close - Close a file and flush the file allocation table.", +" po_flush - Flush an open file", +" po_trunc - Truncate an open file", +" pc_mv - Rename a file.", +" pc_unlink - Delete a file.", +" po_chsize - Truncate or extend an open file.", +" pc_set_attributes - Set File Attributes ", +" pc_get_attributes - Get File Attributes ", +" pc_stat - Obtain statistics on a path.", +" pc_fstat - Obtain statistics on an open file", +" ", +" -- Directory Operations --", +" pc_mkdir - Create a directory.", +" pc_rmdir - Delete a directory.", +" pc_deltree - Delete an entire directory tree.", +" pc_gfirst - Get stats on the first file to match a pattern.", +" pc_gnext - Get stats on the next file to match a pattern.", +" pc_gdone - Free resources used by pc_gfirst/pc_gnext.", +" ", +" -- Miscelaneous Functions -- ", +" chkdsk - check file system integrity", +" pc_fat_size - Calculate blocks required for a volume's FAT", +" pc_free - Calculate and return the free space on a disk.", +" pc_set_default_drive - Set the default drive number.", +" pc_set_cwd - Set the current working directory.", +" pc_isdir - Determine if a path is a directory.", +" pc_isvol - Determine if a path is a volume", +" pc_pwd - Get string representation of current working dir.", +" pc_set_pwd - Set current working directory for this user", +" pc_setdfltfrvno - Set the current default drive for this user.", +" pc_enumerate - Select all directory entries that match rules and", +" call a user callback on each. (like the unix print", +" utility)", +" ", +" ", +" -- Advanced High performance Functions -- ", +" pc_cluster_size - Get the cluster size of a drive", +" pc_find_contig_clusters", +" - Find contiguous clusters in the freelist", +" pc_get_free_list - Get a list free cluster segments on the drive", +" pc_raw_write - Write blocks directly to a disk", +" pc_raw_read - Read blocks directly from a disk", +" pc_get_file_extents- Get the list of block segments that make up a file", +" po_extend_file - Extend a file with contiguous clusters. Also ", +" supports direct assignment blocks to a file", +" ", +" -- Formatting routines -- ", +" pc_get_media_parms - Get media parameters.", +" pc_format_media - Device level format", +" pc_partition_media - Write partition table", +" pc_format_volume - Format a volume", +" ", +/* ". page break", */ +" Sample utilities provided with ERTFS", +" ", +" Test Shell - An interactive test shell is provided. This test", +" shell is an excellent demontration of how to use the API as well ", +" as a useful tool itself. The console IO needs of the test shell are ", +" minimal and can be accomplished from a simple RS232 connection.", +" ", +" Regression Test - This program tests the ERTFS API and is a good test ", +" prgram for checking out ports of the library to new environments.", +" ", +" Check disk - This is a utility and callable function that performs", +" the check disk functionality.", +" ", +/* ". page break", */ +" This demo program", +" ", +" ", +" This demo program is built with the ERTFS library. It contains the ", +" interactive test shell, the regression test module, and the check", +" disk utility. ", +" The demo runs from inside a \"DOS Box\" and it is safe to run it on your ", +" workstation. All devices are simulated in ram or in simple disk files ", +" in the current directory.", +" ", +/* ". page break", */ +" ", +" When the program starts you will see the following diagnostics.", +" ", +"ERTFS Device List", +"=================", +"Drive: A Device: HOST DISK HOSTDISK.DAT:256K", +"Drive: B Device: STATIC RAM DISK", +"Drive: C Device: FLASH DISK", +"Drive: D Device: STATIC ROM DISK", +" ", +"Notes:", +"1. Host disk is a 256 K disk volume simulated in a disk file", +"2. Flash Disk is the FTL driver running on a flsh disk simulation in ram", +"3. The Static Rom disk was initialized with the MKROM tool.", +" ", +" = ", +" ", +/* ". page break", */ +" ", +"The program will then print:", +" ", +"Autoformatting RAM Devices", +"==========================", +"Autoformatting Drive: A Device: HOST DISK HOSTDISK.DAT:256K", +"Autoformatting Drive: B Device: STATIC RAM DISK", +"Autoformatting Drive: C Device: FLASH DISK", +" = ", +"The devices are autoformatted at initialization, an attribute in the ", +"device record tells ERTFS to format the device. Other attributes", +"flag the device as removable and partitioned.", +" ", +"Note that the ROM disk is not autoformatted at initialization time.", +" ", +" ", +/* ". page break", */ +" ", +"The program next enters the test shell.", +"The test shell is like the DOS or Unix command shell. ", +"The Static ROM disk D: is already populated", +"so running informations commands on D: is a the most interesting", +"until you use the shell to populate the other volumes.", +" ", +"The test shell commands are listed on the next page.", +" ", +/* ". page break", */ +" ", +"These commands are avaliable from the test shell: ", +"HELP", +"CAT PATH", +"CHSIZE FILENAME NEWSIZE", +"CD PATH or CD to display PWD ", +"CHKDSK D: <0,1> 1 is write lost chains", +"CLOSE FDNO", +"COPY PATH PATH", +"DELETE PATH", +"DELTREE PATH", +"DEVINFO (Display Device Information)", +"DIFF PATH PATH", +"DIR PATH", +"DSKSEL D:", +"ECHO: [args]", +"EJECT (ejects LS-120)", +"FILLFILE PATH PATTERN NTIMES", +"FORMAT (routine will prompt for arguments)", +/* ". page break", */ +"GETATTR FILE", +"HELP:", +"LSTOPEN (lists all open file decscriptors) ", +"MKDIR PATH", +"PCMCIAINT (Force a PCMCIA mgmt Interrupt)", +"QUIT", +"READ FDNO", +"RENAME PATH NEWNAME", +"RMDIR PATH", +"RNDOP PATH RECLEN", +"SEEK FDNO RECORD", +"SETATTR D:PATH RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL", +"STAT PATH", +"WRITE FDNO QUOTED DATA", +"REGRESSTEST D: (perform regression test)", +">: ", +" ", +" ", +" ", +" ", +" Notes about using the demo test shell. ", +" 1. Since the D: drive is populated running chkdsk on it is most", +" interesting. ", +" 2. To create a file on one of the other drives (A: to C:) ", +" use fillfile, rndop or copy files from the rom disk ", +" Example: ", +" >: FILLFILE mydatafile.dat \"This is my story\" 100 ", +" >: CAT mydatafile.dat ", +" Will produce a list of 100 lines of \"This is my story\"", +" Example: ", +" >: rndop myfile.dat 100", +" >: lstopen", +" 0: myfile.dat", +" >: dir *.*", +" myfile .dat 0 03-28-88 19:37 ", +" 1 File(s) 252 KBytes free", +" >: write 0 \"this is record zero\"", +" >: write 0 \"this is record one\"", +" >: write 0 \"this is record two\"", +" >: seek 0 1", +" >: read 0", +" this is record one :", +" >: close 0", +" >: dir *.*", +" myfile .dat 300 03-28-88 19:37 ", +" 1 File(s) 252 KBytes free", +" >: copy myfile.dat COPY.DAT", +" >: dir *.*", +" myfile .dat 300 03-28-88 19:37 ", +" COPY .DAT 300 03-28-88 19:37 ", +" 2 File(s) 251 KBytes free", +" >:", +" ", +" ", +"Thanks again for your interest in ERTFS.", +0, +}; + +void display_introduction(void) +{ + display_text(rtfsdemo_introduction); +} + +void winexit(void) +{ + printf("========================================\n"); + printf("========================================\n"); + printf("========================================\n"); + printf("Thanks for trying ERTFS. Please Call If \n"); + printf(" You Have Any Questions \n"); + printf("========================================\n"); + printf("========================================\n"); + printf("========================================\n"); + printf("========================================\n"); + exit(0); +} + + +#endif /* USEWIN32 - End readme display package */ + diff --git a/build/libraries/fatfs/Makefile b/build/libraries/fatfs/Makefile new file mode 100644 index 0000000..4f69ad1 --- /dev/null +++ b/build/libraries/fatfs/Makefile @@ -0,0 +1,34 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - libraries - spi +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Log: $ +# $NoKeywords: $ +#---------------------------------------------------------------------------- + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + + +#---------------------------------------------------------------------------- + +# SUBDIRS = ARM9 + +ifdef TWL_WITH_ARM7 +SUBDIRS += ARM7 +endif + +#---------------------------------------------------------------------------- + +include $(TWLSDK_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/include/twl/devices/sdmc/ARM7/sdmc.h b/include/twl/devices/sdmc/ARM7/sdmc.h new file mode 100644 index 0000000..a88b9b2 --- /dev/null +++ b/include/twl/devices/sdmc/ARM7/sdmc.h @@ -0,0 +1,108 @@ + +#ifndef __SDMC_H__ +#define __SDMC_H__ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************************************* + カードエラーコード(カードエラーステータス設定値)アプリケーション固有のSDCARD_ErrStatusに対して +*********************************************/ +typedef enum { + SDMC_NORMAL = 0, /* 正常終了 */ + SDMC_ERR_COMMAND = 0x0001, /* CMDエラー */ + SDMC_ERR_CRC = 0x0002, /* CRCエラー */ + SDMC_ERR_END = 0x0004, /* 実行エラー */ + SDMC_ERR_TIMEOUT = 0x0008, /* コマンドタイムアウト */ + SDMC_ERR_FIFO_OVF = 0x0010, /* FIFO オーバーフローエラー(INFO2のIllegal write access to buffer) */ + SDMC_ERR_FIFO_UDF = 0x0020, /* FIFO アンダーフローエラー(INFO2のIllegal read access to buffer) */ + SDMC_ERR_WP = 0x0040, /* WriteProtectによる書き込みエラー */ + SDMC_ERR_FPGA_TIMEOUT = 0x0100, /* FPGA アクセスタイムアウト */ + SDMC_ERR_PARAM = 0x0200, /* コマンドパラメータエラー */ + SDMC_ERR_R1_STATUS = 0x0800, /* Normal response command カードステータス エラー */ + SDMC_ERR_NUM_WR_SECTORS = 0x1000, /* 書き込み完了セクタ数 エラー */ + SDMC_ERR_RESET = 0x2000, /* 初期化カードリセットコマンド時1.5秒タイムアウトエラー */ + SDMC_ERR_ILA = 0x4000, /* イリーガルアクセスエラー */ + SDMC_ERR_INFO_DETECT = 0x8000 /* カード排出時判別エラービット(IO3) */ +}SDMC_ERR_CODE; + + +/********************************************* + SDドライバ処理結果通知情報構造体 +*********************************************/ +typedef struct { + u16 b_flags; /* 処理内容 */ + u16 result; /* 実行結果 */ + u32 resid; /* 読み(書き)サイズ */ +} SdmcResultInfo; + + +/********************************************* + SDスペック構造体 +*********************************************/ +typedef struct { + u32 csd_ver2_flag; //CSDフォーマットバージョン(SDHCのときは1) + u32 memory_capacity; //data areaのサイズ(512Byte単位) + u32 protected_capacity; //protected areaのサイズ(512Byte単位) + u32 card_capacity; //カード全体のサイズ(512Byte単位) + + u32 adjusted_memory_capacity; //memory_capacityをシリンダ(heads*secptrack)の倍数に調整したサイズ(cylinders*heads*secptrackになる) + + u16 heads; + u16 secptrack; + u16 cylinders; + u16 SC; //sectors per cluster + u16 BU; + u16 RDE; //number of root dir entries(512 fix) + u32 SS; //sector size(512 fix) + u32 RSC; //reserved sector count(1 fix) +// u32 TS; //total sectors + u16 FATBITS; //16 or 32 + u16 SF; //sectors per FAT + u32 SSA; //sectors in system area + u32 NOM; //sectors in master boot record +} SdmcSpec; + + +/********************************************* + RTFS用ドライバインタフェース +*********************************************/ +BOOL sdmcRtfsIo( int driveno, dword block, void* buffer, word count, BOOLEAN reading); +int sdmcRtfsCtrl( int driveno, int opcode, void* pargs); +BOOL sdmcRtfsAttach( int driveno); + +BOOL sdmcCheckMedia( void); + + +/********************************************* + 基本API +*********************************************/ +SDMC_ERR_CODE sdmcInit(void (*func1)(void),void (*func2)(void)); /* カードドライバ初期化 */ +SDMC_ERR_CODE sdmcReset( void); /* カードリセット */ + +SDMC_ERR_CODE sdmcGetStatus(u16 *status); /* カードドライバの現在の状態を取得する */ +u32 sdmcGetCardSize(void); /* カード全サイズの取得 */ + +/*SD I/FのFIFOを使ってリードする(高速)*/ +SDMC_ERR_CODE sdmcReadFifo(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info);/* テスト用カードリード */ +/*リードする*/ +SDMC_ERR_CODE sdmcRead(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info); /* テスト用カードリード */ + +/*SD I/FのFIFOを使ってライトする(高速)*/ +SDMC_ERR_CODE sdmcWriteFifo(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info);/* テスト用カードライト */ +/*ライトする*/ +SDMC_ERR_CODE sdmcWrite(void* buf,u32 bufsize,u32 offset,void(*func)(void),SdmcResultInfo *info); /* テスト用カードライト */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /*__SDMC_H__*/ diff --git a/include/twl/fatfs/ARM7/attach.h b/include/twl/fatfs/ARM7/attach.h new file mode 100644 index 0000000..5443ce9 --- /dev/null +++ b/include/twl/fatfs/ARM7/attach.h @@ -0,0 +1,25 @@ +/*---------------------------------------------------------------------------* + Project: CTR - for RTFS + File: attach.h + + 2006 Nintendo. + *---------------------------------------------------------------------------*/ + +#ifndef __ATTACH_H__ +#define __ATTACH_H__ + + +#include +#include + + + +/*---------------------------------------------------------------------------* + API + *---------------------------------------------------------------------------*/ +BOOLEAN rtfs_attach( int driveno, DDRIVE* pdr, char* dev_name); +BOOLEAN rtfs_detach( int driveno); + + + +#endif /*__ATTACH_H__*/ diff --git a/include/twl/fatfs/ARM7/csstrtab.h b/include/twl/fatfs/ARM7/csstrtab.h new file mode 100644 index 0000000..10ca1b4 --- /dev/null +++ b/include/twl/fatfs/ARM7/csstrtab.h @@ -0,0 +1,446 @@ +byte *rtfs_strtab_user_prompt(int prompt_id); +byte *rtfs_strtab_user_string(int string_id); + +#define USTRING_SYS_NULL 100 +#define USTRING_SYS_BADALIAS 101 +#define USTRING_SYS_BADLFN 102 +#define USTRING_SYS_UCRESERVED_NAMES 103 +#define USTRING_SYS_LCRESERVED_NAMES 104 +#define USTRING_SYS_VOLUME_LABEL 105 +#define USTRING_SYS_OEMNAME 106 +#define USTRING_SYS_TAB 107 +#define USTRING_SYS_FSFILENAME 108 +#define USTRING_CHKDSK_01 200 +#define USTRING_CHKDSK_02 201 +#define USTRING_CHKDSK_03 202 +#define USTRING_CHKDSK_04 203 +#define USTRING_CHKDSK_05 204 +#define USTRING_CHKDSK_06 205 +#define USTRING_CHKDSK_07 206 +#define USTRING_CHKDSK_08 207 +#define USTRING_CHKDSK_09 208 +#define USTRING_CHKDSK_10 209 +#define USTRING_CHKDSK_11 210 +#define USTRING_CHKDSK_12 211 +#define USTRING_CHKDSK_13 212 +#define USTRING_CHKDSK_14 213 +#define USTRING_CHKDSK_15 214 +#define USTRING_CHKDSK_16 215 +#define USTRING_CHKDSK_17 216 +#define USTRING_CHKDSK_18 217 +#define USTRING_CHKDSK_19 218 +#define USTRING_CHKDSK_20 219 +#define USTRING_CHKDSK_21 220 +#define USTRING_CHKDSK_22 221 +#define USTRING_CHKDSK_23 222 +#define USTRING_CHKDSK_24 223 +#define USTRING_CHKDSK_25 224 +#define USTRING_CHKDSK_26 225 +#define USTRING_CHKDSK_27 226 +#define USTRING_CHKDSK_28 227 +#define USTRING_CHKDSK_29 228 +#define USTRING_CHKDSK_30 229 +#define USTRING_CHKDSK_31 230 +#define USTRING_CHKDSK_32 231 +#define USTRING_CHKDSK_33 232 +#define USTRING_CHKDSK_34 233 +#define USTRING_CHKDSK_35 234 +#define USTRING_CHKDSK_36 235 +#define USTRING_CHKDSK_37 236 +#define USTRING_CHKDSK_38 237 +#define USTRING_CHKDSK_39 238 +#define USTRING_CHKDSK_40 239 +#define USTRING_CHKDSK_41 240 +#define USTRING_CHKDSK_42 241 +#define USTRING_CHKDSK_43 242 +#define USTRING_CHKDSK_44 243 +#define USTRING_CHKDSK_45 244 +#define USTRING_CHKDSK_46 245 +#define USTRING_CHKDSK_47 246 +#define USTRING_CHKDSK_48 247 +#define USTRING_CHKDSK_49 248 +#define USTRING_CHKDSK_50 249 +#define USTRING_CHKDSK_51 250 +#define USTRING_CHKDSK_52 251 +#define USTRING_CHKDSK_53 252 +#define USTRING_CHKDSK_54 253 +#define USTRING_CHKDSK_55 254 +#define USTRING_CHKDSK_56 255 +#define USTRING_CHKDSK_57 256 +#define USTRING_CHKDSK_58 257 +#define USTRING_CHKDSK_59 258 +#define USTRING_CHKDSK_60 259 +#define USTRING_CHKDSK_61 260 +#define USTRING_CHKDSK_62 261 +#define USTRING_CRITERR_01 400 +#define USTRING_CRITERR_02 401 +#define USTRING_CRITERR_03 402 +#define USTRING_CRITERR_04 403 +#define USTRING_CRITERR_05 404 +#define USTRING_CRITERR_06 405 +#define USTRING_CRITERR_07 406 +#define USTRING_CRITERR_08 407 +#define USTRING_CRITERR_09 408 +#define USTRING_CRITERR_10 409 +#define USTRING_FLDRVER_01 600 +#define USTRING_FLDRVER_02 601 +#define USTRING_FLDRVER_03 602 +#define USTRING_FLDRVER_04 603 +#define USTRING_FLDRVER_05 604 +#define USTRING_FLDRVER_06 605 +#define USTRING_FLDRVER_07 606 +#define USTRING_FLDRVER_08 607 +#define USTRING_FLDRVER_09 608 +#define USTRING_FLDRVER_10 609 +#define USTRING_FLDRVER_11 610 +#define USTRING_FLDRVER_12 611 +#define USTRING_FLDRVER_13 612 +#define USTRING_FLDRVER_14 613 +#define USTRING_FLDRVER_15 614 +#define USTRING_FLDRVER_16 615 +#define USTRING_FLDRVER_17 616 +#define USTRING_FLDRVER_18 617 +#define USTRING_FLDRVER_19 618 +#define USTRING_FLASHDRV_01 800 +#define USTRING_FLASHDRV_02 801 +#define USTRING_FLASHDRV_03 802 +#define USTRING_FLASHDRV_04 803 +#define USTRING_FLASHDRV_05 804 +#define USTRING_FLASHDRV_06 805 +#define USTRING_FLASHDRV_07 806 +#define USTRING_FLASHDRV_08 807 +#define USTRING_FLASHDRV_09 808 +#define USTRING_FLASHDRV_10 809 +#define USTRING_FLASHDRV_11 810 +#define USTRING_FLASHDRV_12 811 +#define USTRING_FLASHDRV_13 812 +#define USTRING_FLASHDRV_14 813 +#define USTRING_FLASHDRV_15 814 +#define USTRING_FLASHDRV_16 815 +#define USTRING_FLASHDRV_17 816 +#define USTRING_FLASHDRV_18 817 +#define USTRING_FLASHDRV_19 818 +#define USTRING_FLASHDRV_20 819 +#define USTRING_FLASHDRV_21 820 +#define USTRING_FLASHDRV_22 821 +#define USTRING_FLASHDRV_23 822 +#define USTRING_FLASHDRV_24 823 +#define USTRING_FLASHDRV_25 824 +#define USTRING_FLASHDRV_26 825 +#define USTRING_FLASHDRV_27 826 +#define USTRING_FLASHDRV_28 827 +#define USTRING_FLASHDRV_29 828 +#define USTRING_FLASHDRV_30 829 +#define USTRING_FLASHDSK_01 1000 +#define USTRING_FLASHDSK_02 1001 +#define USTRING_FLASHEMU_01 1200 +#define USTRING_FLASHEMU_02 1201 +#define USTRING_FLASHEMU_03 1202 +#define USTRING_FLASHEMU_04 1203 +#define USTRING_FLASHEMU_05 1204 +#define USTRING_FLASHEMU_06 1205 +#define USTRING_FLASHEMU_07 1206 +#define USTRING_RTFSINIT_01 1400 +#define USTRING_RTFSINIT_02 1401 +#define USTRING_RTFSINIT_03 1402 +#define USTRING_RTFSINIT_04 1403 +#define USTRING_RTFSINIT_05 1404 +#define USTRING_RTFSINIT_06 1405 +#define USTRING_RTFSINIT_07 1406 +#define USTRING_RTFSINIT_08 1407 +#define USTRING_RTFSINIT_09 1408 +#define USTRING_RTFSINIT_10 1409 +#define USTRING_RTFSINIT_11 1410 +#define USTRING_RTFSINIT_12 1411 +#define USTRING_RTFSINIT_13 1412 +#define USTRING_IDEDRV_01 1600 +#define USTRING_IDEDRV_02 1601 +#define USTRING_IDEDRV_03 1602 +#define USTRING_IDEDRV_04 1603 +#define USTRING_IDEDRV_05 1604 +#define USTRING_PORTMAIN_01 1800 +#define USTRING_PORTMAIN_02 1801 +#define USTRING_PORTKERN_01 2000 +#define USTRING_PORTKERN_02 2001 +#define USTRING_PORTKERN_03 2002 +#define USTRING_PORTKERN_04 2003 +#define USTRING_RTFSDEM_01 2200 +#define USTRING_RTFSDEM_02 2201 +#define USTRING_RTFSDEM_03 2202 +#define USTRING_RTFSDEM_04 2203 +#define USTRING_RTFSDEM_05 2204 +#define USTRING_RTFSDEM_06 2205 +#define USTRING_RTFSDEM_07 2206 +#define USTRING_RTFSDEM_08 2207 +#define USTRING_RTFSDEM_09 2208 +#define USTRING_RTFSDEM_10 2209 +#define USTRING_RTFSDEM_11 2210 +#define USTRING_RTFSDEM_12 2211 +#define USTRING_RTFSDEM_13 2212 +#define USTRING_RTFSDEM_14 2213 +#define USTRING_RTFSDEM_15 2214 +#define USTRING_RTFSDEM_16 2215 +#define USTRING_RTFSDEM_17 2216 +#define USTRING_TSTSH_01 2400 +#define USTRING_TSTSH_02 2401 +#define USTRING_TSTSH_03 2402 +#define USTRING_TSTSH_04 2403 +#define USTRING_TSTSH_05 2404 +#define USTRING_TSTSH_06 2405 +#define USTRING_TSTSH_07 2406 +#define USTRING_TSTSH_08 2407 +#define USTRING_TSTSH_09 2408 +#define USTRING_TSTSH_10 2409 +#define USTRING_TSTSH_11 2410 +#define USTRING_TSTSH_12 2411 +#define USTRING_TSTSH_13 2412 +#define USTRING_TSTSH_14 2413 +#define USTRING_TSTSH_15 2414 +#define USTRING_TSTSH_16 2415 +#define USTRING_TSTSH_17 2416 +#define USTRING_TSTSH_18 2417 +#define USTRING_TSTSH_19 2418 +#define USTRING_TSTSH_20 2419 +#define USTRING_TSTSH_21 2420 +#define USTRING_TSTSH_22 2421 +#define USTRING_TSTSH_23 2422 +#define USTRING_TSTSH_24 2423 +#define USTRING_TSTSH_25 2424 +#define USTRING_TSTSH_26 2425 +#define USTRING_TSTSH_27 2426 +#define USTRING_TSTSH_28 2427 +#define USTRING_TSTSH_29 2428 +#define USTRING_TSTSH_30 2429 +#define USTRING_TSTSH_31 2430 +#define USTRING_TSTSH_32 2431 +#define USTRING_TSTSH_33 2432 +#define USTRING_TSTSH_34 2433 +#define USTRING_TSTSH_35 2434 +#define USTRING_TSTSH_36 2435 +#define USTRING_TSTSH_37 2436 +#define USTRING_TSTSH_38 2437 +#define USTRING_TSTSH_39 2438 +#define USTRING_TSTSH_40 2439 +#define USTRING_TSTSH_41 2440 +#define USTRING_TSTSH_42 2441 +#define USTRING_TSTSH_43 2442 +#define USTRING_TSTSH_44 2443 +#define USTRING_TSTSH_45 2444 +#define USTRING_TSTSH_46 2445 +#define USTRING_TSTSH_47 2446 +#define USTRING_TSTSH_48 2447 +#define USTRING_TSTSH_49 2448 +#define USTRING_TSTSH_50 2449 +#define USTRING_TSTSH_51 2450 +#define USTRING_TSTSH_52 2451 +#define USTRING_TSTSH_53 2452 +#define USTRING_TSTSH_54 2453 +#define USTRING_TSTSH_55 2454 +#define USTRING_TSTSH_56 2455 +#define USTRING_TSTSH_57 2456 +#define USTRING_TSTSH_58 2457 +#define USTRING_TSTSH_59 2458 +#define USTRING_TSTSH_60 2459 +#define USTRING_TSTSH_61 2460 +#define USTRING_TSTSH_62 2461 +#define USTRING_TSTSH_63 2462 +#define USTRING_TSTSH_64 2463 +#define USTRING_TSTSH_65 2464 +#define USTRING_TSTSH_66 2465 +#define USTRING_TSTSH_67 2466 +#define USTRING_TSTSH_68 2467 +#define USTRING_TSTSH_69 2468 +#define USTRING_TSTSH_70 2469 +#define USTRING_TSTSH_71 2470 +#define USTRING_TSTSH_72 2471 +#define USTRING_TSTSH_73 2472 +#define USTRING_TSTSH_74 2473 +#define USTRING_TSTSH_75 2474 +#define USTRING_TSTSH_76 2475 +#define USTRING_TSTSH_77 2476 +#define USTRING_TSTSH_78 2477 +#define USTRING_TSTSH_79 2478 +#define USTRING_TSTSH_80 2479 +#define USTRING_TSTSH_81 2480 +#define USTRING_TSTSH_82 2481 +#define USTRING_TSTSH_83 2482 +#define USTRING_TSTSH_84 2483 +#define USTRING_TSTSH_85 2484 +#define USTRING_TSTSH_86 2485 +#define USTRING_TSTSH_87 2486 +#define USTRING_TSTSH_88 2487 +#define USTRING_TSTSH_89 2488 +#define USTRING_TSTSH_90 2489 +#define USTRING_TSTSH_91 2490 +#define USTRING_TSTSH_92 2491 +#define USTRING_TSTSH_93 2492 +#define USTRING_TSTSH_94 2493 +#define USTRING_TSTSH_95 2494 +#define USTRING_TSTSH_96 2495 +#define USTRING_TSTSH_97 2496 +#define USTRING_TSTSH_98 2497 +#define USTRING_TSTSH_99 2498 +#define USTRING_TSTSH_100 2499 +#define USTRING_TSTSH_101 2500 +#define USTRING_TSTSH_102 2501 +#define USTRING_TSTSH_103 2502 +#define USTRING_TSTSH_104 2503 +#define USTRING_TSTSH_105 2504 +#define USTRING_TSTSH_106 2505 +#define USTRING_TSTSH_107 2506 +#define USTRING_TSTSH_108 2507 +#define USTRING_TSTSH_109 2508 +#define USTRING_TSTSH_110 2509 +#define USTRING_TSTSH_111 2510 +#define USTRING_TSTSH_112 2511 +#define USTRING_TSTSH_113 2512 +#define USTRING_TSTSH_114 2513 +#define USTRING_TSTSH_115 2514 +#define USTRING_TSTSH_116 2515 +#define USTRING_TSTSH_117 2516 +#define USTRING_TSTSH_118 2517 +#define USTRING_TSTSH_119 2518 +#define USTRING_TSTSH_120 2519 +#define USTRING_TSTSH_121 2520 +#define USTRING_TSTSH_122 2521 +#define USTRING_TSTSH_123 2522 +#define USTRING_TSTSH_124 2523 +#define USTRING_TSTSH_125 2524 +#define USTRING_TSTSH_126 2525 +#define USTRING_TSTSH_127 2526 +#define USTRING_TSTSH_128 2527 +#define USTRING_TSTSH_129 2528 +#define USTRING_TSTSH_130 2529 +#define USTRING_TSTSH_131 2530 +#define USTRING_TSTSH_132 2531 +#define USTRING_TSTSH_133 2532 +#define USTRING_TSTSH_134 2533 +#define USTRING_TSTSH_135 2534 +#define USTRING_TSTSH_136 2535 +#define USTRING_TSTSH_137 2536 +#define USTRING_TSTSH_138 2537 +#define USTRING_TSTSH_139 2538 +#define USTRING_TSTSH_140 2539 +#define USTRING_TSTSH_141 2540 +#define USTRING_TSTSH_142 2541 +/* #define USTRING_TSTSH_143 2542 - Obsolete */ +#define USTRING_TSTSH_144 2543 +#define USTRING_TSTSH_145 2544 +#define USTRING_TSTSH_146 2545 +#define USTRING_TSTSH_147 2546 +#define USTRING_TSTSH_148 2547 +#define USTRING_TSTSHHELP_01 2600 +#define USTRING_TSTSHHELP_02 2601 +#define USTRING_TSTSHHELP_03 2602 +#define USTRING_TSTSHHELP_04 2603 +#define USTRING_TSTSHHELP_05 2604 +#define USTRING_TSTSHHELP_06 2605 +#define USTRING_TSTSHHELP_07 2606 +#define USTRING_TSTSHHELP_08 2607 +#define USTRING_TSTSHHELP_09 2608 +#define USTRING_TSTSHHELP_10 2609 +#define USTRING_TSTSHHELP_11 2610 +#define USTRING_TSTSHHELP_12 2611 +#define USTRING_TSTSHHELP_13 2612 +#define USTRING_TSTSHHELP_14 2613 +#define USTRING_TSTSHHELP_15 2614 +#define USTRING_TSTSHHELP_16 2615 +#define USTRING_TSTSHHELP_17 2616 +#define USTRING_TSTSHHELP_18 2617 +#define USTRING_TSTSHHELP_19 2618 +#define USTRING_TSTSHHELP_20 2619 +#define USTRING_TSTSHHELP_21 2620 +#define USTRING_TSTSHHELP_22 2621 +#define USTRING_TSTSHHELP_23 2622 +#define USTRING_TSTSHHELP_24 2623 +#define USTRING_TSTSHHELP_25 2624 +#define USTRING_TSTSHHELP_26 2625 +#define USTRING_TSTSHHELP_27 2626 +#define USTRING_TSTSHHELP_28 2627 +#define USTRING_TSTSHHELP_29 2628 +#define USTRING_TSTSHHELP_30 2629 +#define USTRING_TSTSHHELP_31 2630 +#define USTRING_TSTSHHELP_32 2631 +#define USTRING_TSTSHHELP_33 2632 +#define USTRING_TSTSHHELP_34 2633 +#define USTRING_TSTSHHELP_35 2634 +#define USTRING_TSTSHHELP_36 2635 +#define USTRING_TSTSHHELP_37 2636 +#define USTRING_TSTSHHELP_38 2637 +#define USTRING_TSTSHHELP_39 2638 +#define USTRING_TSTSHHELP_40 2639 +#define USTRING_TSTSHHELP_41 2640 +#define USTRING_TSTSHHELP_42 2641 +#define USTRING_TSTSHHELP_43 2642 +#define USTRING_TSTSHHELP_44 2643 +#define USTRING_TSTSHHELP_45 2644 +#define USTRING_TSTSHHELP_46 2645 +#define USTRING_TSTSHCMD_01 2800 +#define USTRING_TSTSHCMD_02 2801 +#define USTRING_TSTSHCMD_03 2802 +#define USTRING_TSTSHCMD_04 2803 +#define USTRING_TSTSHCMD_05 2804 +#define USTRING_TSTSHCMD_06 2805 +#define USTRING_TSTSHCMD_07 2806 +#define USTRING_TSTSHCMD_08 2807 +#define USTRING_TSTSHCMD_09 2808 +#define USTRING_TSTSHCMD_10 2809 +#define USTRING_TSTSHCMD_11 2810 +#define USTRING_TSTSHCMD_12 2811 +#define USTRING_TSTSHCMD_13 2812 +#define USTRING_TSTSHCMD_14 2813 +#define USTRING_TSTSHCMD_15 2814 +#define USTRING_TSTSHCMD_16 2815 +#define USTRING_TSTSHCMD_17 2816 +#define USTRING_TSTSHCMD_18 2817 +#define USTRING_TSTSHCMD_19 2818 +#define USTRING_TSTSHCMD_20 2819 +#define USTRING_TSTSHCMD_21 2820 +#define USTRING_TSTSHCMD_22 2821 +#define USTRING_TSTSHCMD_23 2822 +#define USTRING_TSTSHCMD_24 2823 +#define USTRING_TSTSHCMD_25 2824 +#define USTRING_TSTSHCMD_26 2825 +#define USTRING_TSTSHCMD_27 2826 +#define USTRING_TSTSHCMD_28 2827 +#define USTRING_TSTSHCMD_29 2828 +#define USTRING_TSTSHCMD_30 2829 +#define USTRING_TSTSHCMD_31 2830 +#define USTRING_TSTSHCMD_32 2831 +#define USTRING_TSTSHCMD_33 2832 +#define USTRING_TSTSHCMD_34 2833 +#define USTRING_TSTSHCMD_35 2834 +#define USTRING_TSTSHCMD_36 2835 +#define USTRING_TSTSHCMD_37 2836 +#define USTRING_TSTSHCMD_38 2837 +#define USTRING_TSTSHCMD_39 2838 +#define USTRING_TSTSHCMD_40 2839 +#define USTRING_TSTSHCMD_41 2840 +#define USTRING_TSTSHCMD_42 2841 +#define USTRING_TSTSHCMD_43 2842 +#define USTRING_TSTSHCMD_44 2843 +#define USTRING_TSTSHCMD_45 2844 +#define USTRING_TSTSHCMD_46 2845 +#define UPROMPT_CRITERR 1 +#define UPROMPT_REGRESS 2 +#define UPROMPT_TSTSH1 5 +#define UPROMPT_TSTSH2 6 +#define UPROMPT_TSTSH3 7 +#define UPROMPT_TSTSH4 8 +#define UPROMPT_TSTSH5 9 +#define UPROMPT_TSTSH6 10 +#define UPROMPT_TSTSH7 11 +#define UPROMPT_TSTSH8 12 +#define UPROMPT_TSTSH9 13 +#define UPROMPT_TSTSH10 14 +#define UPROMPT_TSTSH11 15 +#define UPROMPT_TSTSH12 16 +#define UPROMPT_TSTSH13 17 +#define UPROMPT_TSTSH14 18 +#define UPROMPT_TSTSH15 19 +#define UPROMPT_TSTSH16 20 +#define UPROMPT_TSTSH17 21 +#define UPROMPT_TSTSH18 22 +#define UPROMPT_WINSPLSH 23 + + diff --git a/include/twl/fatfs/ARM7/drfile.h b/include/twl/fatfs/ARM7/drfile.h new file mode 100644 index 0000000..b0a7493 --- /dev/null +++ b/include/twl/fatfs/ARM7/drfile.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------------------------* + Project: CTR - for RTFS + File: drfile.h + + 2006 Nintendo. + *---------------------------------------------------------------------------*/ + +#ifndef __DR_FILE_H__ +#define __DR_FILE_H__ + + +#include +#include + + +/*---------------------------------------------------------------------------* + 構造体(未使用) + *---------------------------------------------------------------------------*/ +/*RTFS用 FATパラメータ*/ +typedef struct { + u32 capacity; //data areaのサイズ(512Byte単位) + + u32 adjusted_capacity; //memory_capacityをシリンダ(heads*secptrack)の倍数に調整したサイズ(cylinders*heads*secptrackになる) + + u16 heads; + u16 secptrack; + u16 cylinders; +} FileSpec; + + + +/*---------------------------------------------------------------------------* + API + *---------------------------------------------------------------------------*/ +BOOL fileRtfsAttach( PCFD fileDesc, int driveno); + + + + +#endif /*__DR_FILE_H__*/ diff --git a/include/twl/fatfs/ARM7/portconf.h b/include/twl/fatfs/ARM7/portconf.h new file mode 100644 index 0000000..92a615e --- /dev/null +++ b/include/twl/fatfs/ARM7/portconf.h @@ -0,0 +1,50 @@ +/***************************************************************************** +*Filename: PORTCONF.H - RTFS porting layer tuning constants +* +* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc, 1993-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* Description: +* This file contains porting layer tuning constants for configuring RTFS. +* It is included by rtfsconf.h. +* +****************************************************************************/ + +#ifndef __PORTCONF__ +#define __PORTCONF__ 1 + +/* CPU Configuration section */ + +#define KS_LITTLE_ENDIAN 1 /* See porting reference guide for explanation */ +#define KS_LITTLE_ODD_PTR_OK 0 /* See porting reference guide for explanation */ +#define KS_CONSTANT const /* See porting reference guide for explanation */ +#define KS_FAR /* See porting reference guide for explanation */ + + +/* Compile time constants to control device inclusion + See the reference guide for an explanation +*/ + + +#define INCLUDE_SD 0 +#define INCLUDE_IDE 0 /* - Include the IDE driver */ +#define INCLUDE_PCMCIA 0 /* - Include the pcmcia driver */ +#define INCLUDE_PCMCIA_SRAM 0 /* - Include the pcmcia static ram card driver */ +#define INCLUDE_COMPACT_FLASH 0 /* - Support compact flash (requires IDE and PCMCIA) */ +#define INCLUDE_FLASH_FTL 0 /* - Include the linear flash driver */ +#define INCLUDE_ROMDISK 0 /* - Include the rom disk driver */ +#define INCLUDE_RAMDISK 0 /* - Include the rom disk driver */ +#define INCLUDE_MMCCARD 0 /* - Include the multi media flash card driver */ +#define INCLUDE_SMARTMEDIA 0 /* - Include the smart media flash card driver */ +#define INCLUDE_FLOPPY 0 /* - Include the floppy disk driver */ +#define INCLUDE_HOSTDISK 0 /* - Include the host disk disk simulator */ +#define INCLUDE_UDMA 0 /* - Include ultra dma support for the ide driver */ +#define INCLUDE_82365_PCMCTRL 0 /* - Include the 82365 pcmcia controller driver */ + + +#endif /* __PORTCONF__ */ diff --git a/include/twl/fatfs/ARM7/prfs.h b/include/twl/fatfs/ARM7/prfs.h new file mode 100644 index 0000000..bbf4f5f --- /dev/null +++ b/include/twl/fatfs/ARM7/prfs.h @@ -0,0 +1,190 @@ +/* +* EBS - RTFS (Real Time File Manager) +* +* Copyright EBS Inc. 1987-2003 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* This file is automatically included in rtfs.h if INCLUDE_FAILSAFE_CODE is enabled +* this file is note intended for inclusion by user code. +*/ +/* PRFS.H - ERTFS-PRO Directory FailSafe routines */ + +/* + CFG_VALIDATE_JOURNAL - Set this to one to force a a low level read of the + whole journal file before it is accessed. If any of the reads call Failsafe + tries to recover by re-writing the whole file with zeros, for some media + types this will correct a read error. When the overwrite is completed + the file is read again, if that succeeds we continue, otherwise the journal + file open fails. A disk mount that encounters this error will fail and + errno will be set to PEIOERRORREADJOURNAL */ + +#define CFG_VALIDATE_JOURNAL 0 + +/* + CFG_VALIDATE_BUFFER_SIZE - This is the size, in blocks of the special + buffer dedicated to performing the tasks described in CFG_VALIDATE_JOURNAL. + The largest usable value for CFG_VALIDATE_BUFFER_SIZE is 128. Larger + values will not be utilized but memory will be wasted. + The whole journal file is scanned during startup in disk reads of up to + CFG_VALIDATE_BUFFER_SIZE blocks. Larger values will require fewer reads + and improve perfromance over smaller values. + The buffer is declared as: + static byte validate_buffer[CFG_VALIDATE_BUFFER_SIZE*512]; + in prfsnvio.c. + If CFG_VALIDATE_JOURNAL is zero the value of CFG_VALIDATE_BUFFER_SIZE + is irrelevant because the buffer is not declared. +*/ + +#define CFG_VALIDATE_BUFFER_SIZE 64 + +/* + CFG_NUM_JOURNAL_BLOCKS - This is the default number of 512 byte blocks + to assign to the failsafe for journalling directory blocks. If + the user does not provide a non-zero value in the journal_size argument + to pro_failsafe_init() then the journal file will sized to be large + enough to hold the whole file allocation table plus CFG_NUM_JOURNAL_BLOCKS. + The default value is 128. This represents a very small percentage of all + but the smallest disks but it should be adequate for almost any imaginable + usage. It may be reduced to as little as 1 and FailSafe will still work + fine under most conditions. +*/ + +#define CFG_NUM_JOURNAL_BLOCKS 128 + +/* + CFG_NUM_INDEX_BUFFERS - This compile time constant determines number of + 512 byte buffers to reserve for buffering FailSafe index pages. It + effects the size of the failsafe context structure. It must be at + least one and unless ram resources are very precious it should be + set to at least two if possible. The default value is 4 which should + be large enough for almost any application. +*/ + +#define CFG_NUM_INDEX_BUFFERS 4 + +/* Fail configuration and context structure */ +typedef struct failsafecontext { +/* Configuration values to be set before calling pro_failsafe_init + and not modified thereafter */ +#define FS_MODE_AUTORESTORE 0x01 +#define FS_MODE_AUTORECOVER 0x02 +#define FS_MODE_AUTOCOMMIT 0x04 +/* Internal flags set by failsafe. Not user assignable */ +#define FS_MODE_JOURNALING 0x08 + int configuration_flags; +/* configuration_flags values + FS_MODE_AUTORESTORE- If removable media is reinserted and the + journal file indicates that restore is needed, automatically restore + and continue. If this flag is not set when this condition occurs + the mount of the volume fails and the errno is set to PEFSRESTORENEEDED, + the application must then handle this errno setting and call the + pro_failsafe_restore routine + FS_MODE_AUTORECOVER- If autorestore is required but the restore process + failed due to a bad journal file or IO error abort the restore but don't + report an error. Try to reinitialize the journal file and continue. + If a restore error did occur and FS_MODE_AUTORECOVER is not set the + mount will fail and errno will be set to PEFSRESTOREERROR + FS_MODE_AUTOCOMMIT- If AUTOCOMMIT is enabled the FailSafe commit operation + will be performed automatically be ERTFS at the completion of each API + call. With AUTOCOMMIT enabled the FailSafe operation is transparent to the + user and it is not necessary to call pro_failsafe_commit(). +*/ + dword user_journal_size; /* In blocks */ + +/* Internal elements, not intended to be accessed from user code */ + DDRIVE *pdrive; + dword journal_file_size; /* In blocks */ + dword num_index_blocks; /* Blocks reserved remap index */ + dword num_remap_blocks; /* Blocks reserved for block remaps */ + dword total_blocks_mapped; /* number of valid index entries */ + dword journal_checksum; /* Checksum of all index entries */ + dword known_free_clusters; /* number of valid index entries */ + dword nv_buffer_handle; + int blockmap_size; + struct fsblockmap *blockmap_freelist; + struct fsblockmap *sorted_blockmap; +#define FAILSAFE_HASHTABLE_SIZE 16 +#define FAILSAFE_HASHMASK 0xf + struct fsblockmap *blockmap_hash_tbl[FAILSAFE_HASHTABLE_SIZE]; + int next_index_buffer; + int index_offset[CFG_NUM_INDEX_BUFFERS]; + BOOLEAN index_dirty[CFG_NUM_INDEX_BUFFERS]; + dword index_buffer[CFG_NUM_INDEX_BUFFERS][128]; + byte scratch_block[512]; + /* Internal variable used to differentiate between IO + errors and other errors during journal file re-opens */ + int open_status; +} FAILSAFECONTEXT; + +typedef struct fsblockmap { + struct fsblockmap *pnext; + struct fsblockmap *pnext_by_block; + dword blockno; + dword replacement_block; + } FSBLOCKMAP; + +/* Return codes for pro_failsafe_restore() */ +#define FS_STATUS_OK 0 +#define FS_STATUS_NO_JOURNAL 1 +#define FS_STATUS_BAD_JOURNAL 2 +#define FS_STATUS_IO_ERROR 3 +#define FS_STATUS_BAD_CHECKSUM 4 +#define FS_STATUS_RESTORED 5 +#define FS_STATUS_MUST_RESTORE 6 +#define FS_STATUS_OUT_OF_DATE 7 +#define FS_STATUS_NO_INIT 8 + +/* Failsafe api prototypes see prfsapi.c */ +BOOLEAN pro_failsafe_init(byte *drivename, FAILSAFECONTEXT *pfscntxt); +BOOLEAN pro_failsafe_commit(byte *path); +int pro_failsafe_restore(byte *drive_name, FAILSAFECONTEXT *fscntxt, BOOLEAN dorestore,BOOLEAN doclear); +BOOLEAN pro_failsafe_auto_init(DDRIVE *pdrive); +BOOLEAN pro_failsafe_shutdown(byte *drive_name, BOOLEAN abort); + + +/* Routines called internally by ertfs. see prfscore.c */ +BOOLEAN pro_failsafe_dskopen(DDRIVE *pdrive); +BOOLEAN pro_failsafe_autorestore(DDRIVE *pdrive); +void fs_rewind_failsafe(FAILSAFECONTEXT *pfscntxt); + +/* The rest of this header file contains information that is internal to + failsafe */ +#define FS_JOURNAL_SIGNATURE_1 0x4641494C /* 'F''A''I''L' */ +#define FS_JOURNAL_SIGNATURE_2 0x53414645 /* 'S''A''F''E' */ +/* April-2004 version 2. - substantial rework */ +#define FS_JOURNAL_VERSION 0x00000002 +#define FS_STATUS_PROCESSING 0x01010101 +#define FS_STATUS_COMPLETE 0x11111111 + +/* Internal field offsets in failsafe header */ +#define INDEX_OFFSET_SIGNATURE_1 0 +#define INDEX_OFFSET_SIGNATURE_2 1 +#define INDEX_OFFSET_VERSION 2 +#define INDEX_OFFSET_INDEX_STATUS 3 +#define INDEX_OFFSET_TOTAL_BLOCKS_MAPPED 4 +#define INDEX_OFFSET_INDEX_CHECKSUM 5 +#define INDEX_OFFSET_FREE_CLUSTERS 6 +#define INDEX_OFFSET_NUMB_INDEX_BLOCKS 7 +#define INDEX_OFFSET_NUMB_REMAP_BLOCKS 8 +#define JOURNAL_HEADER_SIZE 9 /* First 9 dwords are status */ +#define JOURNAL_ENTRIES_P_BLOCK 128 +#define JOURNAL_ENTRIES_ZERO (JOURNAL_ENTRIES_P_BLOCK-JOURNAL_HEADER_SIZE) + +/* Prototypes of internal function implemented in prfscore.c */ +BOOLEAN pro_failsafe_init_internal(DDRIVE *pdrive, FAILSAFECONTEXT *pfscntxt); +int pro_failsafe_commit_internal(FAILSAFECONTEXT *pfscntxt); +int failsafe_restore_internal(FAILSAFECONTEXT *pfscntxt, BOOLEAN dorestore, BOOLEAN doclear); +BOOLEAN pro_failsafe_dskopen(DDRIVE *pdrive); +BOOLEAN pro_failsafe_autorestore(DDRIVE *pdrive); +BOOLEAN pro_failsafe_autocommit(DDRIVE *pdrive); +BOOLEAN pro_failsafe_journal_full(DDRIVE *pdrive); + +/* Prototypes of internal function implemented in prfsnvio.c */ +BOOLEAN failsafe_reopen_nv_buffer(FAILSAFECONTEXT *pfscntxt); +BOOLEAN failsafe_create_nv_buffer(FAILSAFECONTEXT *pfscntxt); +BOOLEAN failsafe_write_nv_buffer(FAILSAFECONTEXT *pfscntxt, dword block_no, byte *pblock); +BOOLEAN failsafe_read_nv_buffer(FAILSAFECONTEXT *pfscntxt, dword block_no, byte *pblock); + + diff --git a/include/twl/fatfs/ARM7/rtfs.h b/include/twl/fatfs/ARM7/rtfs.h new file mode 100644 index 0000000..b1fa3b0 --- /dev/null +++ b/include/twl/fatfs/ARM7/rtfs.h @@ -0,0 +1,1402 @@ +/***************************************************************************** +*Filename: RTFS.H - Defines & structures for RTFS ms-dos utilities +* +* +* EBS - RTFS (Real Time File Manager) +* +* Copyright Peter Van Oudenaren , 1993 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* +* +* Description: +* +* +* +* +****************************************************************************/ + +#ifndef __RTFS__ +#define __RTFS__ 1 + +/* Set this next line to 1 if not building RTFS */ +#define CDFS_ONLY 0 + +typedef unsigned long int BLOCKT; /* 32 BIT unsigned */ + +#include "rtfs_naming_convention.h" +#include "rtfsconf.h" +#include "csstrtab.h" + +/* Begin API Definition section - the following lines export the public + API and user required defines RTFS internal definitions begin + at the line #define BLOCKEQ0 0L +*/ + + +typedef int PCFD; /* file desc */ + +#define ARDONLY 0x1 /* MS-DOS File attributes */ +#define AHIDDEN 0x2 +#define ASYSTEM 0x4 +#define AVOLUME 0x8 +#define ADIRENT 0x10 +#define ARCHIVE 0x20 +#define ANORMAL 0x00 +#define CHICAGO_EXT 0x0f /* Chicago extended filename attribute */ + + +/* Date stamping buffer */ +typedef struct datestr { + word date; + word time; + } DATESTR; +/* Structure for use by pc_gfirst, pc_gnext */ +typedef struct dstat { + byte fname[10]; /* Null terminated file and extension only 9 bytes used */ + byte fext[4]; +#if (VFAT) + byte lfname[FILENAMESIZE_BYTES]; /* Long file name for vfat. */ +#else + byte lfname[14]; /* Long file name non-vfat. */ +#endif + byte filename[14]; /* Null terminated file.ext only 13 bytes used */ + byte fattribute; /* File attributes */ + word ftime; /* time & date lastmodified. See date */ + word fdate; /* and time handlers for getting info */ + dword fsize; /* File size */ + /* INTERNAL */ + int driveno; + int drive_opencounter; /* Value of drive structures opencounter */ +/* byte pname[10]; Pattern. */ + byte pname[FILENAMESIZE_BYTES]; + byte pext[4]; + byte path[EMAXPATH_BYTES]; + void *pobj; /* Info for getting at the inode */ + void *pmom; /* Info for getting at parent inode */ + } +DSTAT; + +/* Structure for use by pc_stat and pc_fstat */ +/* Portions of this structure and the macros were lifted from BSD sources. + See the RTFS ANSI library for BSD terms & conditions */ +typedef struct ertfs_stat +{ + int st_dev; /* (drive number, rtfs) */ + int st_ino; /* inode number (0) */ + dword st_mode; /* (see S_xxxx below) */ + int st_nlink; /* (always 1) */ + int st_rdev; /* (drive number, rtfs) */ + dword st_size; /* file size, in bytes */ + DATESTR st_atime; /* last access (all times are the same) */ + DATESTR st_mtime; /* last modification */ + DATESTR st_ctime; /* last file status change */ + long st_blksize; /* optimal blocksize for I/O (cluster size) */ + long st_blocks; /* blocks allocated for file */ + byte fattribute; /* File attributes - DOS attributes + (non standard but useful) */ +} ERTFS_STAT; + +/* File seg info structure. An array of these structures is passed + to pc_get_file_extents(). The extents of the file are returned + in this array */ +typedef struct fileseginfo { + long block; /* Block number of the current extent */ + long nblocks; /* Number of blocks in the extent */ + } FILESEGINFO; + +/* Free list info structure. An array of these structures is passed + to pc_get_free_list(). The list of free clusters is returned in + this array */ +typedef struct freelistinfo { + dword cluster; /* Cluster where the free region starts */ + long nclusters; /* Number of free clusters the free segment */ + } FREELISTINFO; + +typedef struct chkdisk_stats { + dword n_user_files; /* Total #user files found */ + dword n_hidden_files; /* Total #hidden files found */ + dword n_user_directories; /* Total #directories found */ + dword n_free_clusters; /* # free available clusters */ + dword n_bad_clusters; /* # clusters marked bad */ + dword n_file_clusters; /* Clusters in non hidden files */ + dword n_hidden_clusters; /* Clusters in hidden files */ + dword n_dir_clusters; /* Clusters in directories */ + dword n_crossed_points; /* Number of crossed chains. */ + dword n_lost_chains; /* # lost chains */ + dword n_lost_clusters; /* # lost clusters */ + dword n_bad_lfns; /* # corrupt/disjoint win95 lfn chains */ +} CHKDISK_STATS; + + +/* Values for the st_mode field */ +#define S_IFMT 0170000 /* type of file mask */ +#define S_IFCHR 0020000 /* character special (unused) */ +#define S_IFDIR 0040000 /* directory */ +#define S_IFBLK 0060000 /* block special (unused) */ +#define S_IFREG 0100000 /* regular */ +#define S_IWRITE 0000400 /* Write permitted */ +#define S_IREAD 0000200 /* Read permitted. (Always true anyway)*/ + +#define DEFFILEMODE (S_IREAD|S_IWRITE) +#define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */ +#define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */ +#define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */ +#define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */ +#define S_ISFIFO(m) ((m & 0170000) == 0010000) /* fifo */ + +/* Error codes */ + +#define PCERR_FAT_FLUSH 0 /*Cant flush FAT */ +#define PCERR_INITMEDI 1 /*Not a DOS disk:pc_dskinit */ +#define PCERR_INITDRNO 2 /*Invalid driveno to pc_dskinit */ +#define PCERR_INITCORE 3 /*Out of core:pc_dskinit */ +#define PCERR_INITDEV 4 /*Can't initialize device:pc_dskinit */ +#define PCERR_INITREAD 5 /*Can't read block 0:pc_dskinit */ +#define PCERR_BLOCKCLAIM 6 /*PANIC: Buffer Claim */ +#define PCERR_BLOCKLOCK 7 /*Warning: freeing a locked buffer */ +#define PCERR_REMINODE 8 /*Trying to remove inode with open > 1 */ +#define PCERR_FATREAD 9 /* "IO Error While Failed Reading FAT" */ +#define PCERR_DROBJALLOC 10 /* "Memory Failure: Out of DROBJ Structures" */ +#define PCERR_FINODEALLOC 11 /* "Memory Failure: Out of FINODE Structures" */ + + +/* File creation permissions for open */ +/* Note: OCTAL */ +#define PS_IWRITE 0000400 /* Write permitted */ +#define PS_IREAD 0000200 /* Read permitted. (Always true anyway)*/ + + +/* File access flags */ +#define PO_RDONLY 0x0000 /* Open for read only*/ +#define PO_WRONLY 0x0001 /* Open for write only*/ +#define PO_RDWR 0x0002 /* Read/write access allowed.*/ +#define PO_APPEND 0x0008 /* Seek to eof on each write*/ +#define PO_BUFFERED 0x0010 /* Non-Block alligned File IO is buffered */ +#define PO_AFLUSH 0x0020 /* Auto-Flush. File is flushed automatically + each time po_write changes the size */ +#define PO_CREAT 0x0100 /* Create the file if it does not exist.*/ +#define PO_TRUNC 0x0200 /* Truncate the file if it already exists*/ +#define PO_EXCL 0x0400 /* Fail if creating and already exists*/ +#define PO_TEXT 0x4000 /* Ignored*/ +#define PO_BINARY 0x8000 /* Ignored. All file access is binary*/ + +#define PO_NOSHAREANY 0x0004 /* Wants this open to fail if already open. + Other opens will fail while this open + is active */ +#define PO_NOSHAREWRITE 0x0800 /* Wants this opens to fail if already open + for write. Other open for write calls + will fail while this open is active. */ + +/* Errno values */ +#define PEACCES 1 /* deleting an in-use object or witing to read only object */ +#define PEBADF 2 /* Invalid file descriptor*/ +#define PEEXIST 3 /* Creating an object that already exists */ +#define PEINVAL 4 /* Invalid api argument */ +#define PEMFILE 5 /* Out of file descriptors */ +#define PENOENT 6 /* File or directory not found */ +#define PENOSPC 7 /* Out of space to perform the operation */ +#define PESHARE 8 /* Sharing violation in po_open */ +#define PEINVALIDPARMS 9 /* Missing or invalid parameters */ +#define PEINVALIDPATH 10/* Invalid path name used as an argument */ +#define PEINVALIDDRIVEID 11/* Invalid drive specified in an argument */ +#define PEIOERRORREAD 12/* Read error performing the API's function */ +#define PEIOERRORWRITE 13/* Write error performing the API's function */ +#define PECLOSED 14/* Invalid file descriptor because a + removal or media failure asynchronously + closed the volume. po_close must be + called for this file descriptor to + clear this error. */ +#define PETOOLARGE 15 /* Trying to extend a file beyond RTFS_MAX_FILE_SIZE */ + + +#define PEDEVICECHANGED 50/* Device was removed and replaced before flush */ +#define PEDEVICEFAILURE 51/* Driver reports that the device is not working */ +#define PEDEVICEINIT 52/* Trying to access a drive that wasn't initialized */ +#define PEDEVICENOMEDIA 53/* Driver reports that the device is empty */ +#define PEDEVICEUNKNOWNMEDIA 54/* Driver reports that the device is not recognized */ + +#define PEINVALIDBPB 60/* No signature found in BPB (please format) */ +#define PEINVALIDMBR 61/* No signature found in MBR (please write partition table) */ +#define PEINVALIDMBROFFSET 62/* Partition requested but none at that offset */ +#define PEIOERRORREADMBR 63/* IO error reading MBR (note: MBR is first to be read on a new insert) */ +#define PEIOERRORREADBPB 64/* IO error reading BPB (block 0) */ +#define PEIOERRORREADINFO32 65/* IO error reading FAT32 INFO struc (BPB extension) */ + +#define PEIOERRORREADBLOCK 70/* Error reading a directory block through the buffer pool */ +#define PEIOERRORREADFAT 71/* Error reading a fat block through the fat buffer pool */ +#define PEIOERRORWRITEBLOCK 72/* Error writing a directory block through the buffer pool */ +#define PEIOERRORWRITEFAT 73/* Error writing a fat block through the fat buffer pool */ +#define PEIOERRORWRITEINFO32 74/* Error writing info block during fat flush */ + +#define PEINVALIDBLOCKNUMBER 100/* Unexpected block number encountered. Run check disk */ +#define PEINVALIDCLUSTER 101/* Unexpected cluster encountered. Run check disk */ +#define PEINVALIDDIR 102/* A specified path that must be a directory was not. */ +#define PEINTERNAL 103/* Unexpected condition, reset, run check disk */ + +#define PERESOURCE 110/* Out of directory object structures */ +#define PERESOURCEBLOCK 111/* Out of directory and scratch blocks */ +#define PERESOURCEFATBLOCK 112/* Out of fat blocks. (normal only in failsafe mode) */ + +#define PENOINIT 120/* Subsystem not initialized */ + +#define PEFSCREATE 130/*Failsafe -opening journal failed */ +#define PEJOURNALFULL 131/*Failsafe - no room for journal file */ +#define PEIOERRORWRITEJOURNAL 132/*Failsafe - failure writing journal file */ +#define PEFSRESTOREERROR 133/*Failsafe - failure restoring from journal file */ +#define PEFSRESTORENEEDED 134/*Failsafe - restore required but AUTORESTORE + feature is disabled */ +#define PEIOERRORREADJOURNAL 135/*Failsafe - failure reading journal file */ +#define PEFSREINIT 136/*Failsafe - failsafe init called but + failsafe is already initialized for this + device */ +#define PEILLEGALERRNO 200 /* Illegal errno used by regression test */ +/* Arguments to SEEK */ +#define PSEEK_SET 0 /* offset from begining of file*/ +#define PSEEK_CUR 1 /* offset from current file pointer*/ +#define PSEEK_END 2 /* offset from end of file*/ +#define PSEEK_CUR_NEG 3 /* negative offset from end of file*/ + +/* Arguments to po_extend_file */ +#define PC_FIRST_FIT 1 +#define PC_BEST_FIT 2 +#define PC_WORST_FIT 3 +#define PC_FIXED_FIT 4 + +/* Arguments to critical_error_handler() */ +#define CRERR_BAD_FORMAT 1 +#define CRERR_NO_CARD 2 +#define CRERR_BAD_CARD 3 +#define CRERR_CHANGED_CARD 4 +#define CRERR_CARD_FAILURE 5 + +/* Return code from critical_error_handler() */ +#define CRITICAL_ERROR_ABORT 1 +#define CRITICAL_ERROR_RETRY 2 + +/* File API.C: */ +BOOLEAN pc_diskflush(byte *path); +BOOLEAN pc_mkdir(byte *name); +PCFD po_open(byte *name, word flag, word mode); +int po_read(PCFD fd, byte *buf, int count); +int po_write(PCFD fd, byte *buf, int count); +long po_lseek(PCFD fd, long offset, int origin); +BOOLEAN po_ulseek(PCFD fd, dword offset, dword *pnew_offset, int origin); +BOOLEAN po_truncate(PCFD fd, dword offset); +BOOLEAN po_flush(PCFD fd); +int po_close(PCFD fd); +BOOLEAN pc_mv(byte *name, byte *newname); +BOOLEAN pc_unlink(byte *name); +BOOLEAN pc_rmdir(byte *name); +BOOLEAN pc_deltree(byte *name); +long pc_free(byte *path, dword *blocks_total, dword *blocks_free); +BOOLEAN pc_gfirst(DSTAT *statobj, byte *name); +BOOLEAN pc_gnext(DSTAT *statobj); +void pc_gdone(DSTAT *statobj); +BOOLEAN pc_set_default_drive(byte *drive); +int pc_getdfltdrvno(void); +BOOLEAN pc_set_cwd(byte *name); +BOOLEAN pc_isdir(byte *path); +BOOLEAN pc_isvol(byte *path); +BOOLEAN pc_pwd(byte *drive, byte *path); +int pc_fstat(PCFD fd, ERTFS_STAT *pstat); +int pc_stat(byte *name, ERTFS_STAT *pstat); +BOOLEAN pc_get_attributes(byte *path, byte *p_return); +BOOLEAN pc_set_attributes(byte *path, byte attributes); + + +/* APIRT.C */ +int pc_cluster_size(byte *drive); +BOOLEAN po_extend_file(PCFD fd, dword n_bytes, dword *new_bytes, dword start_cluster, int method); +int pc_get_file_extents(PCFD fd, int infolistsize, FILESEGINFO *plist, BOOLEAN raw); +int pc_raw_read(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io); +int pc_raw_write(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io); +int pc_get_free_list(int driveno, int listsize, FREELISTINFO *plist, long threshhold); +int po_chsize(PCFD fd, dword offset); + +/* File REGRESS.C: */ +BOOLEAN pc_regression_test(byte *driveid, BOOLEAN do_clean); + +/* APICKDSK.C */ +BOOLEAN pc_check_disk(byte *drive_id, CHKDISK_STATS *pstat, int verbose, int fix_problems, int write_chains); + +/* End API Definition section */ + +#define BLOCKEQ0 0L + +typedef unsigned long CLUSTERTYPE; + +/* 10-24-2000 added LBA formatting. Submit to main tree */ + +/* Structure used to reserve arrays of blocks in the BSS */ +typedef struct block_alloc { + byte core[512]; + } BLOCK_ALLOC; + +#define PCDELETE (byte) 0xE5 /* MS-DOS file deleted char */ +#define PCDELETEESCAPE (byte) 0xFF /* Escape character rmode + passes to pc_ino2dos to really + delete a directory entry */ + + + +typedef struct fatbuff { + struct fatbuff *pnext; + struct fatbuff *pprev; + struct fatbuff *pnext2; /* All hash table entries start one of these */ +#define FATBLOCK_FREE 0 +#define FATBLOCK_ALLOCATED 1 +#define FATBLOCK_COMMITTED 2 +#define FATBLOCK_UNCOMMITTED 3 + int fat_block_state; + dword fat_blockno; + byte fat_data[512]; + } FATBUFF; + + +/* Fat buffer context */ +typedef struct fatbuffcntxt { + dword stat_primary_cache_hits; + dword stat_secondary_cache_hits; + dword stat_secondary_cache_loads; + dword stat_secondary_cache_swaps; + struct fatbuff *puncommitted_blocks; /* uses pnext/pprev */ + struct fatbuff *pcommitted_blocks; /* uses pnext/pprev */ + struct fatbuff *pfree_blocks; /* uses pnext */ + struct fatbuff *pfat_buffers; /* address of buffer pool */ + int num_blocks; + int num_free; + int low_water; + int hash_size; + dword hash_mask; + dword *mapped_blocks; + byte **mapped_data; + FATBUFF **fat_blk_hash_tbl; /* uses pnext2 */ +} FATBUFFCNTXT; + + /* Structure to contain block 0 image from the disk */ +typedef struct ddrive { + BOOLEAN mount_valid; /* True if valid volume and + BLOCK 0, fat buffer, block pool + etc are valid */ + BOOLEAN mount_abort; /* True if error handler requests abort */ + int drive_opencounter; /* Value of global opencounter when we mounted */ + + dword volume_serialno; /* Volume serial number block 0 */ + byte volume_label[14]; /* Volume entry from block 0 */ + int bytespcluster; /* */ + dword byte_into_cl_mask; /* And this with file pointer to get the + byte offset into the cluster */ + int fasize; /* Nibbles per fat entry. (2 or 4) */ + BLOCKT rootblock; /* First block of root dir */ + BLOCKT firstclblock; /* First block of cluster area */ + int driveno; /* Driveno. Set when open succeeds */ + CLUSTERTYPE maxfindex; /* Last element in the fat - FAT32*/ + BLOCKT fatblock; /* First block in fat */ + int secproot; /* blocks in root dir */ + BOOLEAN fat_is_dirty; + dword bootaddr; + byte oemname[9]; + word bytspsector; /* Must be 512 for this implementation */ + byte secpalloc; /* Sectors per cluster */ + int log2_secpalloc; /* Log of sectors per cluster */ + word secreserved; /* Reserved sectors before the FAT */ + byte numfats; /* Number of FATS on the disk */ + word numroot; /* Maximum # of root dir entries */ + BLOCKT numsecs; /* Total # sectors on the disk */ + byte mediadesc; /* Media descriptor byte */ + CLUSTERTYPE secpfat; /* Size of each fat */ + word secptrk; /* sectors per track */ + word numhead; /* number of heads */ + BLOCKT numhide; /* # hidden sectors */ + CLUSTERTYPE free_contig_base; /* Guess of where file data would most */ + CLUSTERTYPE free_contig_pointer;/* Efficiently stored */ + long known_free_clusters;/* If not -1 pc_free may use this value - FAT32 */ + word infosec; /* Only used for FAT32 */ + +/* These arent new but they are moved to this section because they play a larger */ +/* role. These values are loaded by the routine pc_read_partition_table(). And */ +/* used by formatting and read/write routines. */ + BLOCKT partition_base; /* Start of the partition */ + dword partition_size; /* Size of the partition */ + int partition_type; /* Partition type */ + /* These buffers are used for parsing arguments to API calls. + they are large, so rather then use the stack we place them + in the DDRIVE structure. The drive is always locked when they + are being used */ + byte pathname_buffer[EMAXPATH_BYTES]; + byte filename_buffer[FILENAMESIZE_BYTES]; + int begin_user_area; /* Beyond this is initialized at + run time and must not be written + by ertfs */ +/* user init is required for the following elements required */ + dword register_file_address; + int interrupt_number; /* note -1 is polled for IDE */ + +/* Flags - These must be set by the pc_ertfs_init */ +#define DRIVE_FLAGS_PARTITIONED 0x0002 /* Partitioned device */ +#define DRIVE_FLAGS_PCMCIA 0x0004 /* Pcmcia device */ +#define DRIVE_FLAGS_PCMCIA_ATA 0x0008 +#define DRIVE_FLAGS_FAILSAFE 0x0800 /* Automatically initialize + failsafe operations for + the device. */ +/* Flags - These must be set by the warmstrt IOCTL call to the driver */ +/* VALID is set by the device driver as a result of a successful call to + the device ioctl call DEVCTL_WARMSTART. If the driver does not set this + flag then it i assumed that the driver probe or init sequence failed */ +#define DRIVE_FLAGS_VALID 0x0001 /* Flags have been set */ +#define DRIVE_FLAGS_REMOVABLE 0x0040 /* Device is removable */ +#define DRIVE_FLAGS_INSERTED 0x0080 /* Device drivers use to + remember states */ +#define DRIVE_FLAGS_FORMAT 0x0100 /* If set by the driver then + rtfs_init must format + the device before it + is usable. */ +#define DRIVE_FLAGS_CDFS 0x0200 +#define DRIVE_FLAGS_RDONLY 0x0400 /* Device is read only */ + +#define DRIVE_FLAGS_FILEIO 0x0020 /* Set by RTFS when the current + block transfer is file io */ + /* only used by the driver */ + dword drive_flags; /* Note: the upper byte is reserved + for private use by device drivers */ + int partition_number; + int pcmcia_slot_number; + int pcmcia_controller_number; + byte pcmcia_cfg_opt_value; + + int controller_number; + int logical_unit_number; + /* These two routines are attached to device driver specific routines */ + BOOLEAN (*dev_table_drive_io)(int driveno, dword sector, void *buffer, word count, BOOLEAN readin); + int (*dev_table_perform_device_ioctl)(int driveno, int opcode, void * arg); + /* Access semaphore for the drive. This is initialized in pc_ertfs_init + from a value in value prtfs_cfg->drive_semaphores[i] */ + dword access_semaphore; +#if (STORE_DEVICE_NAMES_IN_DRIVE_STRUCT) + byte device_name[80]; +#endif + FATBUFFCNTXT fatcontext; /* Controls fat cache */ + /* Controls the buffer pool by default this points to + the shared buffcntxt value in rtfs_cfg. If a drive specific + private buffer pool is assigned through the API this will + point to it */ + struct blkbuffcntxt *pbuffcntxt; + void *pfscntxt; /* Failsafe context. O if not in failsafe mode */ + void *fad; /* Pointer to FAT driver structure caste to FAT_DRIVER * at run time*/ +/* end user init required */ + } DDRIVE; + +/* Fat driver functions bound to the drive structure once the type of fat is determined */ +typedef CLUSTERTYPE (*fatfn_alloc_chain)(DDRIVE *pdr, CLUSTERTYPE *pstart_cluster, CLUSTERTYPE n_clusters, BOOLEAN dolink); +typedef CLUSTERTYPE (*fatfn_clnext)(DDRIVE *pdr, CLUSTERTYPE clno); +typedef BOOLEAN (*fatfn_clrelease_dir)(DDRIVE *pdr, CLUSTERTYPE clno); +typedef BOOLEAN (*fatfn_faxx)(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE *pvalue); +typedef BOOLEAN (*fatfn_flushfat)(int driveno); +typedef BOOLEAN (*fatfn_freechain)(DDRIVE *pdr, CLUSTERTYPE cluster, dword min_to_free, dword max_to_free); +typedef CLUSTERTYPE (*fatfn_cl_truncate_dir)(DDRIVE *pdr, CLUSTERTYPE cluster, CLUSTERTYPE l_cluster); +typedef CLUSTERTYPE (*fatfn_get_chain)(DDRIVE *pdr, CLUSTERTYPE start_cluster, CLUSTERTYPE *pnext_cluster, CLUSTERTYPE n_clusters, int *end_of_chain); +typedef BOOLEAN (*fatfn_pfaxxterm)(DDRIVE *pdr, CLUSTERTYPE clno); +typedef BOOLEAN (*fatfn_pfaxx)(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE value); + +typedef struct fat_driver { + fatfn_alloc_chain fatop_alloc_chain; + fatfn_clnext fatop_clnext; + fatfn_clrelease_dir fatop_clrelease_dir; + fatfn_faxx fatop_faxx; + fatfn_flushfat fatop_flushfat; + fatfn_freechain fatop_freechain; + fatfn_cl_truncate_dir fatop_cl_truncate_dir; + fatfn_get_chain fatop_get_chain; + fatfn_pfaxxterm fatop_pfaxxterm; + fatfn_pfaxx fatop_pfaxx; +} FAT_DRIVER; + +#define FATOP(PDR) ((FAT_DRIVER *) PDR->fad) + + + /* Dos Directory Entry Memory Image of Disk Entry */ +#define INOPBLOCK 16 /* 16 of these fit in a block */ +typedef struct dosinode { + byte fname[8]; + byte fext[3]; + byte fattribute; /* File attributes */ + byte resarea[8]; + word fclusterhi; /* This is where FAT32 stores file location */ + word ftime; /* time & date lastmodified */ + word fdate; + word fcluster; /* Cluster for data file */ + dword fsize; /* File size */ + } DOSINODE; + + +#if (VFAT) + +/* Trag lfn segments. segblock[0] is the block that contains beginning of the + file name. segindex is the segment in that block where the beginning is + stored. If the lfn spans > 1 block the next block # is in segblock[2] */ +typedef struct segdesc { + int nsegs; /* # segs in the lfn */ + int segindex; + BLOCKT segblock[3]; + byte ncksum; /* checksum of the associated DOSINODE */ + byte fill[3]; /* nice */ + } SEGDESC; + +#define LFNRECORD SEGDESC +#endif + + /* Internal representation of DOS entry */ + /* The first 8 fields !MUST! be identical to the DOSINODE structure. + The code will be changed later so finode contains a DOSINODE (bug) */ +typedef struct finode { + byte fname[8]; + byte fext[3]; + byte fattribute; /* File attributes */ + byte resarea[8]; + word fclusterhi; /* This is where FAT32 stores file location */ + word ftime; /* time & date lastmodified */ + word fdate; + word fcluster; /* Cluster for data file */ + dword fsize; /* File size */ + + dword alloced_size; /* Size rounded up to the hearest cluster + (only maintained for files */ + int opencount; +/* If the finode is an open file the following flags control the sharing. + they are maintained by po__open */ +#ifdef OF_WRITE +/* The watcom Windows include files define OF_WRITE too */ +#undef OF_WRITE +#endif +#define OF_WRITE 0x01 /* File is open for write by someone */ +#define OF_WRITEEXCLUSIVE 0x02 /* File is open for write by someone + they wish exclusive write access */ +#define OF_EXCLUSIVE 0x04 /* File is open with exclusive access not + sharing write or read */ +#define OF_BUFFERED 0x10 /* Non block alligned data is buffered */ + int openflags; /* For Files. Track how files have it open */ + struct blkbuff *pfile_buffer; /* If the file is opened in buffered + mode this is the last buffered + item */ + int file_buffer_dirty; /* If 1 file buffer needs flush */ + DDRIVE *my_drive; + BLOCKT my_block; + int my_index; + struct finode *pnext; + struct finode *pprev; + BOOLEAN is_free; /* True if on the free list */ +#if (VFAT) + LFNRECORD s; /* Defined as SEGDESC for VFAT */ +#endif + } FINODE; + + +/* contain location information for a directory */ + typedef struct dirblk { + BLOCKT my_frstblock; /* First block in this directory */ + BLOCKT my_block; /* Current block number */ + int my_index; /* dirent number in my block */ + } DIRBLK; + +/* Block buffer */ +typedef struct blkbuff { + struct blkbuff *pnext; /* Used to navigate free and populated lists */ + struct blkbuff *pprev; /* the populated list is double linked. */ + /* Free list is not */ + struct blkbuff *pnext2; /* Each hash table entry starts a chain of these */ +#define DIRBLOCK_FREE 0 +#define DIRBLOCK_ALLOCATED 1 +#define DIRBLOCK_COMMITTED 2 +#define DIRBLOCK_UNCOMMITTED 3 + int block_state; + int use_count; + DDRIVE *pdrive; + dword blockno; + byte data[512]; + } BLKBUFF; + +/* Block buffer context */ +typedef struct blkbuffcntxt { + dword stat_cache_hits; + dword stat_cache_misses; + struct blkbuff *ppopulated_blocks; /* uses pnext/pprev */ + struct blkbuff *pfree_blocks; /* uses pnext */ + int num_blocks; + int num_free; + int low_water; + int num_alloc_failures; + int hash_size; + dword hash_mask; + struct blkbuff **blk_hash_tbl; /* uses pnext2 */ + } BLKBUFFCNTXT; + +/* Object used to find a dirent on a disk and its parent's */ +typedef struct drobj { + DDRIVE *pdrive; + FINODE *finode; + DIRBLK blkinfo; + BOOLEAN isroot; /* True if this is the root */ + BOOLEAN is_free; /* True if on the free list */ + BLKBUFF *pblkbuff; + } DROBJ; + +/* Internal file representation */ +typedef struct pc_file { + DROBJ * pobj; /* Info for getting at the inode */ + word flag; /* Acces flags from po_open(). */ + dword fptr; /* Current file pointer */ + CLUSTERTYPE fptr_cluster; /* Current cluster boundary for fptr */ + dword fptr_block; /* Block address at boundary of fprt_cluster */ + BOOLEAN needs_flush; /* If TRUE this FILE must be flushed */ + BOOLEAN is_free; /* If TRUE this FILE may be used (see pc_memry.c) */ + BOOLEAN at_eof; /* True if fptr was > alloced size last time we set + it. If so synch_file pointers will fix it if the + file has expanded. */ + } PC_FILE; + +/* INTERNAL !! */ +/* Structure to contain block 0 image from the disk */ +struct pcblk0 { + byte jump; /* Should be E9 or EB on formatted disk */ + byte oemname[9]; + word bytspsector; /* Must be 512 for this implementation */ + byte secpalloc; /* Sectors per cluster */ + word secreserved; /* Reserved sectors before the FAT */ + byte numfats; /* Number of FATS on the disk */ + word numroot; /* Maximum # of root dir entries */ + word numsecs; /* Total # sectors on the disk */ + byte mediadesc; /* Media descriptor byte */ + word secpfat; /* Size of each fat */ + word secptrk; /* sectors per track */ + word numhead; /* number of heads */ + word numhide; /* # hidden sectors High word if DOS4 */ + word numhide2; /* # hidden sectors Low word if DOS 4 */ + dword numsecs2; /* # secs if numhid+numsec > 32M (4.0) */ + dword secpfat2; /* Size of FAT in sectors (FAT32 only) */ + byte physdrv; /* Physical Drive No. (4.0) */ + byte xtbootsig; /* Extended signt 29H if 4.0 stuf valid */ + dword volid; /* Unique number per volume (4.0) */ + byte vollabel[11]; /* Volume label (4.0) */ + word flags; /* Defined below (FAT32 only) */ +#define NOFATMIRROR 0x0080 +#define ACTIVEFAT 0x000F + word fs_version; /* Version of FAT32 used (FAT32 only) */ + dword rootbegin; /* Location of 1st cluster in root dir(FAT32 only) */ + word infosec; /* Location of information sector (FAT32 only) */ + word backup; /* Location of backup boot sector (FAT32 only) */ + dword free_alloc; /* Free clusters on drive (-1 if unknown) (FAT32 only) */ + dword next_alloc; /* Most recently allocated cluster (FAT32 only) */ + }; + +/* Partition table descriptions. */ +/* One disk partition table */ +typedef struct ptable_entry { + byte boot; + byte s_head; + word s_cyl; + byte p_typ; + byte e_head; + word e_cyl; + dword r_sec; /* Relative sector of start of part */ + dword p_size; /* Size of partition */ + } PTABLE_ENTRY; + +typedef struct ptable { + PTABLE_ENTRY ents[4]; + word signature; /* should be 0xaa55 */ + } PTABLE; + + +/* Parameter block for formatting: Used by format.c */ +typedef struct fmtparms { + byte oemname[9]; /* Only first 8 bytes are used */ + byte secpalloc; /* Sectors per cluster */ + word secreserved; /* Reserved sectors before the FAT */ + byte numfats; /* Number of FATS on the disk */ + dword secpfat; /* Sectors per fat */ + dword numhide; /* Hidden sectors */ + word numroot; /* Maximum # of root dir entries */ + byte mediadesc; /* Media descriptor byte */ + word secptrk; /* sectors per track */ + word numhead; /* number of heads */ + word numcyl; /* number of cylinders */ + byte physical_drive_no; + dword binary_volume_label; + byte text_volume_label[12]; + } FMTPARMS; + + + +#define CS_OP_ASCII(C) C /* 8 bit unity operator, all char sets */ + +#if (INCLUDE_CS_JIS) +#define CS_OP_CP_CHR(TO,FR) jis_char_copy((TO),(FR)) +#define CS_OP_INC_PTR(P) (P )= jis_increment((P)) +#define CS_OP_CMP_CHAR(P1, P2) jis_compare((P1), (P2)) +#define CS_OP_CMP_CHAR_NC(P1, P2) jis_compare_nc((P1), (P2)) +#define CS_OP_ASCII_INDEX(P,C) jis_ascii_index(P,C) +#define CS_OP_DRNO_TO_LETTER(P,D) *P = ((byte) ('A' + D)) +#define CS_OP_TO_LFN(TO, FROM) jis_to_unicode(TO, FROM) +#define CS_OP_LFI_TO_TXT(TO, FROM) unicode_to_jis(TO, FROM) +#define CS_OP_IS_EOS(P) (*P == 0) +#define CS_OP_IS_NOT_EOS(P) (*P) +#define CS_OP_TERM_STRING(P) *P=0 +#define CS_OP_CMP_ASCII(P,C) (*(P)==C) +#define CS_OP_ASSIGN_ASCII(P,C) (*(P)=C) +#define CS_OP_ASCII_TO_CS_STR(PCSSTR,PASCSTR) rtfs_strcpy(PCSSTR,PASCSTR) +#define CS_OP_CS_TO_ASCII_STR(PASCSTR,PCSSTR) rtfs_strcpy(PASCSTR,PCSSTR) +#define CS_OP_GOTO_EOS(P) jis_goto_eos(P) +#define CS_OP_FORMAT_OUTPUT(P) P +#elif (INCLUDE_CS_ASCII) +#define CS_OP_CP_CHR(TO,FR) *(TO) = *(FR) +#define CS_OP_INC_PTR(P) (P)++ +#define CS_OP_CMP_CHAR(P1, P2) (*(P1)==*(P2)) +#define CS_OP_CMP_CHAR_NC(P1, P2) ascii_compare_nc((P1), (P2)) +#define CS_OP_ASCII_INDEX(P,C) ascii_ascii_index(P,C) +#define CS_OP_DRNO_TO_LETTER(P,D) *P = ((byte) ('A' + D)) +#define CS_OP_TO_LFN(TO, FROM) {*TO = *FROM; *(TO+1) = 0;} +#define CS_OP_LFI_TO_TXT(TO, FROM) *TO = *FROM +#define CS_OP_IS_EOS(P) (*P == 0) +#define CS_OP_IS_NOT_EOS(P) (*P) +#define CS_OP_TERM_STRING(P) *P=0 +#define CS_OP_CMP_ASCII(P,C) (*(P)==C) +#define CS_OP_ASSIGN_ASCII(P,C) (*(P)=C) +#define CS_OP_ASCII_TO_CS_STR(PCSSTR,PASCSTR) rtfs_strcpy(PCSSTR,PASCSTR) +#define CS_OP_CS_TO_ASCII_STR(PASCSTR,PCSSTR) rtfs_strcpy(PASCSTR,PCSSTR) +#define CS_OP_GOTO_EOS(P) ascii_goto_eos(P) +#define CS_OP_FORMAT_OUTPUT(P) P +#elif (INCLUDE_CS_UNICODE) +#define CS_OP_CMP_CHAR(P1, P2) unicode_compare((P1), (P2)) +#define CS_OP_CMP_CHAR_NC(P1, P2) unicode_compare_nc((P1), (P2)) +#define CS_OP_CP_CHR(TO,FR) {*(TO)=*(FR);*((TO)+1)=*((FR)+1);} +#define CS_OP_INC_PTR(P) (P)=(P)+2 +#define CS_OP_TO_LFN(TO, FROM) unicode_chr_to_lfn((TO), FROM) +#define CS_OP_LFI_TO_TXT(TO, FROM) lfn_chr_to_unicode(TO, FROM) +#define CS_OP_IS_EOS(P) (*P == 0 && *(P +1) == 0) +#define CS_OP_IS_NOT_EOS(P) (*P || *(P +1)) +#define CS_OP_TERM_STRING(P) {*P=0;*(P +1)=0;} +#define CS_OP_ASCII_INDEX(P,C) unicode_ascii_index(P,C) +#define CS_OP_DRNO_TO_LETTER(P,D) unicode_drno_to_letter(P,D) +#define CS_OP_CMP_ASCII(P,C) unicode_cmp_to_ascii_char((P),C) +#define CS_OP_ASSIGN_ASCII(P,C) unicode_assign_ascii_char((P),C) +#define CS_OP_ASCII_TO_CS_STR(PCSSTR,PASCSTR) map_ascii_to_unicode(PCSSTR,PASCSTR) +#define CS_OP_CS_TO_ASCII_STR(PASCSTR,PCSSTR) map_unicode_to_ascii(PASCSTR,PCSSTR) +#define CS_OP_GOTO_EOS(P) unicode_goto_eos(P) +#define CS_OP_FORMAT_OUTPUT(P) unicode_make_printable(P) +#endif + + + +/* Make sure memory is initted prolog for api functions */ +#define CHECK_MEM(TYPE, RET) if (!prtfs_cfg) {return((TYPE) RET);} +#define VOID_CHECK_MEM() if (!prtfs_cfg) {return;} +#define IS_AVOLORDIR(X) ((X->isroot) || (X->finode->fattribute & AVOLUME|ADIRENT)) + + +/* File RTFSINIT.C: */ +BOOLEAN pc_ertfs_init(void); + +/* File APIPRINI.C: */ +BOOLEAN pro_auto_drive_init(DDRIVE *pdrive); + +/* File RTFSCFG.C */ +BOOLEAN pc_ertfs_config(void); + +/* File API.C: */ +BOOLEAN _po_ulseek(PC_FILE *pfile, dword offset, dword *new_offset, int origin); +long _po_lseek(PC_FILE *pfile, long offset, int origin); +BOOLEAN _po_flush(PC_FILE *pfile); +BOOLEAN pc_is(int op, byte *path); +dword pc_find_contig_clusters(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE *pchain, CLUSTERTYPE min_clusters, int method); + +/* File APIUTIL.C: */ +BOOLEAN pc_i_dskopen(int driveno); +int check_drive(byte *name); +PC_FILE *pc_fd2file(PCFD fd,int flags); +PCFD pc_allocfile(void); +int pc_enum_file(DDRIVE *pdrive, int chore); +void pc_free_all_fil(DDRIVE *pdrive); +BOOLEAN pc_flush_all_fil(DDRIVE *pdrive); +int pc_test_all_fil(DDRIVE *pdrive); +BOOLEAN pc_flush_file_buffer(PC_FILE *pfile); +BOOLEAN pc_load_file_buffer(PC_FILE *pfile, dword new_blockno); +BOOLEAN pc_sync_file_buffer(PC_FILE *pfile, dword start_block, dword nblocks, BOOLEAN doflush); +int pc_log_base_2(word n); +BOOLEAN pc_dskinit(int driveno); +BOOLEAN get_disk_volume(int driveno, byte *pvollabel, dword *pserialno); +BOOLEAN pc_idskclose(int driveno); +DROBJ *pc_get_cwd(DDRIVE *pdrive); +void pc_upstat(DSTAT *statobj); +BOOLEAN _synch_file_ptrs(PC_FILE *pfile); +int pc_read_partition_table(int driveno, DDRIVE *pdr); + +/* File PRBLOCK.C: */ +void pc_free_all_blk(DDRIVE *pdrive); +void pc_discard_buf(BLKBUFF *pblk); +void pc_release_buf(BLKBUFF *pblk); +BOOLEAN pc_initialize_block_pool(BLKBUFFCNTXT *pbuffcntxt, int nblkbuffs, + BLKBUFF *pmem_block_pool, int blk_hashtble_size, BLKBUFF **pblock_hash_table); +BLKBUFF *pc_find_blk(DDRIVE *pdrive, dword blockno); +BLKBUFF *pc_init_blk(DDRIVE *pdrive, BLOCKT blockno); +BLKBUFF *pc_read_blk(DDRIVE *pdrive, BLOCKT blockno); +BOOLEAN pc_write_blk(BLKBUFF *pblk); +BLKBUFF *pc_scratch_blk(void); +FATBUFF *pc_find_fat_blk(FATBUFFCNTXT *pfatbuffcntxt, dword blockno); +void pc_flush_chain_blk(DDRIVE *pdrive, CLUSTERTYPE cluster); +void pc_free_scratch_blk(BLKBUFF *pblk); + +BLKBUFF *pc_alloc_file_buffer(DDRIVE *pdrive); +void pc_free_file_buffer(BLKBUFF *pblk); +BOOLEAN pc_initialize_fat_block_pool(FATBUFFCNTXT *pfatbuffcntxt, + int fat_buffer_size, FATBUFF *pfat_buffers, + int fat_hashtbl_size, FATBUFF **pfat_hash_table, + byte **pfat_primary_cache, dword *pfat_primary_index); +void pc_free_all_fat_blocks(FATBUFFCNTXT *pfatbuffcntxt); +BOOLEAN pc_flush_fat_blocks(DDRIVE *pdrive); +byte *pc_map_fat_block(DDRIVE *pdrive, dword blockno, dword usage_flags); + + + +/* File DEVIO.C: */ +int check_drive_name_mount(byte *name); +BOOLEAN check_drive_number_mount(int driveno); +void release_drive_mount(int driveno); +BOOLEAN release_drive_mount_write(int driveno); +BOOLEAN check_drive_number_present(int driveno); +BOOLEAN devio_read(int driveno, dword blockno, byte * buf, word n_to_read, BOOLEAN raw); +BOOLEAN devio_write(int driveno, dword blockno, byte * buf, word n_to_write, BOOLEAN raw); +BOOLEAN devio_write_format(int driveno, dword blockno, byte * buf, word n_to_write, BOOLEAN raw); + +/* File DROBJ.C: */ +DROBJ *pc_fndnode(byte *path); +DROBJ *pc_get_inode( DROBJ *pobj, DROBJ *pmom, byte *filename, byte *fileext, int action); +BOOLEAN pc_findin( DROBJ *pobj, byte *filename, byte *fileext, int action); +DROBJ *pc_get_mom(DROBJ *pdotdot); +DROBJ *pc_mkchild( DROBJ *pmom); +DROBJ *pc_mknode(DROBJ *pmom ,byte *filename, byte *fileext, byte attributes, CLUSTERTYPE incluster); +BOOLEAN pc_insert_inode(DROBJ *pobj , DROBJ *pmom, byte attr, CLUSTERTYPE cluster, byte *filename, byte *fileext); +BOOLEAN pc_rmnode( DROBJ *pobj); +BOOLEAN pc_update_inode(DROBJ *pobj, BOOLEAN set_archive, BOOLEAN set_date); +DROBJ *pc_get_root( DDRIVE *pdrive); +BLOCKT pc_firstblock( DROBJ *pobj); +BOOLEAN pc_next_block( DROBJ *pobj); +BLOCKT pc_l_next_block(DDRIVE *pdrive, BLOCKT curblock); +void pc_marki( FINODE *pfi, DDRIVE *pdrive, BLOCKT sectorno, int index); +FINODE *pc_scani( DDRIVE *pdrive, BLOCKT sectorno, int index); +DROBJ *pc_allocobj(void); +FINODE *pc_alloci(void); +void pc_free_all_drobj( DDRIVE *pdrive); +void pc_free_all_i( DDRIVE *pdrive); +void pc_freei( FINODE *pfi); +void pc_freeobj( DROBJ *pobj); +void pc_dos2inode (FINODE *pdir, DOSINODE *pbuff); +void pc_init_inode(FINODE *pdir, KS_CONSTANT byte *filename, + KS_CONSTANT byte *fileext, byte attr, + CLUSTERTYPE cluster, dword size, DATESTR *crdate); +void pc_ino2dos (DOSINODE *pbuff, FINODE *pdir); +BOOLEAN pc_isavol( DROBJ *pobj); +BOOLEAN pc_isadir( DROBJ *pobj); +BOOLEAN pc_isroot( DROBJ *pobj); + + +/* File FORMAT.C: */ +/* See end of file for prototypes */ + +/* FATXX.C */ +BOOLEAN init_fat32(DDRIVE *pdr); +BOOLEAN init_fat16(DDRIVE *pdr); +BOOLEAN init_fat12(DDRIVE *pdr); + +/* FAT32.C */ +BOOLEAN pc_init_drv_fat_info(DDRIVE *pdr, struct pcblk0 *pbl0); +/* These are defined twice. once for FAT32 systems once for non FAT32 */ +CLUSTERTYPE pc_get_parent_cluster(DDRIVE *pdrive, DROBJ *pobj); +CLUSTERTYPE pc_grow_dir(DDRIVE *pdrive, DROBJ *pobj); +CLUSTERTYPE pc_alloc_dir(DDRIVE *pdrive, DROBJ *pobj); +void pc_truncate_dir(DDRIVE *pdrive, DROBJ *pobj, CLUSTERTYPE cluster); +BOOLEAN pc_mkfs32(int driveno, FMTPARMS *pfmt, BOOLEAN use_raw); +CLUSTERTYPE pc_finode_cluster(DDRIVE *pdr, FINODE *finode); +void pc_pfinode_cluster(DDRIVE *pdr, FINODE *finode, CLUSTERTYPE value); +BOOLEAN pc_gblk0_32(word driveno, struct pcblk0 *pbl0, byte *b); +BOOLEAN pc_validate_partition_type(byte p_type); +BOOLEAN fat_flushinfo(DDRIVE *pdr); + + +/* FAT16.C */ +BOOLEAN pc_init_drv_fat_info16(DDRIVE *pdr, struct pcblk0 *pbl0); +BOOLEAN pc_mkfs16(int driveno, FMTPARMS *pfmt, BOOLEAN use_raw); + +/* File LOWL.C: */ +BOOLEAN pc_gblk0(word driveno, struct pcblk0 *pbl0); +BOOLEAN pc_clzero(DDRIVE *pdrive, CLUSTERTYPE cluster); +DDRIVE *pc_drno2dr(int driveno); +DDRIVE *pc_drno_to_drive_struct(int driveno); +BOOLEAN pc_dskfree(int driveno); +CLUSTERTYPE pc_sec2cluster(DDRIVE *pdrive, BLOCKT blockno); +word pc_sec2index(DDRIVE *pdrive, BLOCKT blockno); +BLOCKT pc_cl2sector(DDRIVE *pdrive, CLUSTERTYPE cluster); +CLUSTERTYPE pc_finode_cluster(DDRIVE *pdr, FINODE *finode); +void pc_pfinode_cluster(DDRIVE *pdr, FINODE *finode, CLUSTERTYPE value); +dword pc_chain_length(DDRIVE *pdrive, dword byte_length); + +/* File PC_MEMRY.C: */ +int pc_num_drives(void); +int pc_num_users(void); +int pc_nuserfiles(void); +BOOLEAN pc_validate_driveno(int driveno); +BOOLEAN pc_memory_init(void); +void pc_memory_close(void); +DROBJ *pc_memory_drobj(DROBJ *pobj); +FINODE *pc_memory_finode(FINODE *pinode); + +/* File UTILJIS.C: */ +byte *jis_goto_eos(byte *p); +int jis_char_length(byte *p); +int jis_char_copy(byte *to, byte *from); +byte *jis_increment(byte *p); +int rtfs_jis_strlen(byte * string); +int jis_compare(byte *p1, byte *p2); +int jis_compare_nc(byte *p1, byte *p2); +int jis_ascii_index(byte *p, byte c); +BOOLEAN valid_jis_char(byte *p); +byte *map_jis_input(byte *pin); +byte *map_jis_output(byte *pin); +BOOLEAN _illegal_jis_alias_char(byte *p); +int rtfs_jis_strlen(byte * string); +int jis_char_copy(byte *to, byte *from); +BOOLEAN _illegal_jis_lfn_char(byte *p); + +/* File UTILUNI.C: */ +byte *unicode_goto_eos(byte *p); +void unicode_drno_to_letter(byte *p,int driveno); +int unicode_compare(byte *p1, byte *p2); +int unicode_compare_nc(byte *p1, byte *p2); + +BOOLEAN pc_unicode_patcmp(byte *bpat, byte *bname, BOOLEAN dowildcard); +BOOLEAN pc_valid_lfn(byte *afilename); +void lfn_chr_to_unicode(byte *to, byte *fr); +void unicode_chr_to_lfn(byte *to, byte *fr); +int rtfs_unicode_strcpy(byte * targ, byte * src); +int rtfs_unicode_strcmp(byte * s1, byte * s2); +int rtfs_unicode_strlen(byte * string); +int unicode_ascii_index(byte *p, byte c); +void map_ascii_to_unicode(byte *unicode_to, byte *ascii_from); +void map_unicode_to_ascii(byte *to, byte *from); +BOOLEAN pc_parsepath(byte *atopath, byte *afilename, byte *afileext, byte *apath); +void pc_ascii_strn2upper(byte *to, byte *from, int n); +void pc_byte2upper(byte *to, byte *from); +void pc_ascii_str2upper(byte *to, byte *from); +BOOLEAN pc_cs_malias(byte *alias, byte *input_file, int try); +int unicode_cmp_to_ascii_char(byte *p, byte c); +void unicode_assign_ascii_char(byte *p, byte c); +byte *unicode_make_printable(byte *p); +byte *unicode_convert_strtable(byte *p); + +/* File UTILASCI.C */ +byte *ascii_goto_eos(byte *p); +void pc_ascii_strn2upper(byte *to, byte *from, int n); +void pc_byte2upper(byte *to, byte *from); +void pc_ascii_str2upper(byte *to, byte *from); +void pc_str2upper(byte *to, byte *from); +BOOLEAN validate_filename(byte * name, byte * ext); +int ascii_compare_nc(byte *p1, byte *p2); +int ascii_ascii_index(byte *p,byte c); +static BOOLEAN valid_alias_char(byte c); +BOOLEAN validate_8_3_name(byte * name,int len); +BOOLEAN validate_filename(byte * name, byte * ext); +BOOLEAN pc_valid_sfn(byte *filename); +BOOLEAN pc_valid_lfn(byte *filename); +BOOLEAN pc_cs_malias(byte *alias, byte *input_file, int try); +BOOLEAN pc_ascii_fileparse(byte *fname, byte *fext, byte *alias); + + +/* These routines are duplicated in UTILJIS.C UTILASCI.C UTILUNI.C */ +BOOLEAN pc_patcmp_8(byte *pat, byte *name, BOOLEAN dowildcard); +BOOLEAN pc_patcmp_3(byte *pat, byte *name, BOOLEAN dowildcard); +void pc_ascii_strn2upper(byte *to, byte *from, int n); +void pc_byte2upper(byte *to, byte *from); +void pc_ascii_str2upper(byte *to, byte *from); +void pc_str2upper(byte *to, byte *from); +BOOLEAN validate_filename(byte * name, byte * fext); +BOOLEAN validate_8_3_name(byte * name,int len); +BOOLEAN pc_valid_sfn(byte *filename); +BOOLEAN pc_cs_malias(byte *alias, byte *input_file, int try); +byte *pc_cs_mfile(byte *to, byte *filename, byte *ext); +byte *pc_ascii_mfile(byte *to, byte *filename, byte *ext); +int rtfs_cs_strcmp(byte * s1, byte * s2); +int rtfs_cs_strcpy(byte * targ, byte * src); +int rtfs_cs_strlen(byte * string); +int rtfs_cs_strlen_bytes(byte * string); +byte *rtfs_cs_strcat(byte *to, byte *from); + +/* File JISTAB.C */ +int unicode_to_jis(byte *pjis, byte *punicode); +void jis_to_unicode(byte *to, byte *from); + +/* File UTILBYTE.C: */ +byte *pc_strchr(byte *string, byte ch); +BOOLEAN _illegal_alias_char(byte ch); +BOOLEAN _illegal_lfn_char(byte ch); +BOOLEAN pc_isdot(byte *fname, byte *fext); +BOOLEAN pc_isdotdot(byte *fname, byte *fext); +BOOLEAN name_is_reserved(byte *filename); +byte pc_cksum(byte *test); +int rtfs_strcpy(byte * targ, byte * src); +int rtfs_strcmp(byte * s1, byte * s2); +int rtfs_strlen(byte * string); +void copybuff(void *vto, void *vfrom, int size); +void pc_cppad(byte *to, byte *from, int size); +void rtfs_memset(void *pv, byte b, int n); +byte * rtfs_strcat(byte * targ, byte * src); + +/* File UTIL.C: */ +byte *pc_mpath(byte *to, byte *path, byte *filename); +int pc_path_to_driveno(byte *path); +int pc_parse_raw_drive(byte *path); +byte *pc_parsedrive(int *driveno, byte *path); +byte *pc_nibbleparse(byte *filename, byte *fileext, byte *path); +BOOLEAN pc_parsepath(byte *topath, byte *filename, byte *fileext, byte *path); +BOOLEAN pc_patcmp_vfat(byte *pat, byte *name, BOOLEAN dowildcard); +BOOLEAN pc_malias(byte *fname, byte *fext, byte *input_file, DROBJ *dest); +void RTFS_ARGSUSED_PVOID(void * p); +void RTFS_ARGSUSED_INT(int i); +dword to_DWORD ( byte *from); +word to_WORD ( byte *from); +void fr_WORD ( byte *to, word from); +void fr_DWORD ( byte *to, dword from); + + +/* Extra arguments to pc_get_inode() and pc_findin() */ +#define GET_INODE_MATCH 0 /* Must match the pattern exactly */ +#define GET_INODE_WILD 1 /* Pattern may contain wild cards */ +#define GET_INODE_STAR 2 /* Like he passed *.* (pattern will be null) */ +#define GET_INODE_DOTDOT 3 /* Like he past .. (pattern will be null */ + + +/* User structure management */ +typedef struct rtfs_system_user +{ + dword task_handle; /* Task this is for */ + int rtfs_errno; /* current errno value for the task */ + dword rtfs_driver_errno;/* device driver errno value for the task */ + int dfltdrv; /* Default drive to use if no drive specified */ + int dfltdrv_set; /* If 1 Default drive was set, + otherwise use the default in rtfs_cfg */ + void * lcwd[26]; /* current working enough for 26 drives */ + +} RTFS_SYSTEM_USER; + +typedef struct rtfs_system_user *PRTFS_SYSTEM_USER; + +/* RTFSKERN.C */ +void pc_report_error(int error_number); +PRTFS_SYSTEM_USER rtfs_get_system_user(void); +BOOLEAN rtfs_resource_init(void); +void pc_free_user(void); +void pc_free_all_users(int driveno); +int rtfs_set_errno(int error); +int get_errno(void); +void rtfs_set_driver_errno(dword error); +dword rtfs_get_driver_errno(void); +int critical_error_handler(int driveno, int media_status, dword sector); + +/* These opcodes must be supported by the device driver's + perform_device_ioctl() routine. +*/ + +#define DEVCTL_CHECKSTATUS 0 + +/* DEVCTL_WARMSTART - Called when RTIP initializes. + When ERTFS initializes it calls xxx_device_ioctl() with this + argument in ascending order for each possible logical device. + The device driver may use this call to perform spin up of the + device or to clear driver specific information. In most cases + this routine can do nothing and simply return zero. + The device driver must return zero. There is no error condition +*/ + +#define DEVCTL_WARMSTART 1 + +/* This is called by RTFS when it notices that power has been restored. IE + if current power count is not the same as it was the last time that the + device was accessed. */ + +#define DEVCTL_POWER_RESTORE 2 + +/* Called when RTFS when power is going down. +!!! RTFS does not make this call. It is provided as a mechanism + that that system power management system may use to communicate a power + down event to the device driver */ +#define DEVCTL_POWER_LOSS 3 + + +/* Perform a low level format on the media. (If required). Return 0 on + success or return a driver specific format error code. ERTFS does not + interpret the error code but it will abort a file system format operation + if anything but zero is returned. */ + +#define DEVCTL_FORMAT 4 + + +/* + DEVCTL_GET_GEOMETRY - Report back to ertfs the geometry of the + device in the structure pointed to by the arg pointer. + The structure pointed to at parg is of type PDEV_GEOMETRY: + + Note: The geometry must be in DOS HCN format. If the device is a + logical block oriented device (where blocks are a contiguous + array of blocks) then the device must convert lba units to HCN units. + + The device should return zero if it can report the geometry. + Any other return code will cause ERTFS to abort a file + system format operation. + +*/ + +#define DEVCTL_GET_GEOMETRY 5 + + +typedef struct dev_geometry { + int dev_geometry_heads; /*- Must be < 256 */ + dword dev_geometry_cylinders; /*- Must be < 1024 */ + int dev_geometry_secptrack; /*- Must be < 64 */ + dword dev_geometry_lbas; /*- For oversize media */ + BOOLEAN fmt_parms_valid; /*If the device io control call sets this */ + /*TRUE then it it telling the applications */ + /*layer that these format parameters should */ + /*be used. This is a way to format floppy */ + /*disks exactly as they are fromatted by dos. */ + FMTPARMS fmt; +} DEV_GEOMETRY; + +#define DEVCTL_REPORT_REMOVE 6 /* this can be called by an external */ + /* interrupt to tell the driver that */ + /* the device has been removed. For */ + /* pcmcia management interrupt calls */ + /* this and the pcmcia aware drivers */ + /* become aware tht they have to re-mount */ + + +/*---------- ctr modified ----------*/ +/*_po_flushから呼ばれる。RTFSがflush時にdeviceのIO関数を呼んだ後に + これが呼ばれる。メディアにフラッシュせよとの指示。 + _po_flush は apiwrite.c を参照のこと。*/ +#define DEVCTL_FLUSH 7 +/*----------------------------------*/ + + +typedef struct dev_geometry *PDEV_GEOMETRY; + + +/* These codes are to be returned from the device driver's + perform_device_ioctl() function when presented with + DEVCTL_CHECKSTATUS as an opcode. +*/ + +#define DEVTEST_NOCHANGE 0 /* Status is 'UP' */ +#define DEVTEST_NOMEDIA 1 /* The device is empty */ +#define DEVTEST_UNKMEDIA 2 /* Contains unknown media */ +#define DEVTEST_CHANGED 3 /* Controller recognized and cleared a */ + /* change condition */ +#define BIN_VOL_LABEL 0x12345678L + +#define READ_PARTION_OK 0 /* Partition read succesfully */ +#define READ_PARTION_ERR -1 /* Internal error (couldn't allocate buffers ?) */ +#define READ_PARTION_NO_TABLE -2 /* No partition table found */ +#define READ_PARTION_NO_ENTRY -3 /* Request entry not found */ +#define READ_PARTION_IOERROR -4 /* Device IO error */ + +/* File FORMAT.C: */ +BOOLEAN pc_get_media_parms(byte *path, PDEV_GEOMETRY pgeometry); +BOOLEAN pc_format_media(byte *path, PDEV_GEOMETRY pgeometry); +BOOLEAN pc_partition_media(byte *path, PDEV_GEOMETRY pgeometry, dword * partition_list); +BOOLEAN pc_format_volume(byte *path, PDEV_GEOMETRY pgeometry); + +/* File VFAT.C */ +BOOLEAN pc_delete_lfn_info(DROBJ *pobj); +void pc_zero_lfn_info(FINODE *pdir); +BOOLEAN pc_get_lfn_filename(DROBJ *pobj, byte *path); + +/* Terminal input/output macros. +* RTFS_PRINT_LONG_1 +* RTFS_PRINT_STRING_1 +* RTFS_PRINT_STRING_2 +* +* These macros by default are defined as calls to functions in rtfsterm.c as seen here +* +* #define RTFS_PRINT_LONG_1(L1,FLAGS) rtfs_print_long_1(L1,FLAGS) +* #define RTFS_PRINT_STRING_1(STR1ID,FLAGS) rtfs_print_string_1(STR1ID,FLAGS) +* #define RTFS_PRINT_STRING_2(STR1ID,STR2,FLAGS) rtfs_print_string_2(STR1ID,STR2,FLAGS) +* +* If no console output available thy may be dfined as seen here +* +* #define RTFS_PRINT_LONG_1(L1,FLAGS) +* #define RTFS_PRINT_STRING_1(STR1ID,FLAGS) +* #define RTFS_PRINT_STRING_2(STR1ID,STR2,FLAGS) +* +*/ +/* String printing control characters used throughout the library */ +#define PRFLG_NL 0x0001 /* Newline Carriage return at end */ +#define PRFLG_CR 0x0002 /* Carriage Return only at end */ + +/* Prototype of console input function that is implemented in rtfsterm.c */ +void rtfs_print_prompt_user(int prompt_id, byte *buf); + +/* Prototypes of print functions that are implemented in rtfsterm.c + these routines are never called directly but are called through macros */ +void rtfs_print_long_1(dword l1,int flags); +void rtfs_print_string_1(int str1_id,int flags); +void rtfs_print_string_2(int str1_id,byte *pstr2, int flags); + +/* Macros that are used to access the print routines */ +#define RTFS_PRINT_LONG_1(L1,FLAGS) rtfs_print_long_1(L1,FLAGS) +#define RTFS_PRINT_STRING_1(STR1ID,FLAGS) rtfs_print_string_1(STR1ID,FLAGS) +#define RTFS_PRINT_STRING_2(STR1ID,STR2,FLAGS) rtfs_print_string_2(STR1ID,STR2,FLAGS) + +BOOLEAN pc_failsafe_hold(DDRIVE *pdrive); + +/* These STUBBED versions may be used instead if output is not available */ + +/* +#define RTFS_PRINT_LONG_1(L1,FLAGS) +#define RTFS_PRINT_STRING_1(STR1ID,FLAGS) +#define RTFS_PRINT_STRING_2(STR1ID,STR2,FLAGS) +*/ + +/* PORTRTFS.C */ +dword rtfs_port_alloc_mutex(void); +void rtfs_port_claim_mutex(dword handle); +void rtfs_port_release_mutex(dword handle); +dword rtfs_port_alloc_signal(void); +void rtfs_port_clear_signal(dword handle); +int rtfs_port_test_signal(dword handle, int timeout); +void rtfs_port_set_signal(dword handle) ; +void rtfs_port_sleep(int sleeptime) ; +dword rtfs_port_elapsed_zero(void) ; +int rtfs_port_elapsed_check(dword zero_val, int timeout); +dword rtfs_port_get_taskid(void) ; +void rtfs_port_tm_gets(byte *buffer); +void rtfs_port_puts(byte *buffer); +void rtfs_port_disable(void); +void rtfs_port_enable(void); +void rtfs_port_exit(void); +DATESTR *pc_getsysdate(DATESTR * pd); +void hook_82365_pcmcia_interrupt(int irq); +void phys82365_to_virtual(byte ** virt, dword phys); +void write_82365_index_register(byte value) ; +void write_82365_data_register(byte value); +byte read_82365_data_register(void); +void hook_ide_interrupt(int irq, int controller_number); +byte ide_rd_status(dword register_file_address); +word ide_rd_data(dword register_file_address); +byte ide_rd_sector_count(dword register_file_address); +byte ide_rd_alt_status(dword register_file_address,int contiguous_io_mode); +byte ide_rd_error(dword register_file_address); +byte ide_rd_sector_number(dword register_file_address); +byte ide_rd_cyl_low(dword register_file_address); +byte ide_rd_cyl_high(dword register_file_address); +byte ide_rd_drive_head(dword register_file_address); +byte ide_rd_drive_address(dword register_file_address, int contiguous_io_mode); +void ide_wr_dig_out(dword register_file_address, int contiguous_io_mode, byte value); +void ide_wr_data(dword register_file_address, word value); +void ide_wr_sector_count(dword register_file_address, byte value); +void ide_wr_sector_number(dword register_file_address, byte value); +void ide_wr_cyl_low(dword register_file_address, byte value); +void ide_wr_cyl_high(dword register_file_address, byte value); +void ide_wr_drive_head(dword register_file_address, byte value); +void ide_wr_command(dword register_file_address, byte value); +void ide_wr_feature(dword register_file_address, byte value); +byte ide_rd_udma_status(dword bus_master_address); +void ide_wr_udma_status(dword bus_master_address, byte value); +byte ide_rd_udma_command(dword bus_master_address); +void ide_wr_udma_command(dword bus_master_address, byte value); +void ide_wr_udma_address(dword bus_master_address, dword bus_address); +void ide_insw(dword register_file_address, unsigned short *p, int nwords); +void ide_outsw(dword register_file_address, unsigned short *p, int nwords); +dword rtfs_port_ide_bus_master_address(int controller_number); +unsigned long rtfs_port_bus_address(void * p); + +BOOLEAN pcmctrl_init(void); +BOOLEAN pcmcia_card_is_ata(int socket, dword register_file_address,int interrupt_number, byte pcmcia_cfg_opt_value); +BOOLEAN pcmctrl_card_installed(int socket); +/* Drive access semaphores are store in the drive structure. These macros + map logical drive number to drive structure and then access the semaphores */ +#define OS_CLAIM_LOGDRIVE(X) rtfs_port_claim_mutex((prtfs_cfg->drno_to_dr_map[X])->access_semaphore); +#define OS_RELEASE_LOGDRIVE(X) rtfs_port_release_mutex((prtfs_cfg->drno_to_dr_map[X])->access_semaphore); +/* System wide critical region semaphore */ +#define OS_CLAIM_FSCRITICAL() rtfs_port_claim_mutex(prtfs_cfg->critical_semaphore); +#define OS_RELEASE_FSCRITICAL() rtfs_port_release_mutex(prtfs_cfg->critical_semaphore); + + +/* Configuration structure. Must be filled in by the user. + see rtfscfg.c */ +typedef struct rtfs_cfg { + /* Configuration values */ + int cfg_NDRIVES; /* The number of drives to support */ + int cfg_NBLKBUFFS; /* The number of block buffers */ + int cfg_BLK_HASHTBLE_SIZE; /* The number entries in the hash table */ + int cfg_NUSERFILES; /* The number of user files */ + int cfg_NDROBJS; /* The number of directory objects */ + int cfg_NFINODES; /* The number of directory inodes */ + int cfg_NUM_USERS; /* The number of users to support */ + int cfg_FAT_BUFFER_SIZE[26]; /* Fat buffer size in blocks, per logical drive */ + int cfg_FAT_HASHTBL_SIZE[26]; /* Fat hash table entries, per logical drive */ + + /* Core that must be provided by the user */ + DDRIVE *mem_drives_structures; /* Point at cfg_NDRIVES * sizeof(DDRIVE) bytes*/ + BLKBUFF *mem_block_pool; /* Point at cfg_NBLKBUFFS * sizeof(BLKBUFF) bytes*/ + BLKBUFF **mem_block_hash_table; /* Point cfg_BLK_HASHTBL_SIZE dwords */ + PC_FILE *mem_file_pool; /* Point at cfg_USERFILES * sizeof(PC_FILE) bytes*/ + DROBJ *mem_drobj_pool; /* Point at cfg_DROBJS * sizeof(DROBJ) bytes*/ + FINODE *mem_finode_pool; /* Point at cfg_NFINODE * sizeof(FINODE) bytes*/ + FATBUFF *fat_buffers[26]; /* Point fat_buffers[N] at cfg_FAT_BUFFER_SIZE[N] * sizeof(FATBUFF) bytes*/ + FATBUFF **fat_hash_table[26]; /* Point fat_hash_table[N] at cfg_FAT_HASHTBL_SIZE[N] * sizeof(FATBUFF *) */ + byte **fat_primary_cache[26]; /* Point fat_primary_cache[[N] at cfg_FAT_HASHTBL_SIZE[N] * sizeof(byte *) */ + dword *fat_primary_index[26]; /* Point fat_primary_index[[N] at cfg_FAT_HASHTBL_SIZE[N] * sizeof(dword) */ + RTFS_SYSTEM_USER *rtfs_user_table; /* Point at cfg_NUM_USERS * sizeof(RTFS_SYSTEM_USER) bytes*/ + /* These pointers are internal no user setup is needed */ + BLKBUFFCNTXT buffcntxt; /* Systemwide shared buffer pool */ + FINODE *inoroot; /* Begining of inode pool */ + FINODE *mem_finode_freelist; + DROBJ *mem_drobj_freelist; + DDRIVE *drno_to_dr_map[26]; + + dword ide_semaphore; /* Allocated and used by the IDE driver */ + dword floppy_semaphore; /* Allocated and used by the FLOPPY driver */ + dword floppy_signal; /* Allocated and used by the FLOPPY driver */ + dword critical_semaphore; /* Used by ERTFS for critical sections */ +/* Note: cfg_NDRIVES semaphores are allocated and assigned to the individual + drive structure within routine pc_ertfs_init() */ + /* This value is set in pc_rtfs_init(). It is the drive number of the + lowest (0-25) == A: - Z:. valid drive identifier in the system. + If the user does not set a default drive, this value will be used. */ + int default_drive_id; + /* BSS area used by the block buffer pool system */ + dword useindex; + DDRIVE *scratch_pdrive; + /* Counter used to uniquely identify a drive mount. Each time an open + succeeds this value is incremented and stored in the drive structure. + it is used by gfirst gnext et al to ensure that the drive was not + closed and remounted between calls */ + int drive_opencounter; +} RTFS_CFG; + +extern RTFS_CFG *prtfs_cfg; + +#if (INCLUDE_FAILSAFE_CODE) +#include +#endif /* INCLUDE_FAILSAFE_CODE */ +/* Include RTFS Pro features */ +#include //twl modified + + +#include //ctr modified + +BOOLEAN rtfs_init( void); //ctr modified + + + +#endif /* __RTFS__ */ diff --git a/include/twl/fatfs/ARM7/rtfs_naming_convention.h b/include/twl/fatfs/ARM7/rtfs_naming_convention.h new file mode 100644 index 0000000..2fad9cc --- /dev/null +++ b/include/twl/fatfs/ARM7/rtfs_naming_convention.h @@ -0,0 +1,57 @@ + +#ifndef __RTFS_NAMING_CONVENTION__ +#define __RTFS_NAMING_CONVENTION__ + + +/*#define original-name centrair-name*/ +#define pc_ertfs_init rtfs_pc_ertfs_init +#define pc_ertfs_config rtfs_pc_ertfs_config +#define pc_free_user rtfs_pc_free_user +#define pc_set_cwd rtfs_pc_set_cwd +#define pc_set_default_drive rtfs_pc_set_default_drive +#define pc_pwd rtfs_pc_pwd +#define pc_gfirst rtfs_pc_gfirst +#define pc_gnext rtfs_pc_gnext +#define pc_gdone rtfs_pc_gdone +#define pc_enumerate rtfs_pc_enumerate +#define get_errno rtfs_get_errno +#define rtfs_set_driver_errno rtfs_rtfs_set_driver_errno +#define rtfs_get_driver_errno rtfs_rtfs_get_driver_errno +#define pc_free rtfs_pc_free +#define pc_get_attributes rtfs_pc_get_attributes +#define pc_isdir rtfs_pc_isdir +#define pc_isvol rtfs_pc_isvol +#define pc_stat rtfs_pc_stat +#define pc_fstat rtfs_pc_fstat +#define pc_check_disk rtfs_pc_check_disk +#define pc_mkdir rtfs_pc_mkdir +#define pc_mv rtfs_pc_mv +#define pc_rmdir rtfs_pc_rmdir +#define pc_deltree rtfs_pc_deltree +#define pc_set_attributes rtfs_pc_set_attributes +#define pc_unlink rtfs_pc_unlink +#define pc_diskflush rtfs_pc_diskflush +#define po_open rtfs_po_open +#define po_close rtfs_po_close +#define po_flush rtfs_po_flush +#define po_read rtfs_po_read +#define po_write rtfs_po_write +#define po_lseek rtfs_po_lseek +#define po_useek rtfs_po_useek +#define po_chsize rtfs_po_chsize +#define po_truncate rtfs_po_truncate +#define pc_get_media_parms rtfs_pc_get_media_parms +#define pc_partition_media rtfs_pc_partition_media +#define pc_format_media rtfs_pc_format_media +#define pc_format_volume rtfs_pc_format_volume +#define pc_cluster_size rtfs_pc_cluster_size +#define pc_get_file_extents rtfs_pc_get_file_extents +#define pc_get_free_list rtfs_pc_get_free_list +#define pc_raw_read rtfs_pc_raw_read +#define pc_raw_write rtfs_pc_raw_write +#define po_extend_file rtfs_po_extend_file +#define pc_regression_test rtfs_pc_regression_test +#define tst_shell rtfs_tst_shell + + +#endif /*__RTFS_NAMING_CONVENTION__*/ diff --git a/include/twl/fatfs/ARM7/rtfs_target_os.h b/include/twl/fatfs/ARM7/rtfs_target_os.h new file mode 100644 index 0000000..325e25f --- /dev/null +++ b/include/twl/fatfs/ARM7/rtfs_target_os.h @@ -0,0 +1,74 @@ + +#ifndef __RTFS_TARGET_OS_H__ +#define __RTFS_TARGET_OS_H__ + + +/*********************************************************************** + ターゲットOS指定 +***********************************************************************/ +#define TARGET_OS_NITRO (1) +#define TARGET_OS_CTR (TARGET_OS_NITRO ^ 1) + + +/*********************************************************************** + NITRO OSのとき +***********************************************************************/ +#if (TARGET_OS_NITRO == 1) + +/* #if( DEBUG_PRINT_ON == 1) + #define PRINTDEBUG OS_TPrintf + #else + #define PRINTDEBUG( ...) ((void)0) + #endif +*/ + + #define OSAPI_CPUFILL8 MI_CpuFill8 + #define OSAPI_CPUCOPY8 MI_CpuCopy8 + #define OSAPI_MALLOC OS_Alloc + #define OSAPI_FREE OS_Free + #define OSAPI_STRLEN STD_GetStringLength + #define OSAPI_STRNCMP STD_CompareNString + #define OSAPI_STRCMP STD_CompareString + #define OSAPI_FLUSHCACHEALL DC_FlushAll + #define OSAPI_WAITCACHEBUF DC_WaitWriteBufferEmpty + #define OSAPI_ENABLEINTR OS_EnableInterrupts + #define OSAPI_DISABLEINTR OS_DisableInterrupts + #define OSAPI_RESTOREINTR OS_RestoreInterrupts + #define OSAPI_RTCINIT RTC_Init + + +/*********************************************************************** + CTR OSのとき +***********************************************************************/ +#else + + #if (DEBUG_PRINT_ON == 1) + #if (CTR_DEF_ENVIRONMENT_DSEMU == 1) + #define PRINTDEBUG osTPrintf + #else + #include + #define PRINTDEBUG vlink_dos_printf + #endif + #else + #define PRINTDEBUG( ...) ((void)0) + #endif + + + #define OSAPI_CPUFILL8 miCpuFill8 + #define OSAPI_CPUCOPY8 miCpuCopy8 + #define OSAPI_MALLOC i_elAlloc + #define OSAPI_FREE i_elFree + #define OSAPI_STRLEN strlen + #define OSAPI_STRNCMP strncmp + #define OSAPI_STRCMP strcmp + #define OSAPI_FLUSHCACHEALL osFlushDCacheAll + #define OSAPI_WAITCACHEBUF osWaitWriteBufferEmpty + #define OSAPI_ENABLEINTR osEnableInterrupts + #define OSAPI_DISABLEINTR osDisableInterrupts + #define OSAPI_RESTOREINTR osRestoreInterrupts + #define OSAPI_RTCINIT rtcInit + + +#endif + +#endif /*__RTFS_TARGET_OS_H__*/ diff --git a/include/twl/fatfs/ARM7/rtfsapi.h b/include/twl/fatfs/ARM7/rtfsapi.h new file mode 100644 index 0000000..2a41e38 --- /dev/null +++ b/include/twl/fatfs/ARM7/rtfsapi.h @@ -0,0 +1,25 @@ +/***************************************************************************** +*Filename: RTFSAPI.H - Public declarations for the rtfs API set +* +* +* EBS - RTFSAPI (Real Time File Manager) +* +* Copyright Peter Van Oudenaren , 1993 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* +* +* Description: +* +* +* +* +****************************************************************************/ + +#ifndef __RTFSAPI__ +#define __RTFSAPI__ 1 +#include +#endif + diff --git a/include/twl/fatfs/ARM7/rtfsconf.h b/include/twl/fatfs/ARM7/rtfsconf.h new file mode 100644 index 0000000..4654bae --- /dev/null +++ b/include/twl/fatfs/ARM7/rtfsconf.h @@ -0,0 +1,153 @@ +/***************************************************************************** +*Filename: RTFSCONF.H - RTFS tuning constants +* +* +* EBS - RTFS (Real Time File Manager) +* +* Copyright Peter Van Oudenaren, 1993 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* Description: +* This file contains tuning constants for configuring RTFS. +* It is included by rtfs.h +* +****************************************************************************/ + +#ifndef __RTFSCONF__ +#define __RTFSCONF__ 1 + +/* Include CPU and peripheral configuration */ +#include + +/* Character set support */ +#define INCLUDE_CS_JIS 0 /* Set to 1 to support JIS (kanji) */ +#define INCLUDE_CS_ASCII 1 /* Set to 1 to support ASCII only */ +#define INCLUDE_CS_UNICODE 0 /* Set to 1 to support unicode characters requires VFAT */ + +/* Note: After we implemented VFAT we learned that Microsoft patented + the Win95 VFS implementation. US PATENT # 5,758,352. + Leaving VFAT set to zero will exclude potential patent infringment + problems. + 3-19-99 +*/ + +/* Set to 1 to support long filenames */ +#define VFAT 1 +/* Set to 1 to support 32 bit FATs */ +#define FAT32 1 +/* Set to 0 to disable file share modes saves ~0.5 K */ +#define RTFS_SHARE 0 +/* Set to 0 to disable subdirs. Feature not implemented must be 1*/ +#define RTFS_SUBDIRS 1 +/* Set to 0 to disable write support. Feature not implemented must be 1*/ +#define RTFS_WRITE 1 +/* Set to 1 to include failsafe support */ +#define INCLUDE_FAILSAFE_CODE 1 + +/* Set to 1 to include support for extended DOS partitions */ +/* ERTFS contains code to interpret extended DOS partitions but since this + feature is rarely used it is provided as a compile time option */ +#define SUPPORT_EXTENDED_PARTITIONS 0 + +/* STORE_DEVICE_NAMES_IN_DRIVE_STRUCT - If this value is set to one then + we save device names for future viewing by diagnostics */ +#define STORE_DEVICE_NAMES_IN_DRIVE_STRUCT 1 + +/* Set to the maximum file size ERTFS may create. If po_chsize or po_extend_file() + are called with a size request larger than this they fail and set errno + to PETOOLARGE. When po_write() is asked to expend the file beyond this maximum + the behavior is determined by the value of RTFS_TRUNCATE_WRITE_TO_MAX */ +#define RTFS_MAX_FILE_SIZE 0xffffffff +/* #define RTFS_MAX_FILE_SIZE 0x80000000 */ +/* Set to 1 to force RTFS to truncate po_write() requests to fit within + RTFS_MAX_FILE_SIZE. If RTFS_TRUNCATE_WRITE_TO_MAX is set to 0, po_write + requests that attempt to extend the file beyond RTFS_TRUNCATE_WRITE_TO_MAX + Fail and set errno to PETOOLARGE. If RTFS_TRUNCATE_WRITE_TO_MAX is set to + 1, po_write requests that attempt to extend the file beyond + RTFS_MAX_FILE_SIZE are truncated to fill the file until its + size reaches RTFS_MAX_FILE_SIZE bytes. */ +#define RTFS_TRUNCATE_WRITE_TO_MAX 1 + + + +#if (VFAT) +#define FILENAMESIZE_CHARS 255 +#else +#if (INCLUDE_CS_UNICODE) +#error - Unicode requires VFAT +#endif +#define FILENAMESIZE_CHARS 8 +#endif + + +#if (VFAT) +#define EMAXPATH_CHARS 260 /* Maximum path length. Change if you like */ +#else +#define EMAXPATH_CHARS 148 /* Maximum path length. Change if you like */ +#endif + +/* Declare buffer sizes, leave room for terminating NULLs, allign to + four bytes for good form. */ +#if (VFAT) +#if (INCLUDE_CS_UNICODE || INCLUDE_CS_JIS) +#define EMAXPATH_BYTES 524 +#define FILENAMESIZE_BYTES 512 +#else +#define EMAXPATH_BYTES 264 +#define FILENAMESIZE_BYTES 256 +#endif +#else /* Not VFAT */ +#if (INCLUDE_CS_UNICODE || INCLUDE_CS_JIS) +#define EMAXPATH_BYTES 300 +#define FILENAMESIZE_BYTES 20 +#else +#define EMAXPATH_BYTES 152 +#define FILENAMESIZE_BYTES 12 +#endif +#endif + +/* When scanning a directory cluster chain fail if more than this many + clusters are in the chain. (Indicates endless loop) +*/ +#define MAX_CLUSTERS_PER_DIR 4096 + + +/* Make sure a character set is enabled */ +#if (INCLUDE_CS_JIS) +#if (INCLUDE_CS_UNICODE||INCLUDE_CS_ASCII) +#error Only one character set may be selected +#endif +#elif (INCLUDE_CS_UNICODE) +#if (INCLUDE_CS_JIS||INCLUDE_CS_ASCII) +#error Only one character set may be selected +#endif +#elif (INCLUDE_CS_ASCII) +#if (INCLUDE_CS_UNICODE||INCLUDE_CS_JIS) +#error Only one character set may be selected +#endif +#else +#error At least one character set must be selected +#endif + + +/*--- ctr modified ---*/ +#define RTFS_DEBUG_PRINT_ON (0) +/*--- ctr modified end ---*/ + + +/******************************************************************** + TYPES +********************************************************************/ + +#define TRUE 1 /* Don't change */ +#define FALSE 0 /* Don't change */ + +typedef unsigned char byte; /* Don't change */ +typedef unsigned short word; /* Don't change */ +typedef unsigned long dword; /* Don't change */ +/* typedef int BOOLEAN; Don't change */ +#define BOOLEAN int + +#endif /* __RTFSCONF__ */ diff --git a/include/twl/fatfs/ARM7/rtfspro.h b/include/twl/fatfs/ARM7/rtfspro.h new file mode 100644 index 0000000..0903447 --- /dev/null +++ b/include/twl/fatfs/ARM7/rtfspro.h @@ -0,0 +1,53 @@ +/***************************************************************************** +*Filename: RTFSPRO.H - Defines & structures for RTFSPRO Enhancements +* +* +* EBS - RTFS (Real Time File Manager) +* +* Copyright Peter Van Oudenaren , 2004 +* All rights reserved. +* This code may not be redistributed in source or linkable object form +* without the consent of its author. +* +* +* +* Description: +* +* This file is automatically included in rtfs.h if INCLUDE_RTFS_PRO is enabled +* this file is note intended for inclusion by user code. +* +* +****************************************************************************/ + +#ifndef __RTFSPRO__ +#define __RTFSPRO__ 1 + +struct pro_buffer_stats { + /* Informational fields. These are filled in by a call to ?? */ +int failsafe_mode; +dword failsafe_blocks_used; +dword failsafe_blocks_free; +dword total_block_buffers; +dword block_buffers_pending; +dword block_buffers_available; +dword block_buffers_fail; +dword block_buffers_low; +dword block_buffers_cache_hits; +dword block_buffers_cache_misses; +dword fat_buffers_pending; +dword fat_buffers_free; +dword fat_buffers_available; +dword fat_buffer_primary_cache_hits; +dword fat_buffer_secondary_cache_hits; +dword fat_buffer_cache_loads; +dword fat_buffer_cache_swaps; +dword total_fat_buffers; +}; + +BOOLEAN pro_buffer_status(byte *drive_name, struct pro_buffer_stats *fsstat); + +BOOLEAN pro_assign_buffers(byte *drivename, BLKBUFFCNTXT *block_buffer_context, + int block_hashtable_size, BLKBUFF **block_hash_table, + int block_buffer_pool_size, BLKBUFF *block_buffer_pool_data); +#endif +