mirror of
https://github.com/wiiu-env/libfat.git
synced 2024-11-22 18:09:17 +01:00
Moved disc interfaces to appropriate libraries (DLDI to libnds, SD/USB to libogc).
Changed read/write functions to use size_t and seek function to use off_t -- will help on 64 bit platforms. Partitions no longer specified by fat1:/, fat2:/, etc. Use "nice" device names. On GBA/NDS the default partition is still fat:/; however on Wii and GC sd:/, usb:/, etc. are used instead. Added fsync and ftruncate functions. Mount and unmount no longer startup/shutdown the device, this is the job of the host app. Mount now accepts a partition start parameter. Used standard types, eg uint32_t instead of u32. Archive bit is set when closing/synchronizing a modified file. Various minor bug fixes.
This commit is contained in:
parent
841c1a692c
commit
2cc2cc2e39
@ -17,7 +17,7 @@ include $(DEVKITARM)/gba_rules
|
|||||||
# all directories are relative to this makefile
|
# all directories are relative to this makefile
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
BUILD ?= release
|
BUILD ?= release
|
||||||
SOURCES := ../source ../source/disc_io
|
SOURCES := ../source
|
||||||
INCLUDES := ../include
|
INCLUDES := ../include
|
||||||
DATA :=
|
DATA :=
|
||||||
LIB := $(TOPDIR)/gba/lib
|
LIB := $(TOPDIR)/gba/lib
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -35,11 +34,8 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "gba_types.h"
|
#include <stdint.h>
|
||||||
|
#include "disc_io.h"
|
||||||
typedef enum {PI_CART_SLOT} PARTITION_INTERFACE;
|
|
||||||
|
|
||||||
struct IO_INTERFACE_STRUCT ;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialise any inserted block-devices.
|
Initialise any inserted block-devices.
|
||||||
@ -47,45 +43,36 @@ Add the fat device driver to the devoptab, making it available for standard file
|
|||||||
cacheSize: The number of pages to allocate for each inserted block-device
|
cacheSize: The number of pages to allocate for each inserted block-device
|
||||||
setAsDefaultDevice: if true, make this the default device driver for file operations
|
setAsDefaultDevice: if true, make this the default device driver for file operations
|
||||||
*/
|
*/
|
||||||
bool fatInit (u32 cacheSize, bool setAsDefaultDevice);
|
extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
||||||
*/
|
*/
|
||||||
bool fatInitDefault (void);
|
extern bool fatInitDefault (void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount the device specified by partitionNumber
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
|
You can then access the filesystem using "name:/".
|
||||||
PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
|
This will mount the active partition or the first valid partition on the disc,
|
||||||
|
and will use a cache size optimized for the host system.
|
||||||
*/
|
*/
|
||||||
bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
|
extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount a partition on a custom device
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
|
You can then access the filesystem using "name:/".
|
||||||
|
If startSector = 0, it will mount the active partition of the first valid partition on
|
||||||
|
the disc. Otherwise it will try to mount the partition starting at startSector.
|
||||||
|
cacheSize specifies the number of pages to allocate for the cache.
|
||||||
|
This will not startup the disc, so you need to call interface->startup(); first.
|
||||||
*/
|
*/
|
||||||
bool fatMountCustomInterface (struct IO_INTERFACE_STRUCT* device, u32 cacheSize);
|
extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Unmount the partition specified by partitionNumber
|
Unmount the partition specified by name.
|
||||||
If there are open files, it will fail
|
If there are open files, it will attempt to synchronise them to disc.
|
||||||
*/
|
*/
|
||||||
bool fatUnmount (PARTITION_INTERFACE partitionNumber);
|
extern void fatUnmount (const char* name);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Forcibly unmount the partition specified by partitionNumber
|
|
||||||
Any open files on the partition will become invalid
|
|
||||||
The cache will be invalidated, and any unflushed writes will be lost
|
|
||||||
*/
|
|
||||||
bool fatUnsafeUnmount (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Set the default device for access by fat: and fat0:
|
|
||||||
PD_DEFAULT is unallowed.
|
|
||||||
Doesn't do anything useful on GBA, since there is only one device
|
|
||||||
*/
|
|
||||||
bool fatSetDefaultInterface (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -24,15 +24,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-07-14
|
|
||||||
* fatInitialise renamed to fatInit
|
|
||||||
|
|
||||||
2006-07-16 - Chishm
|
|
||||||
* Added fatInitDefault
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -50,73 +41,54 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#if defined(__gamecube__) || defined (__wii__)
|
#if defined(__gamecube__) || defined (__wii__)
|
||||||
# include <gctypes.h>
|
# include <ogc/disc_io.h>
|
||||||
#else
|
#else
|
||||||
# ifdef NDS
|
# ifdef NDS
|
||||||
# include <nds/ndstypes.h>
|
# include "nds/disc_io.h"
|
||||||
# else
|
# else
|
||||||
# include "gba_types.h"
|
# include "disc_io.h"
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined( __wii__ )
|
|
||||||
typedef enum {PI_DEFAULT, PI_SDGECKO_A, PI_SDGECKO_B, PI_INTERNAL_SD, PI_CUSTOM, PI_MAX_PARTITIONS } PARTITION_INTERFACE;
|
|
||||||
#elif defined(__gamecube__)
|
|
||||||
typedef enum {PI_DEFAULT, PI_SDGECKO_A, PI_SDGECKO_B, PI_CUSTOM, PI_MAX_PARTITIONS } PARTITION_INTERFACE;
|
|
||||||
#elif defined(NDS)
|
|
||||||
typedef enum {PI_DEFAULT, PI_SLOT_1, PI_SLOT_2, PI_CUSTOM, PI_MAX_PARTITIONS} PARTITION_INTERFACE;
|
|
||||||
#elif defined(GBA)
|
|
||||||
typedef enum {PI_CART_SLOT, PI_MAX_PARTITIONS} PARTITION_INTERFACE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct IO_INTERFACE_STRUCT ;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialise any inserted block-devices.
|
Initialise any inserted block-devices.
|
||||||
Add the fat device driver to the devoptab, making it available for standard file functions.
|
Add the fat device driver to the devoptab, making it available for standard file functions.
|
||||||
cacheSize: The number of pages to allocate for each inserted block-device
|
cacheSize: The number of pages to allocate for each inserted block-device
|
||||||
setAsDefaultDevice: if true, make this the default device driver for file operations
|
setAsDefaultDevice: if true, make this the default device driver for file operations
|
||||||
*/
|
*/
|
||||||
bool fatInit (u32 cacheSize, bool setAsDefaultDevice);
|
extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
||||||
*/
|
*/
|
||||||
bool fatInitDefault (void);
|
extern bool fatInitDefault (void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount the device specified by partitionNumber
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
|
You can then access the filesystem using "name:/".
|
||||||
PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
|
This will mount the active partition or the first valid partition on the disc,
|
||||||
|
and will use a cache size optimized for the host system.
|
||||||
*/
|
*/
|
||||||
bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
|
extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount a partition on a custom device
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
|
You can then access the filesystem using "name:/".
|
||||||
|
If startSector = 0, it will mount the active partition of the first valid partition on
|
||||||
|
the disc. Otherwise it will try to mount the partition starting at startSector.
|
||||||
|
cacheSize specifies the number of pages to allocate for the cache.
|
||||||
|
This will not startup the disc, so you need to call interface->startup(); first.
|
||||||
*/
|
*/
|
||||||
bool fatMountCustomInterface (const struct IO_INTERFACE_STRUCT* device, u32 cacheSize);
|
extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Unmount the partition specified by partitionNumber
|
Unmount the partition specified by name.
|
||||||
If there are open files, it will fail
|
If there are open files, it will attempt to synchronise them to disc.
|
||||||
*/
|
*/
|
||||||
bool fatUnmount (PARTITION_INTERFACE partitionNumber);
|
extern void fatUnmount (const char* name);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Forcibly unmount the partition specified by partitionNumber
|
|
||||||
Any open files on the partition will become invalid
|
|
||||||
The cache will be invalidated, and any unflushed writes will be lost
|
|
||||||
*/
|
|
||||||
bool fatUnsafeUnmount (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Set the default device for access by fat: and fat0:
|
|
||||||
PD_DEFAULT is unallowed.
|
|
||||||
Doesn't do anything useful on GBA, since there is only one device
|
|
||||||
*/
|
|
||||||
bool fatSetDefaultInterface (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ endif
|
|||||||
# all directories are relative to this makefile
|
# all directories are relative to this makefile
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
BUILD ?= wii_release
|
BUILD ?= wii_release
|
||||||
SOURCES := ../source ../source/ogc_io
|
SOURCES := ../source
|
||||||
INCLUDES := ../include
|
INCLUDES := ../include
|
||||||
DATA :=
|
DATA :=
|
||||||
LIBDIR := $(TOPDIR)/libogc/lib
|
LIBDIR := $(TOPDIR)/libogc/lib
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -35,15 +34,8 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gctypes.h>
|
#include <stdint.h>
|
||||||
|
#include <ogc/disc_io.h>
|
||||||
#if defined(__wii__)
|
|
||||||
typedef enum {PI_DEFAULT, PI_SDGECKO_A, PI_SDGECKO_B, PI_INTERNAL_SD, PI_CUSTOM } PARTITION_INTERFACE;
|
|
||||||
#else
|
|
||||||
typedef enum {PI_DEFAULT, PI_SDGECKO_A, PI_SDGECKO_B, PI_CUSTOM } PARTITION_INTERFACE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct IO_INTERFACE_STRUCT ;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialise any inserted block-devices.
|
Initialise any inserted block-devices.
|
||||||
@ -51,45 +43,36 @@ Add the fat device driver to the devoptab, making it available for standard file
|
|||||||
cacheSize: The number of pages to allocate for each inserted block-device
|
cacheSize: The number of pages to allocate for each inserted block-device
|
||||||
setAsDefaultDevice: if true, make this the default device driver for file operations
|
setAsDefaultDevice: if true, make this the default device driver for file operations
|
||||||
*/
|
*/
|
||||||
bool fatInit (u32 cacheSize, bool setAsDefaultDevice);
|
extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
||||||
*/
|
*/
|
||||||
bool fatInitDefault (void);
|
extern bool fatInitDefault (void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount the device specified by partitionNumber
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
|
You can then access the filesystem using "name:/".
|
||||||
PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
|
This will mount the active partition or the first valid partition on the disc,
|
||||||
|
and will use a cache size optimized for the host system.
|
||||||
*/
|
*/
|
||||||
bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
|
extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount a partition on a custom device
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
|
You can then access the filesystem using "name:/".
|
||||||
|
If startSector = 0, it will mount the active partition of the first valid partition on
|
||||||
|
the disc. Otherwise it will try to mount the partition starting at startSector.
|
||||||
|
cacheSize specifies the number of pages to allocate for the cache.
|
||||||
|
This will not startup the disc, so you need to call interface->startup(); first.
|
||||||
*/
|
*/
|
||||||
bool fatMountCustomInterface (struct IO_INTERFACE_STRUCT* device, u32 cacheSize);
|
extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Unmount the partition specified by partitionNumber
|
Unmount the partition specified by name.
|
||||||
If there are open files, it will fail
|
If there are open files, it will attempt to synchronise them to disc.
|
||||||
*/
|
*/
|
||||||
bool fatUnmount (PARTITION_INTERFACE partitionNumber);
|
extern void fatUnmount (const char* name);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Forcibly unmount the partition specified by partitionNumber
|
|
||||||
Any open files on the partition will become invalid
|
|
||||||
The cache will be invalidated, and any unflushed writes will be lost
|
|
||||||
*/
|
|
||||||
bool fatUnsafeUnmount (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Set the default device for access by fat: and fat0:
|
|
||||||
PD_DEFAULT is unallowed.
|
|
||||||
Doesn't do anything useful on GBA, since there is only one device
|
|
||||||
*/
|
|
||||||
bool fatSetDefaultInterface (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ include $(DEVKITARM)/ds_rules
|
|||||||
# all directories are relative to this makefile
|
# all directories are relative to this makefile
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
BUILD ?= release
|
BUILD ?= release
|
||||||
SOURCES := ../source ../source/disc_io source
|
SOURCES := ../source source
|
||||||
INCLUDES := ../include
|
INCLUDES := ../include
|
||||||
DATA :=
|
DATA :=
|
||||||
LIB := $(TOPDIR)/nds/lib
|
LIB := $(TOPDIR)/nds/lib
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -40,11 +39,8 @@ extern "C" {
|
|||||||
#define NDS
|
#define NDS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <nds/ndstypes.h>
|
#include <stdint.h>
|
||||||
|
#include "nds/disc_io.h"
|
||||||
typedef enum {PI_DEFAULT, PI_SLOT_1, PI_SLOT_2, PI_CUSTOM} PARTITION_INTERFACE;
|
|
||||||
|
|
||||||
struct IO_INTERFACE_STRUCT ;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialise any inserted block-devices.
|
Initialise any inserted block-devices.
|
||||||
@ -52,45 +48,36 @@ Add the fat device driver to the devoptab, making it available for standard file
|
|||||||
cacheSize: The number of pages to allocate for each inserted block-device
|
cacheSize: The number of pages to allocate for each inserted block-device
|
||||||
setAsDefaultDevice: if true, make this the default device driver for file operations
|
setAsDefaultDevice: if true, make this the default device driver for file operations
|
||||||
*/
|
*/
|
||||||
bool fatInit (u32 cacheSize, bool setAsDefaultDevice);
|
extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
||||||
*/
|
*/
|
||||||
bool fatInitDefault (void);
|
extern bool fatInitDefault (void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount the device specified by partitionNumber
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
|
You can then access the filesystem using "name:/".
|
||||||
PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
|
This will mount the active partition or the first valid partition on the disc,
|
||||||
|
and will use a cache size optimized for the host system.
|
||||||
*/
|
*/
|
||||||
bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
|
extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount a partition on a custom device
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
|
You can then access the filesystem using "name:/".
|
||||||
|
If startSector = 0, it will mount the active partition of the first valid partition on
|
||||||
|
the disc. Otherwise it will try to mount the partition starting at startSector.
|
||||||
|
cacheSize specifies the number of pages to allocate for the cache.
|
||||||
|
This will not startup the disc, so you need to call interface->startup(); first.
|
||||||
*/
|
*/
|
||||||
bool fatMountCustomInterface (struct IO_INTERFACE_STRUCT* device, u32 cacheSize);
|
extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Unmount the partition specified by partitionNumber
|
Unmount the partition specified by name.
|
||||||
If there are open files, it will fail
|
If there are open files, it will attempt to synchronise them to disc.
|
||||||
*/
|
*/
|
||||||
bool fatUnmount (PARTITION_INTERFACE partitionNumber);
|
extern void fatUnmount (const char* name);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Forcibly unmount the partition specified by partitionNumber
|
|
||||||
Any open files on the partition will become invalid
|
|
||||||
The cache will be invalidated, and any unflushed writes will be lost
|
|
||||||
*/
|
|
||||||
bool fatUnsafeUnmount (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Set the default device for access by fat: and fat0:
|
|
||||||
PD_DEFAULT is unallowed.
|
|
||||||
Doesn't do anything useful on GBA, since there is only one device
|
|
||||||
*/
|
|
||||||
bool fatSetDefaultInterface (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,8 @@ int nitroFSDirNext(struct _reent *r, DIR_ITER *dirState, char *filename, struct
|
|||||||
int nitroFSDirClose(struct _reent *r, DIR_ITER *dirState);
|
int nitroFSDirClose(struct _reent *r, DIR_ITER *dirState);
|
||||||
int nitroFSOpen(struct _reent *r, void *fileStruct, const char *path,int flags,int mode);
|
int nitroFSOpen(struct _reent *r, void *fileStruct, const char *path,int flags,int mode);
|
||||||
int nitroFSClose(struct _reent *r,int fd);
|
int nitroFSClose(struct _reent *r,int fd);
|
||||||
int nitroFSRead(struct _reent *r,int fd,char *ptr,int len);
|
ssize_t nitroFSRead(struct _reent *r,int fd,char *ptr,size_t len);
|
||||||
int nitroFSSeek(struct _reent *r,int fd,int pos,int dir);
|
off_t nitroFSSeek(struct _reent *r,int fd,off_t pos,int dir);
|
||||||
int nitroFSFstat(struct _reent *r,int fd,struct stat *st);
|
int nitroFSFstat(struct _reent *r,int fd,struct stat *st);
|
||||||
|
|
||||||
#define LOADERSTR "PASS" //look for this
|
#define LOADERSTR "PASS" //look for this
|
||||||
@ -81,9 +81,9 @@ devoptab_t nitroFSdevoptab={
|
|||||||
sizeof(struct nitroFSStruct), // int structSize;
|
sizeof(struct nitroFSStruct), // int structSize;
|
||||||
&nitroFSOpen, // int (*open_r)(struct _reent *r, void *fileStruct, const char *path,int flags,int mode);
|
&nitroFSOpen, // int (*open_r)(struct _reent *r, void *fileStruct, const char *path,int flags,int mode);
|
||||||
&nitroFSClose, // int (*close_r)(struct _reent *r,int fd);
|
&nitroFSClose, // int (*close_r)(struct _reent *r,int fd);
|
||||||
NULL, // int (*write_r)(struct _reent *r,int fd,const char *ptr,int len);
|
NULL, // ssize_t (*write_r)(struct _reent *r,int fd,const char *ptr,size_t len);
|
||||||
&nitroFSRead, // int (*read_r)(struct _reent *r,int fd,char *ptr,int len);
|
&nitroFSRead, // ssize_t (*read_r)(struct _reent *r,int fd,char *ptr,size_t len);
|
||||||
&nitroFSSeek, // int (*seek_r)(struct _reent *r,int fd,int pos,int dir);
|
&nitroFSSeek, // off_t (*seek_r)(struct _reent *r,int fd,off_t pos,int dir);
|
||||||
&nitroFSFstat, // int (*fstat_r)(struct _reent *r,int fd,struct stat *st);
|
&nitroFSFstat, // int (*fstat_r)(struct _reent *r,int fd,struct stat *st);
|
||||||
NULL, // int (*stat_r)(struct _reent *r,const char *file,struct stat *st);
|
NULL, // int (*stat_r)(struct _reent *r,const char *file,struct stat *st);
|
||||||
NULL, // int (*link_r)(struct _reent *r,const char *existing, const char *newLink);
|
NULL, // int (*link_r)(struct _reent *r,const char *existing, const char *newLink);
|
||||||
@ -97,8 +97,11 @@ devoptab_t nitroFSdevoptab={
|
|||||||
&nitroFSDirOpen, // DIR_ITER* (*diropen_r)(struct _reent *r, DIR_ITER *dirState, const char *path);
|
&nitroFSDirOpen, // DIR_ITER* (*diropen_r)(struct _reent *r, DIR_ITER *dirState, const char *path);
|
||||||
&nitroDirReset, // int (*dirreset_r)(struct _reent *r, DIR_ITER *dirState);
|
&nitroDirReset, // int (*dirreset_r)(struct _reent *r, DIR_ITER *dirState);
|
||||||
&nitroFSDirNext, // int (*dirnext_r)(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
&nitroFSDirNext, // int (*dirnext_r)(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
||||||
&nitroFSDirClose // int (*dirclose_r)(struct _reent *r, DIR_ITER *dirState);
|
&nitroFSDirClose, // int (*dirclose_r)(struct _reent *r, DIR_ITER *dirState);
|
||||||
|
NULL, // int (*statvfs_r)(struct _reent *r, const char *path, struct statvfs *buf);
|
||||||
|
NULL, // int (*ftruncate_r)(struct _reent *r, int fd, off_t len);
|
||||||
|
NULL, // int (*fsync_r)(struct _reent *r, int fd);
|
||||||
|
NULL, // void *deviceData;
|
||||||
};
|
};
|
||||||
|
|
||||||
//inline these mebbe? these 4 'sub' functions deal with actually reading from either gba rom or .nds file :)
|
//inline these mebbe? these 4 'sub' functions deal with actually reading from either gba rom or .nds file :)
|
||||||
@ -340,7 +343,7 @@ int nitroFSClose(struct _reent *r,int fd) {
|
|||||||
return(nitroSubClose(&((struct nitroFSStruct *)fd)->nrs));
|
return(nitroSubClose(&((struct nitroFSStruct *)fd)->nrs));
|
||||||
}
|
}
|
||||||
|
|
||||||
int nitroFSRead(struct _reent *r,int fd,char *ptr,int len) {
|
ssize_t nitroFSRead(struct _reent *r,int fd,char *ptr,size_t len) {
|
||||||
struct nitroFSStruct *fatStruct=(struct nitroFSStruct *)fd;
|
struct nitroFSStruct *fatStruct=(struct nitroFSStruct *)fd;
|
||||||
struct nitroRawStruct *nrs=&((struct nitroFSStruct *)fd)->nrs;
|
struct nitroRawStruct *nrs=&((struct nitroFSStruct *)fd)->nrs;
|
||||||
if(nrs->pos+len > fatStruct->end)
|
if(nrs->pos+len > fatStruct->end)
|
||||||
@ -350,7 +353,7 @@ int nitroFSRead(struct _reent *r,int fd,char *ptr,int len) {
|
|||||||
return(nitroSubRead(nrs,ptr,len));
|
return(nitroSubRead(nrs,ptr,len));
|
||||||
}
|
}
|
||||||
|
|
||||||
int nitroFSSeek(struct _reent *r,int fd,int pos,int dir) {
|
off_t nitroFSSeek(struct _reent *r,int fd ,off_t pos,int dir) {
|
||||||
//need check for eof here...
|
//need check for eof here...
|
||||||
struct nitroFSStruct *fatStruct=(struct nitroFSStruct *)fd;
|
struct nitroFSStruct *fatStruct=(struct nitroFSStruct *)fd;
|
||||||
struct nitroRawStruct *nrs=&((struct nitroFSStruct *)fd)->nrs;
|
struct nitroRawStruct *nrs=&((struct nitroFSStruct *)fd)->nrs;
|
||||||
|
@ -24,35 +24,34 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _BIT_OPS_H
|
#ifndef _BIT_OPS_H
|
||||||
#define _BIT_OPS_H
|
#define _BIT_OPS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
/*-----------------------------------------------------------------
|
||||||
Functions to deal with little endian values stored in u8 arrays
|
Functions to deal with little endian values stored in uint8_t arrays
|
||||||
-----------------------------------------------------------------*/
|
-----------------------------------------------------------------*/
|
||||||
static inline u16 u8array_to_u16 (const u8* item, int offset) {
|
static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) {
|
||||||
return ( item[offset] | (item[offset + 1] << 8));
|
return ( item[offset] | (item[offset + 1] << 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 u8array_to_u32 (const u8* item, int offset) {
|
static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) {
|
||||||
return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
|
return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void u16_to_u8array (u8* item, int offset, u16 value) {
|
static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) {
|
||||||
item[offset] = (u8)value;
|
item[offset] = (uint8_t) value;
|
||||||
item[offset + 1] = (u8)(value >> 8);
|
item[offset + 1] = (uint8_t)(value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void u32_to_u8array (u8* item, int offset, u32 value) {
|
static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) {
|
||||||
item[offset] = (u8)value;
|
item[offset] = (uint8_t) value;
|
||||||
item[offset + 1] = (u8)(value >> 8);
|
item[offset + 1] = (uint8_t)(value >> 8);
|
||||||
item[offset + 2] = (u8)(value >> 16);
|
item[offset + 2] = (uint8_t)(value >> 16);
|
||||||
item[offset + 3] = (u8)(value >> 24);
|
item[offset + 3] = (uint8_t)(value >> 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _BIT_OPS_H
|
#endif // _BIT_OPS_H
|
||||||
|
@ -31,13 +31,10 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2007-11-14 - Chishm
|
|
||||||
* Fixed _FAT_cache_constructor to return NULL on error, not false
|
|
||||||
* Fixed _FAT_cache_flush to return false on error. With thanks to xorloser
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
@ -46,11 +43,11 @@
|
|||||||
#include "mem_allocate.h"
|
#include "mem_allocate.h"
|
||||||
#include "bit_ops.h"
|
#include "bit_ops.h"
|
||||||
|
|
||||||
#define CACHE_FREE 0xFFFFFFFF
|
#define CACHE_FREE UINT_MAX
|
||||||
|
|
||||||
CACHE* _FAT_cache_constructor (u32 numberOfPages, const IO_INTERFACE* discInterface) {
|
CACHE* _FAT_cache_constructor (unsigned int numberOfPages, const DISC_INTERFACE* discInterface) {
|
||||||
CACHE* cache;
|
CACHE* cache;
|
||||||
u32 i;
|
unsigned int i;
|
||||||
CACHE_ENTRY* cacheEntries;
|
CACHE_ENTRY* cacheEntries;
|
||||||
|
|
||||||
if (numberOfPages < 2) {
|
if (numberOfPages < 2) {
|
||||||
@ -80,7 +77,7 @@ CACHE* _FAT_cache_constructor (u32 numberOfPages, const IO_INTERFACE* discInterf
|
|||||||
|
|
||||||
cache->cacheEntries = cacheEntries;
|
cache->cacheEntries = cacheEntries;
|
||||||
|
|
||||||
cache->pages = (u8*) _FAT_mem_allocate ( CACHE_PAGE_SIZE * numberOfPages);
|
cache->pages = (uint8_t*) _FAT_mem_allocate ( CACHE_PAGE_SIZE * numberOfPages);
|
||||||
if (cache->pages == NULL) {
|
if (cache->pages == NULL) {
|
||||||
_FAT_mem_free (cache->cacheEntries);
|
_FAT_mem_free (cache->cacheEntries);
|
||||||
_FAT_mem_free (cache);
|
_FAT_mem_free (cache);
|
||||||
@ -107,13 +104,13 @@ Retrieve a sector's page from the cache. If it is not found in the cache,
|
|||||||
load it into the cache and return the page it was loaded to.
|
load it into the cache and return the page it was loaded to.
|
||||||
Return CACHE_FREE on error.
|
Return CACHE_FREE on error.
|
||||||
*/
|
*/
|
||||||
static u32 _FAT_cache_getSector (CACHE* cache, u32 sector) {
|
static unsigned int _FAT_cache_getSector (CACHE* cache, sec_t sector) {
|
||||||
u32 i;
|
unsigned int i;
|
||||||
CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
||||||
u32 numberOfPages = cache->numberOfPages;
|
unsigned int numberOfPages = cache->numberOfPages;
|
||||||
|
|
||||||
u32 leastUsed = 0;
|
unsigned int leastUsed = 0;
|
||||||
u32 lowestCount = 0xFFFFFFFF;
|
unsigned int lowestCount = UINT_MAX;
|
||||||
|
|
||||||
for (i = 0; (i < numberOfPages) && (cacheEntries[i].sector != sector); i++) {
|
for (i = 0; (i < numberOfPages) && (cacheEntries[i].sector != sector); i++) {
|
||||||
// While searching for the desired sector, also search for the leased used page
|
// While searching for the desired sector, also search for the leased used page
|
||||||
@ -153,8 +150,8 @@ static u32 _FAT_cache_getSector (CACHE* cache, u32 sector) {
|
|||||||
/*
|
/*
|
||||||
Reads some data from a cache page, determined by the sector number
|
Reads some data from a cache page, determined by the sector number
|
||||||
*/
|
*/
|
||||||
bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, u32 sector, u32 offset, u32 size) {
|
bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) {
|
||||||
u32 page;
|
unsigned int page;
|
||||||
|
|
||||||
if (offset + size > BYTES_PER_READ) {
|
if (offset + size > BYTES_PER_READ) {
|
||||||
return false;
|
return false;
|
||||||
@ -168,8 +165,8 @@ bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, u32 sector, u32 o
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _FAT_cache_readLittleEndianValue (CACHE* cache, u32 *value, u32 sector, u32 offset, u32 num_bytes) {
|
bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) {
|
||||||
u8 buf[4];
|
uint8_t buf[4];
|
||||||
if (!_FAT_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false;
|
if (!_FAT_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false;
|
||||||
|
|
||||||
switch(num_bytes) {
|
switch(num_bytes) {
|
||||||
@ -184,8 +181,8 @@ bool _FAT_cache_readLittleEndianValue (CACHE* cache, u32 *value, u32 sector, u32
|
|||||||
/*
|
/*
|
||||||
Writes some data to a cache page, making sure it is loaded into memory first.
|
Writes some data to a cache page, making sure it is loaded into memory first.
|
||||||
*/
|
*/
|
||||||
bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size) {
|
bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) {
|
||||||
u32 page;
|
unsigned int page;
|
||||||
|
|
||||||
if (offset + size > BYTES_PER_READ) {
|
if (offset + size > BYTES_PER_READ) {
|
||||||
return false;
|
return false;
|
||||||
@ -202,8 +199,8 @@ bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, u32 sector
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const u32 value, u32 sector, u32 offset, u32 size) {
|
bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) {
|
||||||
u8 buf[4] = {0, 0, 0, 0};
|
uint8_t buf[4] = {0, 0, 0, 0};
|
||||||
|
|
||||||
switch(size) {
|
switch(size) {
|
||||||
case 1: buf[0] = value; break;
|
case 1: buf[0] = value; break;
|
||||||
@ -218,8 +215,8 @@ bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const u32 value, u32 secto
|
|||||||
/*
|
/*
|
||||||
Writes some data to a cache page, zeroing out the page first
|
Writes some data to a cache page, zeroing out the page first
|
||||||
*/
|
*/
|
||||||
bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size) {
|
bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) {
|
||||||
u32 page;
|
unsigned int page;
|
||||||
|
|
||||||
if (offset + size > BYTES_PER_READ) {
|
if (offset + size > BYTES_PER_READ) {
|
||||||
return false;
|
return false;
|
||||||
@ -243,7 +240,7 @@ Flushes all dirty pages to disc, clearing the dirty flag.
|
|||||||
Also resets all pages' page count to 0.
|
Also resets all pages' page count to 0.
|
||||||
*/
|
*/
|
||||||
bool _FAT_cache_flush (CACHE* cache) {
|
bool _FAT_cache_flush (CACHE* cache) {
|
||||||
u32 i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < cache->numberOfPages; i++) {
|
for (i = 0; i < cache->numberOfPages; i++) {
|
||||||
if (cache->cacheEntries[i].dirty) {
|
if (cache->cacheEntries[i].dirty) {
|
||||||
@ -259,7 +256,7 @@ bool _FAT_cache_flush (CACHE* cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _FAT_cache_invalidate (CACHE* cache) {
|
void _FAT_cache_invalidate (CACHE* cache) {
|
||||||
int i;
|
unsigned int i;
|
||||||
for (i = 0; i < cache->numberOfPages; i++) {
|
for (i = 0; i < cache->numberOfPages; i++) {
|
||||||
cache->cacheEntries[i].sector = CACHE_FREE;
|
cache->cacheEntries[i].sector = CACHE_FREE;
|
||||||
cache->cacheEntries[i].count = 0;
|
cache->cacheEntries[i].count = 0;
|
||||||
|
@ -31,9 +31,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _CACHE_H
|
#ifndef _CACHE_H
|
||||||
@ -45,16 +42,16 @@
|
|||||||
#define CACHE_PAGE_SIZE BYTES_PER_READ
|
#define CACHE_PAGE_SIZE BYTES_PER_READ
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 sector;
|
sec_t sector;
|
||||||
u32 count;
|
unsigned int count;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
} CACHE_ENTRY;
|
} CACHE_ENTRY;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const IO_INTERFACE* disc;
|
const DISC_INTERFACE* disc;
|
||||||
u32 numberOfPages;
|
unsigned int numberOfPages;
|
||||||
CACHE_ENTRY* cacheEntries;
|
CACHE_ENTRY* cacheEntries;
|
||||||
u8* pages;
|
uint8_t* pages;
|
||||||
} CACHE;
|
} CACHE;
|
||||||
|
|
||||||
|
|
||||||
@ -65,43 +62,43 @@ offset is the position to start reading from
|
|||||||
size is the amount of data to read
|
size is the amount of data to read
|
||||||
Precondition: offset + size <= BYTES_PER_READ
|
Precondition: offset + size <= BYTES_PER_READ
|
||||||
*/
|
*/
|
||||||
bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, u32 sector, u32 offset, u32 size);
|
bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||||
|
|
||||||
bool _FAT_cache_readLittleEndianValue (CACHE* cache, u32 *value, u32 sector, u32 offset, u32 num_bytes);
|
bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Write data to a sector in the cache
|
Write data to a sector in the cache
|
||||||
If the sector is not in the cache, it will be swapped in.
|
If the sector is not in the cache, it will be swapped in.
|
||||||
When the sector is swapped out, the data will be written to the disc
|
When the sector is swapped out, the data will be written to the disc
|
||||||
offset is the position to start reading from
|
offset is the position to start writing to
|
||||||
size is the amount of data to read
|
size is the amount of data to write
|
||||||
Precondition: offset + size <= BYTES_PER_READ
|
Precondition: offset + size <= BYTES_PER_READ
|
||||||
*/
|
*/
|
||||||
bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size);
|
bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||||
|
|
||||||
bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const u32 value, u32 sector, u32 offset, u32 num_bytes);
|
bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Write data to a sector in the cache, zeroing the sector first
|
Write data to a sector in the cache, zeroing the sector first
|
||||||
If the sector is not in the cache, it will be swapped in.
|
If the sector is not in the cache, it will be swapped in.
|
||||||
When the sector is swapped out, the data will be written to the disc
|
When the sector is swapped out, the data will be written to the disc
|
||||||
offset is the position to start reading from
|
offset is the position to start writing to
|
||||||
size is the amount of data to read
|
size is the amount of data to write
|
||||||
Precondition: offset + size <= BYTES_PER_READ
|
Precondition: offset + size <= BYTES_PER_READ
|
||||||
*/
|
*/
|
||||||
bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size);
|
bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Read a full sector from the cache
|
Read a full sector from the cache
|
||||||
*/
|
*/
|
||||||
static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, u32 sector) {
|
static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, sec_t sector) {
|
||||||
return _FAT_cache_readPartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
|
return _FAT_cache_readPartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Write a full sector to the cache
|
Write a full sector to the cache
|
||||||
*/
|
*/
|
||||||
static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, u32 sector) {
|
static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec_t sector) {
|
||||||
return _FAT_cache_writePartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
|
return _FAT_cache_writePartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +112,7 @@ Clear out the contents of the cache without writing any dirty sectors first
|
|||||||
*/
|
*/
|
||||||
void _FAT_cache_invalidate (CACHE* cache);
|
void _FAT_cache_invalidate (CACHE* cache);
|
||||||
|
|
||||||
CACHE* _FAT_cache_constructor (u32 numberOfPages, const IO_INTERFACE* discInterface);
|
CACHE* _FAT_cache_constructor (unsigned int numberOfPages, const DISC_INTERFACE* discInterface);
|
||||||
|
|
||||||
void _FAT_cache_destructor (CACHE* cache);
|
void _FAT_cache_destructor (CACHE* cache);
|
||||||
|
|
||||||
|
@ -24,14 +24,16 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _COMMON_H
|
#ifndef _COMMON_H
|
||||||
#define _COMMON_H
|
#define _COMMON_H
|
||||||
|
|
||||||
|
#define BYTES_PER_READ 512
|
||||||
|
#include <fat.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
// When compiling for NDS, make sure NDS is defined
|
// When compiling for NDS, make sure NDS is defined
|
||||||
#ifndef NDS
|
#ifndef NDS
|
||||||
#if defined ARM9 || defined ARM7
|
#if defined ARM9 || defined ARM7
|
||||||
@ -39,24 +41,34 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Platform specific includes
|
||||||
#if defined(__gamecube__) || defined (__wii__)
|
#if defined(__gamecube__) || defined (__wii__)
|
||||||
#include <gctypes.h>
|
#include <gctypes.h>
|
||||||
#else
|
#include <ogc/disc_io.h>
|
||||||
# ifdef NDS
|
#include <gccore.h>
|
||||||
|
#elif defined(NDS)
|
||||||
#include <nds/ndstypes.h>
|
#include <nds/ndstypes.h>
|
||||||
#include <nds/system.h>
|
#include <nds/system.h>
|
||||||
|
#include <nds/disc_io.h>
|
||||||
# else
|
#elif defined(GBA)
|
||||||
#include "gba_types.h"
|
#include <gba_types.h>
|
||||||
# endif
|
#include <disc_io.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BYTES_PER_READ 512
|
// Platform specific options
|
||||||
|
#if defined (__wii__)
|
||||||
#ifndef NULL
|
#define DEFAULT_CACHE_PAGES 8
|
||||||
#define NULL 0
|
#define USE_LWP_LOCK
|
||||||
|
#define USE_RTC_TIME
|
||||||
|
#elif defined (__gamecube__)
|
||||||
|
#define DEFAULT_CACHE_PAGES 8
|
||||||
|
#define USE_LWP_LOCK
|
||||||
|
#define USE_RTC_TIME
|
||||||
|
#elif defined (NDS)
|
||||||
|
#define DEFAULT_CACHE_PAGES 8
|
||||||
|
#define USE_RTC_TIME
|
||||||
|
#elif defined (GBA)
|
||||||
|
#define DEFAULT_CACHE_PAGES 2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <fat.h>
|
|
||||||
|
|
||||||
#endif // _COMMON_H
|
#endif // _COMMON_H
|
||||||
|
@ -25,47 +25,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-08-14 - Chishm
|
|
||||||
* entryFromPath correctly finds "" and "." now
|
|
||||||
|
|
||||||
2006-08-17 - Chishm
|
|
||||||
* entryFromPath doesn't look for "" anymore - use "." to refer to the current directory
|
|
||||||
|
|
||||||
2006-08-19 - Chishm
|
|
||||||
* Fixed entryFromPath bug when looking for "." in root directory
|
|
||||||
|
|
||||||
2006-10-01 - Chishm
|
|
||||||
* Now clears the whole new cluster when linking in more clusters for a directory
|
|
||||||
|
|
||||||
2006-10-28 - Chishm
|
|
||||||
* stat returns the hostType for the st_dev value
|
|
||||||
|
|
||||||
2007-03-14 - Chishm
|
|
||||||
* Check long file names for buffer overflow
|
|
||||||
|
|
||||||
2007-04-22 - Chishm
|
|
||||||
* Added space to list of illegal alias characters - fixes filename creation bug when filename contained a space
|
|
||||||
|
|
||||||
2007-09-01 - Chishm
|
|
||||||
* Use CLUSTER_ERROR when an error occurs with the FAT, not CLUSTER_FREE
|
|
||||||
|
|
||||||
2007-11-01 - Chishm
|
|
||||||
* Added unicode support
|
|
||||||
|
|
||||||
2007-11-04 - Chishm
|
|
||||||
* Fixed alias creation bugs
|
|
||||||
|
|
||||||
2007-11-16 - Chishm
|
|
||||||
* Fixed LFN creation with character codes > 0x7F
|
|
||||||
|
|
||||||
2008-08-02 - Chishm
|
|
||||||
* Correct cluster given on FAT32 when .. entry is included in path to subdirectory
|
|
||||||
* Fixed creation of long filename entry for all-caps filenames longer than 8 characters
|
|
||||||
|
|
||||||
2008-09-07 - Chishm
|
|
||||||
* Don't read high 16 bits of start cluster from a directory entry on non-FAT32 partititions, in case it contains non-zero data
|
|
||||||
* Thanks to Chris Liu
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -73,6 +32,7 @@
|
|||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "directory.h"
|
#include "directory.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@ -125,8 +85,8 @@ Returns -1 if it is an invalid LFN
|
|||||||
*/
|
*/
|
||||||
#define ABOVE_UCS_RANGE 0xF0
|
#define ABOVE_UCS_RANGE 0xF0
|
||||||
static int _FAT_directory_lfnLength (const char* name) {
|
static int _FAT_directory_lfnLength (const char* name) {
|
||||||
u32 i;
|
unsigned int i;
|
||||||
u32 nameLength;
|
size_t nameLength;
|
||||||
int ucsLength;
|
int ucsLength;
|
||||||
const char* tempName = name;
|
const char* tempName = name;
|
||||||
|
|
||||||
@ -185,7 +145,7 @@ static size_t _FAT_directory_mbstoucs2 (ucs2_t* dst, const char* src, size_t len
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Convert a UCS-2 string into a NUL-terminated multibyte string, storing at most len chars
|
Convert a UCS-2 string into a NUL-terminated multibyte string, storing at most len chars
|
||||||
return number of chars stored
|
return number of chars stored, or (size_t)-1 on error
|
||||||
*/
|
*/
|
||||||
static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len) {
|
static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len) {
|
||||||
mbstate_t ps = {0};
|
mbstate_t ps = {0};
|
||||||
@ -194,7 +154,7 @@ static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len
|
|||||||
char buff[MB_CUR_MAX];
|
char buff[MB_CUR_MAX];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
while (count < len - 1 && src != '\0') {
|
while (count < len - 1 && *src != '\0') {
|
||||||
bytes = wcrtomb (buff, *src, &ps);
|
bytes = wcrtomb (buff, *src, &ps);
|
||||||
if (bytes < 0) {
|
if (bytes < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -233,7 +193,7 @@ static int _FAT_directory_mbsncasecmp (const char* s1, const char* s2, size_t le
|
|||||||
s2 += b2;
|
s2 += b2;
|
||||||
b1 = mbrtowc(&wc1, s1, MB_CUR_MAX, &ps1);
|
b1 = mbrtowc(&wc1, s1, MB_CUR_MAX, &ps1);
|
||||||
b2 = mbrtowc(&wc2, s2, MB_CUR_MAX, &ps2);
|
b2 = mbrtowc(&wc2, s2, MB_CUR_MAX, &ps2);
|
||||||
if (b1 < 0 || b2 < 0) {
|
if ((int)b1 < 0 || (int)b2 < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
len1 -= b1;
|
len1 -= b1;
|
||||||
@ -276,7 +236,7 @@ static bool _FAT_directory_entryGetAlias (const u8* entryData, char* destName) {
|
|||||||
return (destName[0] != '\0');
|
return (destName[0] != '\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 _FAT_directory_entryGetCluster (PARTITION* partition, const u8* entryData) {
|
uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData) {
|
||||||
if (partition->filesysType == FS_FAT32) {
|
if (partition->filesysType == FS_FAT32) {
|
||||||
// Only use high 16 bits of start cluster when we are certain they are correctly defined
|
// Only use high 16 bits of start cluster when we are certain they are correctly defined
|
||||||
return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16);
|
return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16);
|
||||||
@ -286,9 +246,8 @@ u32 _FAT_directory_entryGetCluster (PARTITION* partition, const u8* entryData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ENTRY_POSITION* entryPosition, bool extendDirectory) {
|
static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ENTRY_POSITION* entryPosition, bool extendDirectory) {
|
||||||
DIR_ENTRY_POSITION position;
|
DIR_ENTRY_POSITION position = *entryPosition;
|
||||||
position = *entryPosition;
|
uint32_t tempCluster;
|
||||||
u32 tempCluster;
|
|
||||||
|
|
||||||
// Increment offset, wrapping at the end of a sector
|
// Increment offset, wrapping at the end of a sector
|
||||||
++ position.offset;
|
++ position.offset;
|
||||||
@ -323,17 +282,12 @@ static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_
|
|||||||
bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
||||||
DIR_ENTRY_POSITION entryStart;
|
DIR_ENTRY_POSITION entryStart;
|
||||||
DIR_ENTRY_POSITION entryEnd;
|
DIR_ENTRY_POSITION entryEnd;
|
||||||
|
uint8_t entryData[0x20];
|
||||||
u8 entryData[0x20];
|
|
||||||
|
|
||||||
ucs2_t lfn[MAX_LFN_LENGTH];
|
ucs2_t lfn[MAX_LFN_LENGTH];
|
||||||
|
|
||||||
bool notFound, found;
|
bool notFound, found;
|
||||||
u32 maxSectors;
|
|
||||||
int lfnPos;
|
int lfnPos;
|
||||||
u8 lfnChkSum, chkSum;
|
uint8_t lfnChkSum, chkSum;
|
||||||
bool lfnExists;
|
bool lfnExists;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
lfnChkSum = 0;
|
lfnChkSum = 0;
|
||||||
@ -347,13 +301,6 @@ bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
|||||||
|
|
||||||
entryEnd = entryStart;
|
entryEnd = entryStart;
|
||||||
|
|
||||||
// Can only be FAT16_ROOT_DIR_CLUSTER if it is the root directory on a FAT12 or FAT16 partition
|
|
||||||
if (entryStart.cluster == FAT16_ROOT_DIR_CLUSTER) {
|
|
||||||
maxSectors = partition->dataStart - partition->rootDirStart;
|
|
||||||
} else {
|
|
||||||
maxSectors = partition->sectorsPerCluster;
|
|
||||||
}
|
|
||||||
|
|
||||||
lfnExists = false;
|
lfnExists = false;
|
||||||
|
|
||||||
found = false;
|
found = false;
|
||||||
@ -364,7 +311,9 @@ bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
|||||||
notFound = true;
|
notFound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
|
_FAT_cache_readPartialSector (partition->cache, entryData,
|
||||||
|
_FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector,
|
||||||
|
entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
|
||||||
|
|
||||||
if (entryData[DIR_ENTRY_attributes] == ATTRIB_LFN) {
|
if (entryData[DIR_ENTRY_attributes] == ATTRIB_LFN) {
|
||||||
// It's an LFN
|
// It's an LFN
|
||||||
@ -412,7 +361,7 @@ bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lfnExists) {
|
if (lfnExists) {
|
||||||
if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) < 0) {
|
if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) == (size_t)-1) {
|
||||||
// Failed to convert the file name to UTF-8. Maybe the wrong locale is set?
|
// Failed to convert the file name to UTF-8. Maybe the wrong locale is set?
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -436,7 +385,7 @@ bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster) {
|
bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) {
|
||||||
entry->dataStart.cluster = dirCluster;
|
entry->dataStart.cluster = dirCluster;
|
||||||
entry->dataStart.sector = 0;
|
entry->dataStart.sector = 0;
|
||||||
entry->dataStart.offset = -1; // Start before the beginning of the directory
|
entry->dataStart.offset = -1; // Start before the beginning of the directory
|
||||||
@ -469,19 +418,14 @@ bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) {
|
bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) {
|
||||||
DIR_ENTRY_POSITION entryStart;
|
DIR_ENTRY_POSITION entryStart = entry->dataStart;
|
||||||
DIR_ENTRY_POSITION entryEnd;
|
DIR_ENTRY_POSITION entryEnd = entry->dataEnd;
|
||||||
entryStart = entry->dataStart;
|
|
||||||
entryEnd = entry->dataEnd;
|
|
||||||
bool entryStillValid;
|
bool entryStillValid;
|
||||||
bool finished;
|
bool finished;
|
||||||
|
|
||||||
ucs2_t lfn[MAX_LFN_LENGTH];
|
ucs2_t lfn[MAX_LFN_LENGTH];
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
int lfnPos;
|
int lfnPos;
|
||||||
|
uint8_t entryData[DIR_ENTRY_DATA_SIZE];
|
||||||
u8 entryData[DIR_ENTRY_DATA_SIZE];
|
|
||||||
|
|
||||||
memset (entry->filename, '\0', MAX_FILENAME_LENGTH);
|
memset (entry->filename, '\0', MAX_FILENAME_LENGTH);
|
||||||
|
|
||||||
@ -525,7 +469,7 @@ bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Encode the long file name into a multibyte string
|
// Encode the long file name into a multibyte string
|
||||||
if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) < 0) {
|
if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) == (size_t)-1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -539,11 +483,9 @@ bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const
|
|||||||
size_t dirnameLength;
|
size_t dirnameLength;
|
||||||
const char* pathPosition;
|
const char* pathPosition;
|
||||||
const char* nextPathPosition;
|
const char* nextPathPosition;
|
||||||
u32 dirCluster;
|
uint32_t dirCluster;
|
||||||
bool foundFile;
|
bool foundFile;
|
||||||
|
|
||||||
char alias[MAX_ALIAS_LENGTH];
|
char alias[MAX_ALIAS_LENGTH];
|
||||||
|
|
||||||
bool found, notFound;
|
bool found, notFound;
|
||||||
|
|
||||||
pathPosition = path;
|
pathPosition = path;
|
||||||
@ -659,14 +601,11 @@ bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
||||||
DIR_ENTRY_POSITION entryStart;
|
DIR_ENTRY_POSITION entryStart = entry->dataStart;
|
||||||
DIR_ENTRY_POSITION entryEnd;
|
DIR_ENTRY_POSITION entryEnd = entry->dataEnd;
|
||||||
entryStart = entry->dataStart;
|
|
||||||
entryEnd = entry->dataEnd;
|
|
||||||
bool entryStillValid;
|
bool entryStillValid;
|
||||||
bool finished;
|
bool finished;
|
||||||
|
uint8_t entryData[DIR_ENTRY_DATA_SIZE];
|
||||||
u8 entryData[DIR_ENTRY_DATA_SIZE];
|
|
||||||
|
|
||||||
// Create an empty directory entry to overwrite the old ones with
|
// Create an empty directory entry to overwrite the old ones with
|
||||||
for ( entryStillValid = true, finished = false;
|
for ( entryStillValid = true, finished = false;
|
||||||
@ -688,14 +627,11 @@ bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster, u32 size) {
|
static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster, size_t size) {
|
||||||
DIR_ENTRY_POSITION gapStart;
|
DIR_ENTRY_POSITION gapStart;
|
||||||
DIR_ENTRY_POSITION gapEnd;
|
DIR_ENTRY_POSITION gapEnd;
|
||||||
|
uint8_t entryData[DIR_ENTRY_DATA_SIZE];
|
||||||
u8 entryData[DIR_ENTRY_DATA_SIZE];
|
size_t dirEntryRemain;
|
||||||
|
|
||||||
u32 dirEntryRemain;
|
|
||||||
|
|
||||||
bool endOfDirectory, entryStillValid;
|
bool endOfDirectory, entryStillValid;
|
||||||
|
|
||||||
// Scan Dir for free entry
|
// Scan Dir for free entry
|
||||||
@ -763,11 +699,11 @@ static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, u32 dirCluster) {
|
static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, uint32_t dirCluster) {
|
||||||
DIR_ENTRY tempEntry;
|
DIR_ENTRY tempEntry;
|
||||||
bool foundFile;
|
bool foundFile;
|
||||||
char alias[MAX_ALIAS_LENGTH];
|
char alias[MAX_ALIAS_LENGTH];
|
||||||
u32 dirnameLength;
|
size_t dirnameLength;
|
||||||
|
|
||||||
dirnameLength = strnlen(name, MAX_FILENAME_LENGTH);
|
dirnameLength = strnlen(name, MAX_FILENAME_LENGTH);
|
||||||
|
|
||||||
@ -835,7 +771,7 @@ static int _FAT_directory_createAlias (char* alias, const char* lfn) {
|
|||||||
lfnPos += bytesUsed;
|
lfnPos += bytesUsed;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (oemChar == WEOF) {
|
if (oemChar == EOF) {
|
||||||
oemChar = '_'; // Replace unconvertable characters with underscores
|
oemChar = '_'; // Replace unconvertable characters with underscores
|
||||||
lossyConversion = true;
|
lossyConversion = true;
|
||||||
}
|
}
|
||||||
@ -882,7 +818,7 @@ static int _FAT_directory_createAlias (char* alias, const char* lfn) {
|
|||||||
lfnExt += bytesUsed;
|
lfnExt += bytesUsed;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (oemChar == WEOF) {
|
if (oemChar == EOF) {
|
||||||
oemChar = '_'; // Replace unconvertable characters with underscores
|
oemChar = '_'; // Replace unconvertable characters with underscores
|
||||||
lossyConversion = true;
|
lossyConversion = true;
|
||||||
}
|
}
|
||||||
@ -910,14 +846,14 @@ static int _FAT_directory_createAlias (char* alias, const char* lfn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster) {
|
bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) {
|
||||||
u32 entrySize;
|
size_t entrySize;
|
||||||
u8 lfnEntry[DIR_ENTRY_DATA_SIZE];
|
uint8_t lfnEntry[DIR_ENTRY_DATA_SIZE];
|
||||||
s32 i,j; // Must be signed for use when decrementing in for loop
|
int i,j; // Must be signed for use when decrementing in for loop
|
||||||
char *tmpCharPtr;
|
char *tmpCharPtr;
|
||||||
DIR_ENTRY_POSITION curEntryPos;
|
DIR_ENTRY_POSITION curEntryPos;
|
||||||
bool entryStillValid;
|
bool entryStillValid;
|
||||||
u8 aliasCheckSum = 0;
|
uint8_t aliasCheckSum = 0;
|
||||||
char alias [MAX_ALIAS_LENGTH];
|
char alias [MAX_ALIAS_LENGTH];
|
||||||
int aliasLen;
|
int aliasLen;
|
||||||
int lfnLen;
|
int lfnLen;
|
||||||
@ -938,7 +874,7 @@ bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirClu
|
|||||||
entry->filename[i] = '\0';
|
entry->filename[i] = '\0';
|
||||||
}
|
}
|
||||||
// Remove leading spaces
|
// Remove leading spaces
|
||||||
for (i = 0; (i < strlen (entry->filename)) && (entry->filename[i] == ' '); ++i) ;
|
for (i = 0; (i < (int)strlen (entry->filename)) && (entry->filename[i] == ' '); ++i) ;
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
memmove (entry->filename, entry->filename + i, strlen (entry->filename + i));
|
memmove (entry->filename, entry->filename + i, strlen (entry->filename + i));
|
||||||
}
|
}
|
||||||
@ -1060,7 +996,7 @@ bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirClu
|
|||||||
{
|
{
|
||||||
if (i > 1) {
|
if (i > 1) {
|
||||||
// Long filename entry
|
// Long filename entry
|
||||||
lfnEntry[LFN_offset_ordinal] = (i - 1) | (i == entrySize ? LFN_END : 0);
|
lfnEntry[LFN_offset_ordinal] = (i - 1) | ((size_t)i == entrySize ? LFN_END : 0);
|
||||||
for (j = 0; j < 13; j++) {
|
for (j = 0; j < 13; j++) {
|
||||||
if (lfn [(i - 2) * 13 + j] == '\0') {
|
if (lfn [(i - 2) * 13 + j] == '\0') {
|
||||||
if ((j > 1) && (lfn [(i - 2) * 13 + (j-1)] == '\0')) {
|
if ((j > 1) && (lfn [(i - 2) * 13 + (j-1)] == '\0')) {
|
||||||
|
@ -25,16 +25,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2007-11-01 - Chishm
|
|
||||||
* Added unicode support
|
|
||||||
|
|
||||||
2008-09-07 - Chishm
|
|
||||||
* Don't read high 16 bits of start cluster from a directory entry on non-FAT32 partititions, in case it contains non-zero data
|
|
||||||
* Thanks to Chris Liu
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _DIRECTORY_H
|
#ifndef _DIRECTORY_H
|
||||||
@ -70,13 +60,13 @@
|
|||||||
typedef enum {FT_DIRECTORY, FT_FILE} FILE_TYPE;
|
typedef enum {FT_DIRECTORY, FT_FILE} FILE_TYPE;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 cluster;
|
uint32_t cluster;
|
||||||
u32 sector;
|
sec_t sector;
|
||||||
s32 offset;
|
int32_t offset;
|
||||||
} DIR_ENTRY_POSITION;
|
} DIR_ENTRY_POSITION;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u8 entryData[DIR_ENTRY_DATA_SIZE];
|
uint8_t entryData[DIR_ENTRY_DATA_SIZE];
|
||||||
DIR_ENTRY_POSITION dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
DIR_ENTRY_POSITION dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
||||||
DIR_ENTRY_POSITION dataEnd; // Always points to the file/directory's alias entry
|
DIR_ENTRY_POSITION dataEnd; // Always points to the file/directory's alias entry
|
||||||
char filename[MAX_FILENAME_LENGTH];
|
char filename[MAX_FILENAME_LENGTH];
|
||||||
@ -121,7 +111,7 @@ Places result in entry
|
|||||||
entry will be destroyed even if no directory entry is found
|
entry will be destroyed even if no directory entry is found
|
||||||
Returns true on success, false on failure
|
Returns true on success, false on failure
|
||||||
*/
|
*/
|
||||||
bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster);
|
bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reads the next directory entry after the one already pointed to by entry
|
Reads the next directory entry after the one already pointed to by entry
|
||||||
@ -161,12 +151,12 @@ The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are
|
|||||||
updated with the new directory entry position and alias.
|
updated with the new directory entry position and alias.
|
||||||
Returns true on success, false on failure
|
Returns true on success, false on failure
|
||||||
*/
|
*/
|
||||||
bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster);
|
bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get the start cluster of a file from it's entry data
|
Get the start cluster of a file from it's entry data
|
||||||
*/
|
*/
|
||||||
u32 _FAT_directory_entryGetCluster (PARTITION* partition, const u8* entryData);
|
uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Fill in the file name and entry data of DIR_ENTRY* entry.
|
Fill in the file name and entry data of DIR_ENTRY* entry.
|
||||||
|
@ -25,81 +25,28 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
#ifndef _DISC_H
|
#ifndef _DISC_H
|
||||||
#define _DISC_H
|
#define _DISC_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
|
||||||
// Customisable features
|
|
||||||
|
|
||||||
// Use DMA to read the card, remove this line to use normal reads/writes
|
|
||||||
// #define _IO_USE_DMA
|
|
||||||
|
|
||||||
// Allow buffers not alligned to 16 bits when reading files.
|
|
||||||
// Note that this will slow down access speed, so only use if you have to.
|
|
||||||
// It is also incompatible with DMA
|
|
||||||
#define _IO_ALLOW_UNALIGNED
|
|
||||||
|
|
||||||
#if defined _IO_USE_DMA && defined _IO_ALLOW_UNALIGNED
|
|
||||||
#error "You can't use both DMA and unaligned memory"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DEVICE_TYPE_WII 0
|
|
||||||
#define DEVICE_TYPE_GC 1
|
|
||||||
|
|
||||||
#define FEATURE_MEDIUM_CANREAD 0x00000001
|
|
||||||
#define FEATURE_MEDIUM_CANWRITE 0x00000002
|
|
||||||
#define FEATURE_SLOT_GBA 0x00000010
|
|
||||||
#define FEATURE_SLOT_NDS 0x00000020
|
|
||||||
#define FEATURE_GAMECUBE_SLOTA 0x00000010
|
|
||||||
#define FEATURE_GAMECUBE_SLOTB 0x00000020
|
|
||||||
|
|
||||||
typedef bool (* FN_MEDIUM_STARTUP)(void) ;
|
|
||||||
typedef bool (* FN_MEDIUM_ISINSERTED)(void) ;
|
|
||||||
typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u32 numSectors, void* buffer) ;
|
|
||||||
typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u32 numSectors, const void* buffer) ;
|
|
||||||
typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ;
|
|
||||||
typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ;
|
|
||||||
|
|
||||||
struct IO_INTERFACE_STRUCT {
|
|
||||||
unsigned long ioType ;
|
|
||||||
unsigned long features ;
|
|
||||||
FN_MEDIUM_STARTUP fn_startup ;
|
|
||||||
FN_MEDIUM_ISINSERTED fn_isInserted ;
|
|
||||||
FN_MEDIUM_READSECTORS fn_readSectors ;
|
|
||||||
FN_MEDIUM_WRITESECTORS fn_writeSectors ;
|
|
||||||
FN_MEDIUM_CLEARSTATUS fn_clearStatus ;
|
|
||||||
FN_MEDIUM_SHUTDOWN fn_shutdown ;
|
|
||||||
} ;
|
|
||||||
|
|
||||||
typedef struct IO_INTERFACE_STRUCT IO_INTERFACE ;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Search for a block based device in all available slots.
|
A list of all default devices to try at startup,
|
||||||
Return a pointer to a usable interface if one is found,
|
terminated by a {NULL,NULL} entry.
|
||||||
NULL if not.
|
|
||||||
*/
|
*/
|
||||||
extern const IO_INTERFACE* _FAT_disc_findInterface (void);
|
typedef struct {
|
||||||
|
const char* name;
|
||||||
/*
|
const DISC_INTERFACE* (*getInterface)(void);
|
||||||
Search for a block based device in a specific slot.
|
} INTERFACE_ID;
|
||||||
Return a pointer to a usable interface if one is found,
|
extern const INTERFACE_ID _FAT_disc_interfaces[];
|
||||||
NULL if not.
|
|
||||||
*/
|
|
||||||
extern const IO_INTERFACE* _FAT_disc_findInterfaceSlot (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check if a disc is inserted
|
Check if a disc is inserted
|
||||||
Return true if a disc is inserted and ready, false otherwise
|
Return true if a disc is inserted and ready, false otherwise
|
||||||
*/
|
*/
|
||||||
static inline bool _FAT_disc_isInserted (const IO_INTERFACE* disc) {
|
static inline bool _FAT_disc_isInserted (const DISC_INTERFACE* disc) {
|
||||||
return disc->fn_isInserted();
|
return disc->isInserted();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -108,8 +55,8 @@ numSectors is between 1 and 256
|
|||||||
sector is from 0 to 2^28
|
sector is from 0 to 2^28
|
||||||
buffer is a pointer to the memory to fill
|
buffer is a pointer to the memory to fill
|
||||||
*/
|
*/
|
||||||
static inline bool _FAT_disc_readSectors (const IO_INTERFACE* disc, u32 sector, u32 numSectors, void* buffer) {
|
static inline bool _FAT_disc_readSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer) {
|
||||||
return disc->fn_readSectors (sector, numSectors, buffer);
|
return disc->readSectors (sector, numSectors, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -118,43 +65,43 @@ numSectors is between 1 and 256
|
|||||||
sector is from 0 to 2^28
|
sector is from 0 to 2^28
|
||||||
buffer is a pointer to the memory to read from
|
buffer is a pointer to the memory to read from
|
||||||
*/
|
*/
|
||||||
static inline bool _FAT_disc_writeSectors (const IO_INTERFACE* disc, u32 sector, u32 numSectors, const void* buffer) {
|
static inline bool _FAT_disc_writeSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, const void* buffer) {
|
||||||
return disc->fn_writeSectors (sector, numSectors, buffer);
|
return disc->writeSectors (sector, numSectors, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reset the card back to a ready state
|
Reset the card back to a ready state
|
||||||
*/
|
*/
|
||||||
static inline bool _FAT_disc_clearStatus (const IO_INTERFACE* disc) {
|
static inline bool _FAT_disc_clearStatus (const DISC_INTERFACE* disc) {
|
||||||
return disc->fn_clearStatus();
|
return disc->clearStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialise the disc to a state ready for data reading or writing
|
Initialise the disc to a state ready for data reading or writing
|
||||||
*/
|
*/
|
||||||
static inline bool _FAT_disc_startup (const IO_INTERFACE* disc) {
|
static inline bool _FAT_disc_startup (const DISC_INTERFACE* disc) {
|
||||||
return disc->fn_startup();
|
return disc->startup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Put the disc in a state ready for power down.
|
Put the disc in a state ready for power down.
|
||||||
Complete any pending writes and disable the disc if necessary
|
Complete any pending writes and disable the disc if necessary
|
||||||
*/
|
*/
|
||||||
static inline bool _FAT_disc_shutdown (const IO_INTERFACE* disc) {
|
static inline bool _FAT_disc_shutdown (const DISC_INTERFACE* disc) {
|
||||||
return disc->fn_shutdown();
|
return disc->shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Return a 32 bit value unique to each type of interface
|
Return a 32 bit value unique to each type of interface
|
||||||
*/
|
*/
|
||||||
static inline u32 _FAT_disc_hostType (const IO_INTERFACE* disc) {
|
static inline uint32_t _FAT_disc_hostType (const DISC_INTERFACE* disc) {
|
||||||
return disc->ioType;
|
return disc->ioType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Return a 32 bit value that specifies the capabilities of the disc
|
Return a 32 bit value that specifies the capabilities of the disc
|
||||||
*/
|
*/
|
||||||
static inline u32 _FAT_disc_features (const IO_INTERFACE* disc) {
|
static inline uint32_t _FAT_disc_features (const DISC_INTERFACE* disc) {
|
||||||
return disc->features;
|
return disc->features;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,210 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
disc.c
|
|
||||||
|
|
||||||
uniformed io-interface to work with Chishm's FAT library
|
|
||||||
|
|
||||||
Written by MightyMax
|
|
||||||
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2005-11-06 - Chishm
|
|
||||||
* Added WAIT_CR modifications for NDS
|
|
||||||
|
|
||||||
2006-02-03 www.neoflash.com
|
|
||||||
* Added SUPPORT_* defines, comment out any of the SUPPORT_* defines in disc_io.h to remove support
|
|
||||||
for the given interface and stop code being linked to the binary
|
|
||||||
|
|
||||||
* Added support for MK2 MMC interface
|
|
||||||
|
|
||||||
* Added disc_Cache* functions
|
|
||||||
|
|
||||||
2006-02-05 - Chishm
|
|
||||||
* Added Supercard SD support
|
|
||||||
|
|
||||||
2006-02-26 - Cytex
|
|
||||||
* Added EFA2 support
|
|
||||||
|
|
||||||
2006-05-18 - Chishm
|
|
||||||
* Rewritten for FATlib disc.c
|
|
||||||
|
|
||||||
2006-06-19 - Chishm
|
|
||||||
* Changed read and write interface to accept a u32 instead of a u8 for the number of sectors
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Removed disc_Cache* functions, since there is now a proper unified cache
|
|
||||||
* Removed SUPPORT_* defines
|
|
||||||
* Rewrote device detection functions
|
|
||||||
* First libfat release
|
|
||||||
|
|
||||||
2006-07-25 - Chishm
|
|
||||||
* Changed IO_INTERFACEs to const
|
|
||||||
|
|
||||||
2006-08-02 - Chishm
|
|
||||||
* Added NinjaDS
|
|
||||||
|
|
||||||
2006-12-25 - Chishm
|
|
||||||
* Added DLDI
|
|
||||||
* Removed experimental interfaces
|
|
||||||
|
|
||||||
2007-05-01 - Chishm
|
|
||||||
* Removed FCSR
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
#ifdef NDS
|
|
||||||
#include <nds.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <fat.h>
|
|
||||||
|
|
||||||
// Include known io-interfaces:
|
|
||||||
#include "io_dldi.h"
|
|
||||||
#include "io_njsd.h"
|
|
||||||
#include "io_nmmc.h"
|
|
||||||
#include "io_mpcf.h"
|
|
||||||
#include "io_m3cf.h"
|
|
||||||
#include "io_sccf.h"
|
|
||||||
#include "io_scsd.h"
|
|
||||||
#include "io_m3sd.h"
|
|
||||||
|
|
||||||
const IO_INTERFACE* ioInterfaces[] = {
|
|
||||||
&_io_dldi, // Reserved for new interfaces
|
|
||||||
#ifdef NDS
|
|
||||||
// Place Slot 1 (DS Card) interfaces here
|
|
||||||
&_io_njsd, &_io_nmmc,
|
|
||||||
#endif
|
|
||||||
// Place Slot 2 (GBA Cart) interfaces here
|
|
||||||
&_io_mpcf, &_io_m3cf, &_io_sccf, &_io_scsd, &_io_m3sd
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Hardware level disc funtions
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
const IO_INTERFACE* _FAT_disc_gbaSlotFindInterface (void)
|
|
||||||
{
|
|
||||||
// If running on an NDS, make sure the correct CPU can access
|
|
||||||
// the GBA cart. First implemented by SaTa.
|
|
||||||
#ifdef NDS
|
|
||||||
#ifdef ARM9
|
|
||||||
sysSetCartOwner(BUS_OWNER_ARM9);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
|
|
||||||
if ((ioInterfaces[i]->features & FEATURE_SLOT_GBA) && (ioInterfaces[i]->fn_startup())) {
|
|
||||||
return ioInterfaces[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef NDS
|
|
||||||
/*
|
|
||||||
* Check the DS card slot for a valid memory card interface
|
|
||||||
* If an interface is found, it is set as the default interace
|
|
||||||
* and it returns true. Otherwise the default interface is left
|
|
||||||
* untouched and it returns false.
|
|
||||||
*/
|
|
||||||
const IO_INTERFACE* _FAT_disc_dsSlotFindInterface (void)
|
|
||||||
{
|
|
||||||
#ifdef ARM9
|
|
||||||
sysSetCardOwner(BUS_OWNER_ARM9);
|
|
||||||
#endif
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
|
|
||||||
if ((ioInterfaces[i]->features & FEATURE_SLOT_NDS) && (ioInterfaces[i]->fn_startup())) {
|
|
||||||
return ioInterfaces[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When running on an NDS, check the either slot for a valid memory
|
|
||||||
* card interface.
|
|
||||||
* When running on a GBA, call _FAT_disc_gbaSlotFindInterface
|
|
||||||
* If an interface is found, it is set as the default interace
|
|
||||||
* and it returns true. Otherwise the default interface is left
|
|
||||||
* untouched and it returns false.
|
|
||||||
*/
|
|
||||||
#ifdef NDS
|
|
||||||
const IO_INTERFACE* _FAT_disc_findInterface (void)
|
|
||||||
{
|
|
||||||
#ifdef ARM9
|
|
||||||
sysSetBusOwners(BUS_OWNER_ARM9, BUS_OWNER_ARM9);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
|
|
||||||
if (ioInterfaces[i]->fn_startup()) {
|
|
||||||
return ioInterfaces[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
const IO_INTERFACE* _FAT_disc_findInterface (void)
|
|
||||||
{
|
|
||||||
return _FAT_disc_gbaSlotFindInterface();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const IO_INTERFACE* _FAT_disc_findInterfaceSlot (PARTITION_INTERFACE partitionNumber)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef NDS
|
|
||||||
switch (partitionNumber) {
|
|
||||||
case PI_SLOT_1:
|
|
||||||
// Mount the disc in slot 1
|
|
||||||
return _FAT_disc_dsSlotFindInterface ();
|
|
||||||
break;
|
|
||||||
case PI_SLOT_2:
|
|
||||||
// Mount the disc in slot 2
|
|
||||||
return _FAT_disc_gbaSlotFindInterface ();
|
|
||||||
break;
|
|
||||||
case PI_DEFAULT:
|
|
||||||
case PI_CUSTOM:
|
|
||||||
default:
|
|
||||||
// Anything else has to be handled specially
|
|
||||||
return NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return _FAT_disc_gbaSlotFindInterface ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
@ -1,322 +0,0 @@
|
|||||||
/*
|
|
||||||
io_cf_common.c based on
|
|
||||||
|
|
||||||
compact_flash.c
|
|
||||||
By chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Common hardware routines for using a compact flash card. This is not reentrant
|
|
||||||
and does not do range checking on the supplied addresses. This is designed to
|
|
||||||
be as fast as possible.
|
|
||||||
|
|
||||||
CF routines modified with help from Darkfader
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "io_cf_common.h"
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// DMA
|
|
||||||
#ifdef _IO_USE_DMA
|
|
||||||
#ifndef NDS
|
|
||||||
#include "gba_dma.h"
|
|
||||||
#else
|
|
||||||
#include <nds/dma.h>
|
|
||||||
#ifdef ARM9
|
|
||||||
#include <nds/arm9/cache.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// CF Addresses & Commands
|
|
||||||
|
|
||||||
CF_REGISTERS cfRegisters = {0};
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_CF_isInserted
|
|
||||||
Is a compact flash card inserted?
|
|
||||||
bool return OUT: true if a CF card is inserted
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _CF_isInserted (void) {
|
|
||||||
// Change register, then check if value did change
|
|
||||||
*(cfRegisters.status) = CF_STS_INSERTED;
|
|
||||||
return ((*(cfRegisters.status) & 0xff) == CF_STS_INSERTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_CF_clearStatus
|
|
||||||
Tries to make the CF card go back to idle mode
|
|
||||||
bool return OUT: true if a CF card is idle
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _CF_clearStatus (void) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// Wait until CF card is finished previous commands
|
|
||||||
i=0;
|
|
||||||
while ((*(cfRegisters.command) & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until card is ready for commands
|
|
||||||
i = 0;
|
|
||||||
while ((!(*(cfRegisters.status) & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i >= CF_CARD_TIMEOUT)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_CF_readSectors
|
|
||||||
Read 512 byte sector numbered "sector" into "buffer"
|
|
||||||
u32 sector IN: address of first 512 byte sector on CF card to read
|
|
||||||
u32 numSectors IN: number of 512 byte sectors to read,
|
|
||||||
1 to 256 sectors can be read
|
|
||||||
void* buffer OUT: pointer to 512 byte buffer to store data in
|
|
||||||
bool return OUT: true if successful
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _CF_readSectors (u32 sector, u32 numSectors, void* buffer) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
u16 *buff = (u16*)buffer;
|
|
||||||
#ifdef _IO_ALLOW_UNALIGNED
|
|
||||||
u8 *buff_u8 = (u8*)buffer;
|
|
||||||
int temp;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (defined _IO_USE_DMA) && (defined NDS) && (defined ARM9)
|
|
||||||
DC_FlushRange( buffer, j * BYTES_PER_READ);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Wait until CF card is finished previous commands
|
|
||||||
i=0;
|
|
||||||
while ((*(cfRegisters.command) & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until card is ready for commands
|
|
||||||
i = 0;
|
|
||||||
while ((!(*(cfRegisters.status) & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i >= CF_CARD_TIMEOUT)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Set number of sectors to read
|
|
||||||
*(cfRegisters.sectorCount) = (numSectors < 256 ? numSectors : 0); // Read a maximum of 256 sectors, 0 means 256
|
|
||||||
|
|
||||||
// Set read sector
|
|
||||||
*(cfRegisters.lba1) = sector & 0xFF; // 1st byte of sector number
|
|
||||||
*(cfRegisters.lba2) = (sector >> 8) & 0xFF; // 2nd byte of sector number
|
|
||||||
*(cfRegisters.lba3) = (sector >> 16) & 0xFF; // 3rd byte of sector number
|
|
||||||
*(cfRegisters.lba4) = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
|
|
||||||
|
|
||||||
// Set command to read
|
|
||||||
*(cfRegisters.command) = CF_CMD_READ;
|
|
||||||
|
|
||||||
|
|
||||||
while (numSectors--)
|
|
||||||
{
|
|
||||||
// Wait until card is ready for reading
|
|
||||||
i = 0;
|
|
||||||
while (((*(cfRegisters.status) & 0xff)!= CF_STS_READY) && (i < CF_CARD_TIMEOUT))
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i >= CF_CARD_TIMEOUT)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Read data
|
|
||||||
#ifdef _IO_USE_DMA
|
|
||||||
#ifdef NDS
|
|
||||||
DMA3_SRC = (u32)(cfRegisters.data);
|
|
||||||
DMA3_DEST = (u32)buff;
|
|
||||||
DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
|
|
||||||
#else
|
|
||||||
DMA3COPY ( (cfRegisters.data), buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
|
|
||||||
#endif
|
|
||||||
buff += BYTES_PER_READ / 2;
|
|
||||||
#elif defined _IO_ALLOW_UNALIGNED
|
|
||||||
i=256;
|
|
||||||
if ((u32)buff_u8 & 0x01) {
|
|
||||||
while(i--)
|
|
||||||
{
|
|
||||||
temp = *(cfRegisters.data);
|
|
||||||
*buff_u8++ = temp & 0xFF;
|
|
||||||
*buff_u8++ = temp >> 8;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while(i--)
|
|
||||||
*buff++ = *(cfRegisters.data);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
i=256;
|
|
||||||
while(i--)
|
|
||||||
*buff++ = *(cfRegisters.data);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#if (defined _IO_USE_DMA) && (defined NDS)
|
|
||||||
// Wait for end of transfer before returning
|
|
||||||
while(DMA3_CR & DMA_BUSY);
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_CF_writeSectors
|
|
||||||
Write 512 byte sector numbered "sector" from "buffer"
|
|
||||||
u32 sector IN: address of 512 byte sector on CF card to read
|
|
||||||
u32 numSectors IN: number of 512 byte sectors to read,
|
|
||||||
1 to 256 sectors can be read
|
|
||||||
void* buffer IN: pointer to 512 byte buffer to read data from
|
|
||||||
bool return OUT: true if successful
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _CF_writeSectors (u32 sector, u32 numSectors, void* buffer) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
u16 *buff = (u16*)buffer;
|
|
||||||
#ifdef _IO_ALLOW_UNALIGNED
|
|
||||||
u8 *buff_u8 = (u8*)buffer;
|
|
||||||
int temp;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined _IO_USE_DMA && defined NDS && defined ARM9
|
|
||||||
DC_FlushRange( buffer, j * BYTES_PER_READ);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Wait until CF card is finished previous commands
|
|
||||||
i=0;
|
|
||||||
while ((*(cfRegisters.command) & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT))
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until card is ready for commands
|
|
||||||
i = 0;
|
|
||||||
while ((!(*(cfRegisters.status) & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT))
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i >= CF_CARD_TIMEOUT)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Set number of sectors to write
|
|
||||||
*(cfRegisters.sectorCount) = (numSectors < 256 ? numSectors : 0); // Write a maximum of 256 sectors, 0 means 256
|
|
||||||
|
|
||||||
// Set write sector
|
|
||||||
*(cfRegisters.lba1) = sector & 0xFF; // 1st byte of sector number
|
|
||||||
*(cfRegisters.lba2) = (sector >> 8) & 0xFF; // 2nd byte of sector number
|
|
||||||
*(cfRegisters.lba3) = (sector >> 16) & 0xFF; // 3rd byte of sector number
|
|
||||||
*(cfRegisters.lba4) = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
|
|
||||||
|
|
||||||
// Set command to write
|
|
||||||
*(cfRegisters.command) = CF_CMD_WRITE;
|
|
||||||
|
|
||||||
while (numSectors--)
|
|
||||||
{
|
|
||||||
// Wait until card is ready for writing
|
|
||||||
i = 0;
|
|
||||||
while (((*(cfRegisters.status) & 0xff) != CF_STS_READY) && (i < CF_CARD_TIMEOUT))
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i >= CF_CARD_TIMEOUT)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Write data
|
|
||||||
#ifdef _IO_USE_DMA
|
|
||||||
#ifdef NDS
|
|
||||||
DMA3_SRC = (u32)buff;
|
|
||||||
DMA3_DEST = (u32)(cfRegisters.data);
|
|
||||||
DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
|
|
||||||
#else
|
|
||||||
DMA3COPY( buff, (cfRegisters.data), 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
|
|
||||||
#endif
|
|
||||||
buff += BYTES_PER_READ / 2;
|
|
||||||
#elif defined _IO_ALLOW_UNALIGNED
|
|
||||||
i=256;
|
|
||||||
if ((u32)buff_u8 & 0x01) {
|
|
||||||
while(i--)
|
|
||||||
{
|
|
||||||
temp = *buff_u8++;
|
|
||||||
temp |= *buff_u8++ << 8;
|
|
||||||
*(cfRegisters.data) = temp;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while(i--)
|
|
||||||
*(cfRegisters.data) = *buff++;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
i=256;
|
|
||||||
while(i--)
|
|
||||||
*(cfRegisters.data) = *buff++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#if defined _IO_USE_DMA && defined NDS
|
|
||||||
// Wait for end of transfer before returning
|
|
||||||
while(DMA3_CR & DMA_BUSY);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_CF_shutdown
|
|
||||||
shutdown the CF interface
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _CF_shutdown(void) {
|
|
||||||
return _CF_clearStatus() ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_CF_startUp
|
|
||||||
Initializes the CF interface using the supplied registers
|
|
||||||
returns true if successful, otherwise returns false
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _CF_startup(const CF_REGISTERS *usableCfRegs) {
|
|
||||||
cfRegisters = *usableCfRegs;
|
|
||||||
// See if there is a read/write register
|
|
||||||
u16 temp = *(cfRegisters.lba1);
|
|
||||||
*(cfRegisters.lba1) = (~temp & 0xFF);
|
|
||||||
temp = (~temp & 0xFF);
|
|
||||||
if (!(*(cfRegisters.lba1) == temp)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Make sure it is 8 bit
|
|
||||||
*(cfRegisters.lba1) = 0xAA55;
|
|
||||||
if (*(cfRegisters.lba1) == 0xAA55) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
io_cf_common.h
|
|
||||||
|
|
||||||
By chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Common Compact Flash card values
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-07-16 - Chishm
|
|
||||||
* Combined all CF interfaces into one common set of routines
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_CF_COMMON_H
|
|
||||||
#define IO_CF_COMMON_H
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
vu16* data;
|
|
||||||
vu16* status;
|
|
||||||
vu16* command;
|
|
||||||
vu16* error;
|
|
||||||
vu16* sectorCount;
|
|
||||||
vu16* lba1;
|
|
||||||
vu16* lba2;
|
|
||||||
vu16* lba3;
|
|
||||||
vu16* lba4;
|
|
||||||
} CF_REGISTERS;
|
|
||||||
|
|
||||||
|
|
||||||
// CF Card status
|
|
||||||
#define CF_STS_INSERTED 0x50
|
|
||||||
#define CF_STS_REMOVED 0x00
|
|
||||||
#define CF_STS_READY 0x58
|
|
||||||
|
|
||||||
#define CF_STS_DRQ 0x08
|
|
||||||
#define CF_STS_BUSY 0x80
|
|
||||||
|
|
||||||
// CF Card commands
|
|
||||||
#define CF_CMD_LBA 0xE0
|
|
||||||
#define CF_CMD_READ 0x20
|
|
||||||
#define CF_CMD_WRITE 0x30
|
|
||||||
|
|
||||||
#define CF_CARD_TIMEOUT 10000000
|
|
||||||
|
|
||||||
bool _CF_isInserted (void);
|
|
||||||
bool _CF_clearStatus (void);
|
|
||||||
bool _CF_readSectors (u32 sector, u32 numSectors, void* buffer);
|
|
||||||
bool _CF_writeSectors (u32 sector, u32 numSectors, void* buffer);
|
|
||||||
bool _CF_shutdown(void);
|
|
||||||
bool _CF_startup(const CF_REGISTERS *usableCfRegs);
|
|
||||||
|
|
||||||
#endif // define IO_CF_COMMON_H
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
io_dldi.h
|
|
||||||
|
|
||||||
Reserved space for post-compilation adding of an extra driver
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-12-22 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_DLDI_H
|
|
||||||
#define IO_DLDI_H
|
|
||||||
|
|
||||||
// 'DLDI'
|
|
||||||
#define DEVICE_TYPE_DLDI 0x49444C44
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_dldi ;
|
|
||||||
|
|
||||||
#endif // define IO_DLDI_H
|
|
@ -1,73 +0,0 @@
|
|||||||
@---------------------------------------------------------------------------------
|
|
||||||
.align 4
|
|
||||||
.arm
|
|
||||||
.global _io_dldi
|
|
||||||
@---------------------------------------------------------------------------------
|
|
||||||
.equ FEATURE_MEDIUM_CANREAD, 0x00000001
|
|
||||||
.equ FEATURE_MEDIUM_CANWRITE, 0x00000002
|
|
||||||
.equ FEATURE_SLOT_GBA, 0x00000010
|
|
||||||
.equ FEATURE_SLOT_NDS, 0x00000020
|
|
||||||
|
|
||||||
|
|
||||||
_dldi_start:
|
|
||||||
|
|
||||||
@---------------------------------------------------------------------------------
|
|
||||||
@ Driver patch file standard header -- 16 bytes
|
|
||||||
.word 0xBF8DA5ED @ Magic number to identify this region
|
|
||||||
.asciz " Chishm" @ Identifying Magic string (8 bytes with null terminator)
|
|
||||||
.byte 0x01 @ Version number
|
|
||||||
.byte 0x0F @32KiB @ Log [base-2] of the size of this driver in bytes.
|
|
||||||
.byte 0x00 @ Sections to fix
|
|
||||||
.byte 0x0F @32KiB @ Log [base-2] of the allocated space in bytes.
|
|
||||||
|
|
||||||
@---------------------------------------------------------------------------------
|
|
||||||
@ Text identifier - can be anything up to 47 chars + terminating null -- 16 bytes
|
|
||||||
.align 4
|
|
||||||
.asciz "Default (No interface)"
|
|
||||||
|
|
||||||
@---------------------------------------------------------------------------------
|
|
||||||
@ Offsets to important sections within the data -- 32 bytes
|
|
||||||
.align 6
|
|
||||||
.word _dldi_start @ data start
|
|
||||||
.word _dldi_end @ data end
|
|
||||||
.word 0x00000000 @ Interworking glue start -- Needs address fixing
|
|
||||||
.word 0x00000000 @ Interworking glue end
|
|
||||||
.word 0x00000000 @ GOT start -- Needs address fixing
|
|
||||||
.word 0x00000000 @ GOT end
|
|
||||||
.word 0x00000000 @ bss start -- Needs setting to zero
|
|
||||||
.word 0x00000000 @ bss end
|
|
||||||
|
|
||||||
@---------------------------------------------------------------------------------
|
|
||||||
@ IO_INTERFACE data -- 32 bytes
|
|
||||||
_io_dldi:
|
|
||||||
.ascii "DLDI" @ ioType
|
|
||||||
.word 0x00000000 @ Features
|
|
||||||
.word _DLDI_startup @
|
|
||||||
.word _DLDI_isInserted @
|
|
||||||
.word _DLDI_readSectors @ Function pointers to standard device driver functions
|
|
||||||
.word _DLDI_writeSectors @
|
|
||||||
.word _DLDI_clearStatus @
|
|
||||||
.word _DLDI_shutdown @
|
|
||||||
|
|
||||||
@---------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_DLDI_startup:
|
|
||||||
_DLDI_isInserted:
|
|
||||||
_DLDI_readSectors:
|
|
||||||
_DLDI_writeSectors:
|
|
||||||
_DLDI_clearStatus:
|
|
||||||
_DLDI_shutdown:
|
|
||||||
mov r0, #0x00 @ Return false for every function
|
|
||||||
bx lr
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@---------------------------------------------------------------------------------
|
|
||||||
.align
|
|
||||||
.pool
|
|
||||||
|
|
||||||
.space (_dldi_start + 32768) - . @ Fill to 32KiB
|
|
||||||
|
|
||||||
_dldi_end:
|
|
||||||
.end
|
|
||||||
@---------------------------------------------------------------------------------
|
|
@ -1,307 +0,0 @@
|
|||||||
/*
|
|
||||||
io_efa2.c by CyteX
|
|
||||||
|
|
||||||
Based on io_mpfc.c by chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Hardware Routines for reading the NAND flash located on
|
|
||||||
EFA2 flash carts
|
|
||||||
|
|
||||||
This software is completely free. No warranty is provided.
|
|
||||||
If you use it, please give me credit and email me about your
|
|
||||||
project at cytex <at> gmx <dot> de and do not forget to also
|
|
||||||
drop chishm <at> hotmail <dot> com a line
|
|
||||||
|
|
||||||
Use with permission by Michael "Chishm" Chisholm
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "io_efa2.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// EFA2 register addresses
|
|
||||||
//
|
|
||||||
|
|
||||||
// RTC registers
|
|
||||||
#define REG_RTC_CLK (*(vu16*)0x080000c4)
|
|
||||||
#define REG_RTC_EN (*(vu16*)0x080000c8)
|
|
||||||
|
|
||||||
// "Magic" registers used for unlock/lock sequences
|
|
||||||
#define REG_EFA2_MAGIC_A (*(vu16*)0x09fe0000)
|
|
||||||
#define REG_EFA2_MAGIC_B (*(vu16*)0x08000000)
|
|
||||||
#define REG_EFA2_MAGIC_C (*(vu16*)0x08020000)
|
|
||||||
#define REG_EFA2_MAGIC_D (*(vu16*)0x08040000)
|
|
||||||
#define REG_EFA2_MAGIC_E (*(vu16*)0x09fc0000)
|
|
||||||
|
|
||||||
// NAND flash lock/unlock register
|
|
||||||
#define REG_EFA2_NAND_LOCK (*(vu16*)0x09c40000)
|
|
||||||
// NAND flash enable register
|
|
||||||
#define REG_EFA2_NAND_EN (*(vu16*)0x09400000)
|
|
||||||
// NAND flash command write register
|
|
||||||
#define REG_EFA2_NAND_CMD (*(vu8*)0x09ffffe2)
|
|
||||||
// NAND flash address/data write register
|
|
||||||
#define REG_EFA2_NAND_WR (*(vu8*)0x09ffffe0)
|
|
||||||
// NAND flash data read register
|
|
||||||
#define REG_EFA2_NAND_RD (*(vu8*)0x09ffc000)
|
|
||||||
|
|
||||||
// ID of Samsung K9K1G NAND flash chip
|
|
||||||
#define EFA2_NAND_ID 0xEC79A5C0
|
|
||||||
|
|
||||||
// first sector of udisk
|
|
||||||
#define EFA2_UDSK_START 0x40
|
|
||||||
|
|
||||||
//
|
|
||||||
// EFA2 access functions
|
|
||||||
//
|
|
||||||
|
|
||||||
// deactivate RTC ports
|
|
||||||
static inline void _EFA2_rtc_deactivate(void) {
|
|
||||||
REG_RTC_EN = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// unlock register access
|
|
||||||
static void _EFA2_reg_unlock(void) {
|
|
||||||
REG_EFA2_MAGIC_A = 0x0d200;
|
|
||||||
REG_EFA2_MAGIC_B = 0x01500;
|
|
||||||
REG_EFA2_MAGIC_C = 0x0d200;
|
|
||||||
REG_EFA2_MAGIC_D = 0x01500;
|
|
||||||
}
|
|
||||||
|
|
||||||
// finish/lock register access
|
|
||||||
static inline void _EFA2_reg_lock(void) {
|
|
||||||
REG_EFA2_MAGIC_E = 0x1500;
|
|
||||||
}
|
|
||||||
|
|
||||||
// global reset/init/enable/unlock ?
|
|
||||||
static void _EFA2_global_unlock(void) {
|
|
||||||
_EFA2_reg_unlock();
|
|
||||||
*(vu16*)0x09880000 = 0x08000;
|
|
||||||
_EFA2_reg_lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
// global lock, stealth mode
|
|
||||||
/*static void _EFA2_global_lock(void) {
|
|
||||||
// quite sure there is such a sequence, but haven't had
|
|
||||||
// a look for it upto now
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// unlock NAND Flash
|
|
||||||
static void _EFA2_nand_unlock(void) {
|
|
||||||
_EFA2_reg_unlock();
|
|
||||||
REG_EFA2_NAND_LOCK = 0x01500;
|
|
||||||
_EFA2_reg_lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
// lock NAND Flash
|
|
||||||
static void _EFA2_nand_lock(void) {
|
|
||||||
_EFA2_reg_unlock();
|
|
||||||
REG_EFA2_NAND_LOCK = 0x0d200;
|
|
||||||
_EFA2_reg_lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Set NAND Flash chip enable and write protection bits ?
|
|
||||||
//
|
|
||||||
// val | ~CE | ~WP |
|
|
||||||
// -----+-----+-----+
|
|
||||||
// 0 | 0 | 0 |
|
|
||||||
// 1 | 1 | 0 |
|
|
||||||
// 3 | 1 | 1 |
|
|
||||||
// -----+-----+-----+
|
|
||||||
//
|
|
||||||
static void _EFA2_nand_enable(u16 val) {
|
|
||||||
_EFA2_reg_unlock();
|
|
||||||
REG_EFA2_NAND_EN = val;
|
|
||||||
_EFA2_reg_lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Perform NAND reset
|
|
||||||
// NAND has to be unlocked and enabled when called
|
|
||||||
//
|
|
||||||
static inline void _EFA2_nand_reset(void) {
|
|
||||||
REG_EFA2_NAND_CMD = 0xff; // write reset command
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Read out NAND ID information, could be used for card detection
|
|
||||||
//
|
|
||||||
// | EFA2 1GBit |
|
|
||||||
// ------------------+------------+
|
|
||||||
// maker code | 0xEC |
|
|
||||||
// device code | 0x79 |
|
|
||||||
// don't care | 0xA5 |
|
|
||||||
// multi plane code | 0xC0 |
|
|
||||||
// ------------------+------------+
|
|
||||||
//
|
|
||||||
static u32 _EFA2_nand_id(void) {
|
|
||||||
u8 byte;
|
|
||||||
u32 id;
|
|
||||||
|
|
||||||
_EFA2_nand_unlock();
|
|
||||||
_EFA2_nand_enable(1);
|
|
||||||
|
|
||||||
REG_EFA2_NAND_CMD = 0x90; // write id command
|
|
||||||
REG_EFA2_NAND_WR = 0x00; // (dummy) address cycle
|
|
||||||
byte = REG_EFA2_NAND_RD; // read maker code
|
|
||||||
id = byte;
|
|
||||||
byte = REG_EFA2_NAND_RD; // read device code
|
|
||||||
id = (id << 8) | byte;
|
|
||||||
byte = REG_EFA2_NAND_RD; // read don't care
|
|
||||||
id = (id << 8) | byte;
|
|
||||||
byte = REG_EFA2_NAND_RD; // read multi plane code
|
|
||||||
id = (id << 8) | byte;
|
|
||||||
|
|
||||||
_EFA2_nand_enable(0);
|
|
||||||
_EFA2_nand_lock();
|
|
||||||
return (id);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Start of gba_nds_fat block device description
|
|
||||||
//
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
EFA2_clearStatus
|
|
||||||
Reads and checks NAND status information
|
|
||||||
bool return OUT: true if NAND is idle
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _EFA2_clearStatus (void)
|
|
||||||
{
|
|
||||||
// tbd: currently there is no write support, so always return
|
|
||||||
// true, there is no possibility for pending operations
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
EFA2_isInserted
|
|
||||||
Checks to see if the NAND chip used by the EFA2 is present
|
|
||||||
bool return OUT: true if the correct NAND chip is found
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _EFA2_isInserted (void)
|
|
||||||
{
|
|
||||||
_EFA2_clearStatus();
|
|
||||||
return (_EFA2_nand_id() == EFA2_NAND_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
EFA2_readSectors
|
|
||||||
Read "numSecs" 512 byte sectors starting from "sector" into "buffer"
|
|
||||||
No error correction, no use of spare cells, no use of R/~B signal
|
|
||||||
u32 sector IN: number of first 512 byte sector to be read
|
|
||||||
u32 numSecs IN: number of 512 byte sectors to read,
|
|
||||||
void* buffer OUT: pointer to 512 byte buffer to store data in
|
|
||||||
bool return OUT: true if successful
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _EFA2_readSectors (u32 sector, u32 numSecs, void* buffer)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
#ifndef _IO_ALLOW_UNALIGNED
|
|
||||||
u8 byte;
|
|
||||||
u16 word;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// NAND page 0x40 (EFA2_UDSK_START) contains the MBR of the
|
|
||||||
// udisk and thus is sector 0. The original EFA2 firmware
|
|
||||||
// does never look at this, it only watches page 0x60, which
|
|
||||||
// contains the boot block of the FAT16 partition. That is
|
|
||||||
// fixed, so the EFA2 udisk must not be reformated, else
|
|
||||||
// the ARK Octopus and also the original Firmware won't be
|
|
||||||
// able to access the udisk anymore and I have to write a
|
|
||||||
// recovery tool.
|
|
||||||
u32 page = EFA2_UDSK_START + sector;
|
|
||||||
|
|
||||||
// future enhancement: wait for possible write operations to
|
|
||||||
// be finisched
|
|
||||||
if (!_EFA2_clearStatus()) return false;
|
|
||||||
|
|
||||||
_EFA2_nand_unlock();
|
|
||||||
_EFA2_nand_enable(1);
|
|
||||||
_EFA2_nand_reset();
|
|
||||||
|
|
||||||
// set NAND to READ1 operation mode and transfer page address
|
|
||||||
REG_EFA2_NAND_CMD = 0x00; // write READ1 command
|
|
||||||
REG_EFA2_NAND_WR = 0x00; // write address [7:0]
|
|
||||||
REG_EFA2_NAND_WR = (page ) & 0xff; // write address [15:8]
|
|
||||||
REG_EFA2_NAND_WR = (page >> 8 ) & 0xff; // write address[23:16]
|
|
||||||
REG_EFA2_NAND_WR = (page >> 16) & 0xff; // write address[26:24]
|
|
||||||
|
|
||||||
// Due to a bug in EFA2 design there is need to waste some cycles
|
|
||||||
// "by hand" instead the possibility to check the R/~B port of
|
|
||||||
// the NAND flash via a register. The RTC deactivation is only
|
|
||||||
// there to make sure the loop won't be optimized by the compiler
|
|
||||||
for (i=0 ; i < 3 ; i++) _EFA2_rtc_deactivate();
|
|
||||||
|
|
||||||
while (numSecs--)
|
|
||||||
{
|
|
||||||
// read page data
|
|
||||||
#ifdef _IO_ALLOW_UNALIGNED
|
|
||||||
// slow byte access to RAM, but works in principle
|
|
||||||
for (i=0 ; i < 512 ; i++)
|
|
||||||
((u8*)buffer)[i] = REG_EFA2_NAND_RD;
|
|
||||||
#else
|
|
||||||
// a bit faster, but DMA is not possible
|
|
||||||
for (i=0 ; i < 256 ; i++) {
|
|
||||||
byte = REG_EFA2_NAND_RD; // read lo-byte
|
|
||||||
word = byte;
|
|
||||||
byte = REG_EFA2_NAND_RD; // read hi-byte
|
|
||||||
word = word | (byte << 8);
|
|
||||||
((u16*)buffer)[i] = word;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
_EFA2_nand_enable(0);
|
|
||||||
_EFA2_nand_lock();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
EFA2_writeSectors
|
|
||||||
Write "numSecs" 512 byte sectors starting at "sector" from "buffer"
|
|
||||||
u32 sector IN: address of 512 byte sector on card to write
|
|
||||||
u32 numSecs IN: number of 512 byte sectors to write
|
|
||||||
1 to 256 sectors can be written, 0 = 256
|
|
||||||
void* buffer IN: pointer to 512 byte buffer to read data from
|
|
||||||
bool return OUT: true if successful
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _EFA2_writeSectors (u32 sector, u8 numSecs, void* buffer)
|
|
||||||
{
|
|
||||||
// Upto now I focused on reading NAND, write operations
|
|
||||||
// will follow
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
EFA2_shutdown
|
|
||||||
unload the EFA2 interface
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _EFA2_shutdown(void)
|
|
||||||
{
|
|
||||||
return _EFA2_clearStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
EFA2_startUp
|
|
||||||
initializes the EFA2 card, returns true if successful,
|
|
||||||
otherwise returns false
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _EFA2_startUp(void)
|
|
||||||
{
|
|
||||||
_EFA2_global_unlock();
|
|
||||||
return (_EFA2_nand_id() == EFA2_NAND_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
the actual interface structure
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
const IO_INTERFACE _io_efa2 = {
|
|
||||||
DEVICE_TYPE_EFA2,
|
|
||||||
FEATURE_MEDIUM_CANREAD | FEATURE_SLOT_GBA,
|
|
||||||
(FN_MEDIUM_STARTUP)&_EFA2_startUp,
|
|
||||||
(FN_MEDIUM_ISINSERTED)&_EFA2_isInserted,
|
|
||||||
(FN_MEDIUM_READSECTORS)&_EFA2_readSectors,
|
|
||||||
(FN_MEDIUM_WRITESECTORS)&_EFA2_writeSectors,
|
|
||||||
(FN_MEDIUM_CLEARSTATUS)&_EFA2_clearStatus,
|
|
||||||
(FN_MEDIUM_SHUTDOWN)&_EFA2_shutdown
|
|
||||||
};
|
|
@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
io_efa2.h by CyteX
|
|
||||||
|
|
||||||
Based on io_mpfc.h by chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Hardware Routines for reading the NAND flash located on
|
|
||||||
EFA2 flash carts
|
|
||||||
|
|
||||||
Used with permission from Cytex.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_EFA2_H
|
|
||||||
#define IO_EFA2_H
|
|
||||||
|
|
||||||
// 'EFA2'
|
|
||||||
#define DEVICE_TYPE_EFA2 0x32414645
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_efa2;
|
|
||||||
|
|
||||||
#endif // define IO_EFA2_H
|
|
@ -1,334 +0,0 @@
|
|||||||
/*
|
|
||||||
io_fcsr.c based on
|
|
||||||
|
|
||||||
compact_flash.c
|
|
||||||
By chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Hardware Routines for using a GBA Flash Cart and SRAM as a
|
|
||||||
block device.
|
|
||||||
|
|
||||||
The file system must be 512 byte aligned, in cart address space.
|
|
||||||
SRAM is supported.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "io_fcsr.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// DMA
|
|
||||||
#ifdef _IO_USE_DMA
|
|
||||||
#ifndef NDS
|
|
||||||
#include "gba_dma.h"
|
|
||||||
#else
|
|
||||||
#include <nds/dma.h>
|
|
||||||
#ifdef ARM9
|
|
||||||
#include <nds/arm9/cache.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef NDS
|
|
||||||
#define SRAM_START 0x0A000000
|
|
||||||
#else
|
|
||||||
#define SRAM_START 0x0E000000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define NO_SRAM 0xFFFFFFFF
|
|
||||||
|
|
||||||
#define FCSR 0x52534346
|
|
||||||
const char _FCSR_LabelString[] = " Chishm FAT";
|
|
||||||
|
|
||||||
u8* _FCSR_FileSysPointer = 0;
|
|
||||||
u8* _FCSR_SramSectorPointer[4] = {0,0,0,0};
|
|
||||||
u32 _FCSR_SramSectorStart[4] = {0,0,0,0};
|
|
||||||
u32 _FCSR_SramSectorEnd[4] = {0,0,0,0};
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FCSR_isInserted
|
|
||||||
Is a GBA Flash Cart with a valid file system inserted?
|
|
||||||
bool return OUT: true if a GBA FC card is inserted
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _FCSR_isInserted (void)
|
|
||||||
{
|
|
||||||
bool flagFoundFileSys = false;
|
|
||||||
|
|
||||||
u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
|
|
||||||
|
|
||||||
// Search for file system
|
|
||||||
while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
|
|
||||||
{
|
|
||||||
while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
|
|
||||||
fileSysPointer += 0x40;
|
|
||||||
if ((strncmp(_FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
|
|
||||||
{
|
|
||||||
flagFoundFileSys = true;
|
|
||||||
} else {
|
|
||||||
fileSysPointer += 0x80;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return flagFoundFileSys;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FCSR_clearStatus
|
|
||||||
Finish any pending operations
|
|
||||||
bool return OUT: always true for GBA FC
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _FCSR_clearStatus (void)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FCSR_readSectors
|
|
||||||
Read 512 byte sector numbered "sector" into "buffer"
|
|
||||||
u32 sector IN: address of first 512 byte sector on Flash Cart to read
|
|
||||||
u32 numSectors IN: number of 512 byte sectors to read,
|
|
||||||
1 to 256 sectors can be read
|
|
||||||
void* buffer OUT: pointer to 512 byte buffer to store data in
|
|
||||||
bool return OUT: true if successful
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _FCSR_readSectors (u32 sector, u32 numSectors, void* buffer)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
bool flagSramSector = false;
|
|
||||||
int readLength = numSectors * BYTES_PER_READ;
|
|
||||||
u8* src;;
|
|
||||||
u8* dst;
|
|
||||||
|
|
||||||
// Find which region this read is in
|
|
||||||
for (i = 0; (i < 4) && !flagSramSector; i++)
|
|
||||||
{
|
|
||||||
if ((sector >= _FCSR_SramSectorStart[i]) && (sector < _FCSR_SramSectorEnd[i]))
|
|
||||||
{
|
|
||||||
flagSramSector = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure read will be completely in SRAM range if it is partially there
|
|
||||||
if ( flagSramSector && ((sector + numSectors) > _FCSR_SramSectorEnd[i]))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Copy data to buffer
|
|
||||||
if (flagSramSector)
|
|
||||||
{
|
|
||||||
src = _FCSR_SramSectorPointer[i] + (sector - _FCSR_SramSectorStart[i]) * BYTES_PER_READ;
|
|
||||||
} else {
|
|
||||||
src = _FCSR_FileSysPointer + sector * BYTES_PER_READ;
|
|
||||||
}
|
|
||||||
dst = (u8*)buffer;
|
|
||||||
|
|
||||||
if (flagSramSector)
|
|
||||||
{
|
|
||||||
while (readLength--)
|
|
||||||
{
|
|
||||||
*dst++ = *src++;
|
|
||||||
}
|
|
||||||
} else { // Reading from Cart ROM
|
|
||||||
|
|
||||||
#ifdef _IO_USE_DMA
|
|
||||||
#ifdef NDS
|
|
||||||
#ifdef ARM9
|
|
||||||
DC_FlushRange( buffer, readLength);
|
|
||||||
#endif // ARM9
|
|
||||||
DMA3_SRC = (u32)src;
|
|
||||||
DMA3_DEST = (u32)buffer;
|
|
||||||
DMA3_CR = (readLength >> 1) | DMA_COPY_HALFWORDS;
|
|
||||||
#else // ! NDS
|
|
||||||
DMA3COPY ( src, buffer, (readLength >> 1) | DMA16 | DMA_ENABLE);
|
|
||||||
#endif // NDS
|
|
||||||
#else // !_IO_USE_DMA
|
|
||||||
memcpy (buffer, src, readLength);
|
|
||||||
#endif // _IO_USE_DMA
|
|
||||||
|
|
||||||
} // if (flagSramSector)
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FCSR_writeSectors
|
|
||||||
Write 512 byte sector numbered "sector" from "buffer"
|
|
||||||
u32 sector IN: address of 512 byte sector on Flash Cart to read
|
|
||||||
u32 numSectors IN: number of 512 byte sectors to read,
|
|
||||||
1 to 256 sectors can be read
|
|
||||||
void* buffer IN: pointer to 512 byte buffer to read data from
|
|
||||||
bool return OUT: true if successful
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _FCSR_writeSectors (u32 sector, u8 numSectors, void* buffer)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
bool flagSramSector = false;
|
|
||||||
u32 writeLength = numSectors * BYTES_PER_READ;
|
|
||||||
u8* src = (u8*) buffer;
|
|
||||||
u8* dst;
|
|
||||||
|
|
||||||
// Find which region this sector belongs in
|
|
||||||
for (i = 0; (i < 4) && !flagSramSector; i++)
|
|
||||||
{
|
|
||||||
if ((sector >= _FCSR_SramSectorStart[i]) && (sector < _FCSR_SramSectorEnd[i]))
|
|
||||||
{
|
|
||||||
flagSramSector = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!flagSramSector)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Entire write must be within an SRAM region
|
|
||||||
if ((sector + numSectors) > _FCSR_SramSectorEnd[i])
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Copy data to SRAM
|
|
||||||
dst = _FCSR_SramSectorPointer[i] + (sector - _FCSR_SramSectorStart[i]) * BYTES_PER_READ;
|
|
||||||
while (writeLength--)
|
|
||||||
{
|
|
||||||
*dst++ = *src++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FCSR_shutdown
|
|
||||||
unload the Flash Cart interface
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _FCSR_shutdown(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
if (_FCSR_clearStatus() == false)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_FCSR_FileSysPointer = 0;
|
|
||||||
|
|
||||||
for (i=0; i < 4; i++)
|
|
||||||
{
|
|
||||||
_FCSR_SramSectorPointer[i] = 0;
|
|
||||||
_FCSR_SramSectorStart[i] = 0;
|
|
||||||
_FCSR_SramSectorEnd[i] = 0;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FCSR_startUp
|
|
||||||
initializes the Flash Cart interface, returns true if successful,
|
|
||||||
otherwise returns false
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _FCSR_startUp(void)
|
|
||||||
{
|
|
||||||
bool flagFoundFileSys = false;
|
|
||||||
int i;
|
|
||||||
int SramRegionSize[4];
|
|
||||||
u8* srcByte;
|
|
||||||
u8* destByte;
|
|
||||||
|
|
||||||
u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
|
|
||||||
|
|
||||||
// Search for file system
|
|
||||||
while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
|
|
||||||
{
|
|
||||||
while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
|
|
||||||
fileSysPointer += 0x40;
|
|
||||||
if ((strncmp(_FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
|
|
||||||
{
|
|
||||||
flagFoundFileSys = true;
|
|
||||||
} else {
|
|
||||||
fileSysPointer += 0x80;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!flagFoundFileSys)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Flash cart file system pointer has been found
|
|
||||||
_FCSR_FileSysPointer = (u8*)(fileSysPointer - 0x40);
|
|
||||||
|
|
||||||
// Get SRAM sector regions from header block
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
_FCSR_SramSectorStart[i] = fileSysPointer[i+4];
|
|
||||||
SramRegionSize[i] = fileSysPointer[i+8];
|
|
||||||
_FCSR_SramSectorEnd[i] = _FCSR_SramSectorStart[i] + SramRegionSize[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate SRAM region pointers
|
|
||||||
_FCSR_SramSectorPointer[0] = (u8*)(SRAM_START + 4);
|
|
||||||
for (i = 1; i < 4; i++)
|
|
||||||
{
|
|
||||||
_FCSR_SramSectorPointer[i] = _FCSR_SramSectorPointer[i-1] + (SramRegionSize[i-1] * BYTES_PER_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise SRAM with overlay if it hasn't been done so
|
|
||||||
if ( (*((u8*)SRAM_START) != 'F') || (*((u8*)(SRAM_START+1)) != 'C') || (*((u8*)(SRAM_START+2)) != 'S') || (*((u8*)(SRAM_START+3)) != 'R') )
|
|
||||||
{
|
|
||||||
*((u8*)SRAM_START) = 'F';
|
|
||||||
*((u8*)(SRAM_START+1)) = 'C';
|
|
||||||
*((u8*)(SRAM_START+2)) = 'S';
|
|
||||||
*((u8*)(SRAM_START+3)) = 'R';
|
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
srcByte = _FCSR_FileSysPointer + (_FCSR_SramSectorStart[i] * BYTES_PER_READ);
|
|
||||||
destByte = _FCSR_SramSectorPointer[i];
|
|
||||||
while (srcByte < _FCSR_FileSysPointer + (_FCSR_SramSectorEnd[i] * BYTES_PER_READ) )
|
|
||||||
*destByte++ = *srcByte++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get SRAM sector regions from header block
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
if (SramRegionSize[i] == 0)
|
|
||||||
{
|
|
||||||
_FCSR_SramSectorStart[i] = NO_SRAM;
|
|
||||||
_FCSR_SramSectorEnd[i] = NO_SRAM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
the actual interface structure
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
const IO_INTERFACE _io_fcsr = {
|
|
||||||
DEVICE_TYPE_FCSR, // 'FCSR'
|
|
||||||
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
|
|
||||||
(FN_MEDIUM_STARTUP)&_FCSR_startUp,
|
|
||||||
(FN_MEDIUM_ISINSERTED)&_FCSR_isInserted,
|
|
||||||
(FN_MEDIUM_READSECTORS)&_FCSR_readSectors,
|
|
||||||
(FN_MEDIUM_WRITESECTORS)&_FCSR_writeSectors,
|
|
||||||
(FN_MEDIUM_CLEARSTATUS)&_FCSR_clearStatus,
|
|
||||||
(FN_MEDIUM_SHUTDOWN)&_FCSR_shutdown
|
|
||||||
} ;
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
io_fcsr.h
|
|
||||||
|
|
||||||
Hardware Routines for using a GBA Flash Cart with SRAM
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_FCSR_H
|
|
||||||
#define IO_FCSR_H
|
|
||||||
|
|
||||||
// 'FCSR'
|
|
||||||
#define DEVICE_TYPE_FCSR 0x52534346
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_fcsr ;
|
|
||||||
|
|
||||||
#endif // define IO_FCSR_H
|
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
io_m3_common.c
|
|
||||||
|
|
||||||
Routines common to all version of the M3
|
|
||||||
|
|
||||||
Some code based on M3 SD drivers supplied by M3Adapter.
|
|
||||||
Some code written by SaTa may have been unknowingly used.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "io_m3_common.h"
|
|
||||||
|
|
||||||
static u16 _M3_readHalfword (u32 addr) {
|
|
||||||
return *((vu16*)addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _M3_changeMode (u32 mode) {
|
|
||||||
_M3_readHalfword (0x08e00002);
|
|
||||||
_M3_readHalfword (0x0800000e);
|
|
||||||
_M3_readHalfword (0x08801ffc);
|
|
||||||
_M3_readHalfword (0x0800104a);
|
|
||||||
_M3_readHalfword (0x08800612);
|
|
||||||
_M3_readHalfword (0x08000000);
|
|
||||||
_M3_readHalfword (0x08801b66);
|
|
||||||
_M3_readHalfword (0x08000000 + (mode << 1));
|
|
||||||
_M3_readHalfword (0x0800080e);
|
|
||||||
_M3_readHalfword (0x08000000);
|
|
||||||
|
|
||||||
if ((mode & 0x0f) != 4) {
|
|
||||||
_M3_readHalfword (0x09000000);
|
|
||||||
} else {
|
|
||||||
_M3_readHalfword (0x080001e4);
|
|
||||||
_M3_readHalfword (0x080001e4);
|
|
||||||
_M3_readHalfword (0x08000188);
|
|
||||||
_M3_readHalfword (0x08000188);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
io_m3_common.h
|
|
||||||
|
|
||||||
Routines common to all version of the M3
|
|
||||||
|
|
||||||
Some code based on M3 SD drivers supplied by M3Adapter.
|
|
||||||
Some code written by SaTa may have been unknowingly used.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_M3_COMMON_H
|
|
||||||
#define IO_M3_COMMON_H
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// Values for changing mode
|
|
||||||
#define M3_MODE_ROM 0x00400004
|
|
||||||
#define M3_MODE_MEDIA 0x00400003
|
|
||||||
|
|
||||||
extern void _M3_changeMode (u32 mode);
|
|
||||||
|
|
||||||
#endif // IO_M3_COMMON_H
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
io_m3cf.c based on
|
|
||||||
|
|
||||||
compact_flash.c
|
|
||||||
By chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Hardware Routines for reading a compact flash card
|
|
||||||
using the M3 Perfect CF Adapter
|
|
||||||
|
|
||||||
CF routines modified with help from Darkfader
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "io_m3cf.h"
|
|
||||||
#include "io_m3_common.h"
|
|
||||||
#include "io_cf_common.h"
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// DMA
|
|
||||||
#ifdef _IO_USE_DMA
|
|
||||||
#ifndef NDS
|
|
||||||
#include "gba_dma.h"
|
|
||||||
#else
|
|
||||||
#include <nds/dma.h>
|
|
||||||
#ifdef ARM9
|
|
||||||
#include <nds/arm9/cache.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// M3 CF Addresses
|
|
||||||
#define REG_M3CF_STS ((vu16*)0x080C0000) // Status of the CF Card / Device control
|
|
||||||
#define REG_M3CF_CMD ((vu16*)0x088E0000) // Commands sent to control chip and status return
|
|
||||||
#define REG_M3CF_ERR ((vu16*)0x08820000) // Errors / Features
|
|
||||||
|
|
||||||
#define REG_M3CF_SEC ((vu16*)0x08840000) // Number of sector to transfer
|
|
||||||
#define REG_M3CF_LBA1 ((vu16*)0x08860000) // 1st byte of sector address
|
|
||||||
#define REG_M3CF_LBA2 ((vu16*)0x08880000) // 2nd byte of sector address
|
|
||||||
#define REG_M3CF_LBA3 ((vu16*)0x088A0000) // 3rd byte of sector address
|
|
||||||
#define REG_M3CF_LBA4 ((vu16*)0x088C0000) // last nibble of sector address | 0xE0
|
|
||||||
|
|
||||||
#define REG_M3CF_DATA ((vu16*)0x08800000) // Pointer to buffer of CF data transered from card
|
|
||||||
|
|
||||||
static const CF_REGISTERS _M3CF_Registers = {
|
|
||||||
REG_M3CF_DATA,
|
|
||||||
REG_M3CF_STS,
|
|
||||||
REG_M3CF_CMD,
|
|
||||||
REG_M3CF_ERR,
|
|
||||||
REG_M3CF_SEC,
|
|
||||||
REG_M3CF_LBA1,
|
|
||||||
REG_M3CF_LBA2,
|
|
||||||
REG_M3CF_LBA3,
|
|
||||||
REG_M3CF_LBA4
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
bool _M3CF_startup(void) {
|
|
||||||
_M3_changeMode (M3_MODE_MEDIA);
|
|
||||||
return _CF_startup (&_M3CF_Registers);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const IO_INTERFACE _io_m3cf = {
|
|
||||||
DEVICE_TYPE_M3CF,
|
|
||||||
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
|
|
||||||
(FN_MEDIUM_STARTUP)&_M3CF_startup,
|
|
||||||
(FN_MEDIUM_ISINSERTED)&_CF_isInserted,
|
|
||||||
(FN_MEDIUM_READSECTORS)&_CF_readSectors,
|
|
||||||
(FN_MEDIUM_WRITESECTORS)&_CF_writeSectors,
|
|
||||||
(FN_MEDIUM_CLEARSTATUS)&_CF_clearStatus,
|
|
||||||
(FN_MEDIUM_SHUTDOWN)&_CF_shutdown
|
|
||||||
} ;
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
io_m3cf.h
|
|
||||||
|
|
||||||
Hardware Routines for reading a compact flash card
|
|
||||||
using the M3 CF
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_M3CF_H
|
|
||||||
#define IO_M3CF_H
|
|
||||||
|
|
||||||
// 'M3CF'
|
|
||||||
#define DEVICE_TYPE_M3CF 0x4643334D
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_m3cf ;
|
|
||||||
|
|
||||||
#endif // define IO_M3CF_H
|
|
@ -1,518 +0,0 @@
|
|||||||
/*
|
|
||||||
io_m3sd.c
|
|
||||||
|
|
||||||
Hardware Routines for reading a Secure Digital card
|
|
||||||
using the M3 SD
|
|
||||||
|
|
||||||
Some code based on M3 SD drivers supplied by M3Adapter.
|
|
||||||
Some code written by SaTa may have been unknowingly used.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-25 - Chishm
|
|
||||||
* Improved startup function that doesn't delay hundreds of seconds
|
|
||||||
before reporting no card inserted.
|
|
||||||
* Fixed writeData function to timeout on error
|
|
||||||
* writeSectors function now wait until the card is ready before continuing with a transfer
|
|
||||||
|
|
||||||
2006-08-05 - Chishm
|
|
||||||
* Tries multiple times to get a Relative Card Address at startup
|
|
||||||
|
|
||||||
2006-08-07 - Chishm
|
|
||||||
* Moved the SD initialization to a common function
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "io_m3sd.h"
|
|
||||||
#include "io_sd_common.h"
|
|
||||||
#include "io_m3_common.h"
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// M3SD register addresses
|
|
||||||
|
|
||||||
#define REG_M3SD_DIR (*(vu16*)0x08800000) // direction control register
|
|
||||||
#define REG_M3SD_DAT (*(vu16*)0x09000000) // SD data line, 8 bits at a time
|
|
||||||
#define REG_M3SD_CMD (*(vu16*)0x09200000) // SD command byte
|
|
||||||
#define REG_M3SD_ARGH (*(vu16*)0x09400000) // SD command argument, high halfword
|
|
||||||
#define REG_M3SD_ARGL (*(vu16*)0x09600000) // SD command argument, low halfword
|
|
||||||
#define REG_M3SD_STS (*(vu16*)0x09800000) // command and status register
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Send / receive timeouts, to stop infinite wait loops
|
|
||||||
#define NUM_STARTUP_CLOCKS 100 // Number of empty (0xFF when sending) bytes to send/receive to/from the card
|
|
||||||
#define TRANSMIT_TIMEOUT 20000 // Time to wait for the M3 to respond to transmit or receive requests
|
|
||||||
#define RESPONSE_TIMEOUT 256 // Number of clocks sent to the SD card before giving up
|
|
||||||
#define WRITE_TIMEOUT 3000 // Time to wait for the card to finish writing
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Variables required for tracking SD state
|
|
||||||
static u32 _M3SD_relativeCardAddress = 0; // Preshifted Relative Card Address
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Internal M3 SD functions
|
|
||||||
|
|
||||||
static inline void _M3SD_unlock (void) {
|
|
||||||
_M3_changeMode (M3_MODE_MEDIA);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _M3SD_waitOnBusy (void) {
|
|
||||||
int i = 0;
|
|
||||||
while ( (REG_M3SD_STS & 0x01) == 0x00) {
|
|
||||||
i++;
|
|
||||||
if (i >= TRANSMIT_TIMEOUT) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _M3SD_waitForDataReady (void) {
|
|
||||||
int i = 0;
|
|
||||||
while ( (REG_M3SD_STS & 0x40) == 0x00) {
|
|
||||||
i++;
|
|
||||||
if (i >= TRANSMIT_TIMEOUT) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool _M3SD_sendCommand (u16 command, u32 argument) {
|
|
||||||
REG_M3SD_STS = 0x8;
|
|
||||||
REG_M3SD_CMD = 0x40 + command; // Include the start bit
|
|
||||||
REG_M3SD_ARGH = argument >> 16;
|
|
||||||
REG_M3SD_ARGL = argument;
|
|
||||||
// The CRC7 of the command is calculated by the M3
|
|
||||||
|
|
||||||
REG_M3SD_DIR=0x29;
|
|
||||||
if (!_M3SD_waitOnBusy()) {
|
|
||||||
REG_M3SD_DIR=0x09;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
REG_M3SD_DIR=0x09;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _M3SD_sendByte (u8 byte) {
|
|
||||||
int i = 0;
|
|
||||||
REG_M3SD_DAT = byte;
|
|
||||||
REG_M3SD_DIR = 0x03;
|
|
||||||
REG_M3SD_STS = 0x01;
|
|
||||||
while ((REG_M3SD_STS & 0x04) == 0) {
|
|
||||||
i++;
|
|
||||||
if (i >= TRANSMIT_TIMEOUT) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u8 _M3SD_getByte (void) {
|
|
||||||
int i;
|
|
||||||
// Request 8 bits of data from the SD's CMD pin
|
|
||||||
REG_M3SD_DIR = 0x02;
|
|
||||||
REG_M3SD_STS = 0x02;
|
|
||||||
// Wait for the data to be ready
|
|
||||||
i = 0;
|
|
||||||
while ((REG_M3SD_STS & 0x08) == 0) {
|
|
||||||
i++;
|
|
||||||
if (i >= TRANSMIT_TIMEOUT) {
|
|
||||||
// Return an empty byte if a timeout occurs
|
|
||||||
return 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i = 0;
|
|
||||||
while ((REG_M3SD_STS & 0x08) != 0) {
|
|
||||||
i++;
|
|
||||||
if (i >= TRANSMIT_TIMEOUT) {
|
|
||||||
// Return an empty byte if a timeout occurs
|
|
||||||
return 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return the data
|
|
||||||
return (REG_M3SD_DAT & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the response from the SD card to a previous command.
|
|
||||||
static bool _M3SD_getResponse (u8* dest, u32 length) {
|
|
||||||
u32 i;
|
|
||||||
u8 dataByte;
|
|
||||||
int shiftAmount;
|
|
||||||
|
|
||||||
// Wait for the card to be non-busy
|
|
||||||
for (i = 0; i < RESPONSE_TIMEOUT; i++) {
|
|
||||||
dataByte = _M3SD_getByte();
|
|
||||||
if (dataByte != SD_CARD_BUSY) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dest == NULL) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Still busy after the timeout has passed
|
|
||||||
if (dataByte == 0xff) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read response into buffer
|
|
||||||
for ( i = 0; i < length; i++) {
|
|
||||||
dest[i] = dataByte;
|
|
||||||
dataByte = _M3SD_getByte();
|
|
||||||
}
|
|
||||||
// dataByte will contain the last piece of the response
|
|
||||||
|
|
||||||
// Send 16 more clocks, 8 more than the delay required between a response and the next command
|
|
||||||
i = _M3SD_getByte();
|
|
||||||
i = _M3SD_getByte();
|
|
||||||
|
|
||||||
// Shift response so that the bytes are correctly aligned
|
|
||||||
// The register may not contain properly aligned data
|
|
||||||
for (shiftAmount = 0; ((dest[0] << shiftAmount) & 0x80) != 0x00; shiftAmount++) {
|
|
||||||
if (shiftAmount >= 7) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < length - 1; i++) {
|
|
||||||
dest[i] = (dest[i] << shiftAmount) | (dest[i+1] >> (8-shiftAmount));
|
|
||||||
}
|
|
||||||
// Get the last piece of the response from dataByte
|
|
||||||
dest[i] = (dest[i] << shiftAmount) | (dataByte >> (8-shiftAmount));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline bool _M3SD_getResponse_R1 (u8* dest) {
|
|
||||||
return _M3SD_getResponse (dest, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _M3SD_getResponse_R1b (u8* dest) {
|
|
||||||
return _M3SD_getResponse (dest, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _M3SD_getResponse_R2 (u8* dest) {
|
|
||||||
return _M3SD_getResponse (dest, 17);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _M3SD_getResponse_R3 (u8* dest) {
|
|
||||||
return _M3SD_getResponse (dest, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _M3SD_getResponse_R6 (u8* dest) {
|
|
||||||
return _M3SD_getResponse (dest, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _M3SD_sendClocks (u32 numClocks) {
|
|
||||||
while (numClocks--) {
|
|
||||||
_M3SD_sendByte(0xff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _M3SD_getClocks (u32 numClocks) {
|
|
||||||
while (numClocks--) {
|
|
||||||
_M3SD_getByte();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _M3SD_cmd_6byte_response (u8* responseBuffer, u8 command, u32 data) {
|
|
||||||
_M3SD_sendCommand (command, data);
|
|
||||||
return _M3SD_getResponse (responseBuffer, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _M3SD_cmd_17byte_response (u8* responseBuffer, u8 command, u32 data) {
|
|
||||||
_M3SD_sendCommand (command, data);
|
|
||||||
return _M3SD_getResponse (responseBuffer, 17);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _M3SD_initCard (void) {
|
|
||||||
// Give the card time to stabilise
|
|
||||||
_M3SD_sendClocks (NUM_STARTUP_CLOCKS);
|
|
||||||
|
|
||||||
// Reset the card
|
|
||||||
if (!_M3SD_sendCommand (GO_IDLE_STATE, 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_M3SD_getClocks (NUM_STARTUP_CLOCKS);
|
|
||||||
|
|
||||||
// Card is now reset, including it's address
|
|
||||||
_M3SD_relativeCardAddress = 0;
|
|
||||||
|
|
||||||
// Init the card
|
|
||||||
return _SD_InitCard (_M3SD_cmd_6byte_response,
|
|
||||||
_M3SD_cmd_17byte_response,
|
|
||||||
true,
|
|
||||||
&_M3SD_relativeCardAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _M3SD_readData (void* buffer) {
|
|
||||||
u32 i;
|
|
||||||
u8* buff_u8 = (u8*)buffer;
|
|
||||||
u16* buff = (u16*)buffer;
|
|
||||||
u16 temp;
|
|
||||||
|
|
||||||
REG_M3SD_DIR = 0x49;
|
|
||||||
if (!_M3SD_waitForDataReady()) {
|
|
||||||
REG_M3SD_DIR = 0x09;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
REG_M3SD_DIR = 0x09;
|
|
||||||
|
|
||||||
REG_M3SD_DIR = 0x8;
|
|
||||||
REG_M3SD_STS = 0x4;
|
|
||||||
|
|
||||||
i = REG_M3SD_DIR;
|
|
||||||
// Read data
|
|
||||||
i=256;
|
|
||||||
if ((u32)buff_u8 & 0x01) {
|
|
||||||
while(i--)
|
|
||||||
{
|
|
||||||
temp = REG_M3SD_DIR;
|
|
||||||
*buff_u8++ = temp & 0xFF;
|
|
||||||
*buff_u8++ = temp >> 8;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while(i--)
|
|
||||||
*buff++ = REG_M3SD_DIR;
|
|
||||||
}
|
|
||||||
// Read end checksum
|
|
||||||
i = REG_M3SD_DIR + REG_M3SD_DIR + REG_M3SD_DIR + REG_M3SD_DIR;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _M3SD_clkout (void) {
|
|
||||||
REG_M3SD_DIR = 0x4;
|
|
||||||
REG_M3SD_DIR = 0xc;
|
|
||||||
/* __asm volatile (
|
|
||||||
"ldr r1, =0x08800000 \n"
|
|
||||||
"mov r0, #0x04 \n"
|
|
||||||
"strh r0, [r1] \n"
|
|
||||||
"mov r0, r0 \n"
|
|
||||||
"mov r0, r0 \n"
|
|
||||||
"mov r0, #0x0c \n"
|
|
||||||
"strh r0, [r1] \n"
|
|
||||||
: // Outputs
|
|
||||||
: // Inputs
|
|
||||||
: "r0", "r1" // Clobber list
|
|
||||||
);*/
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _M3SD_clkin (void) {
|
|
||||||
REG_M3SD_DIR = 0x0;
|
|
||||||
REG_M3SD_DIR = 0x8;
|
|
||||||
/* __asm volatile (
|
|
||||||
"ldr r1, =0x08800000 \n"
|
|
||||||
"mov r0, #0x00 \n"
|
|
||||||
"strh r0, [r1] \n"
|
|
||||||
"mov r0, r0 \n"
|
|
||||||
"mov r0, r0 \n"
|
|
||||||
"mov r0, #0x08 \n"
|
|
||||||
"strh r0, [r1] \n"
|
|
||||||
: // Outputs
|
|
||||||
: // Inputs
|
|
||||||
: "r0", "r1" // Clobber list
|
|
||||||
);*/
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _M3SD_writeData (u8* data, u8* crc) {
|
|
||||||
int i;
|
|
||||||
u8 temp;
|
|
||||||
|
|
||||||
do {
|
|
||||||
_M3SD_clkin();
|
|
||||||
} while ((REG_M3SD_DAT & 0x100) == 0);
|
|
||||||
|
|
||||||
REG_M3SD_DAT = 0; // Start bit
|
|
||||||
|
|
||||||
_M3SD_clkout();
|
|
||||||
|
|
||||||
for (i = 0; i < BYTES_PER_READ; i++) {
|
|
||||||
temp = (*data++);
|
|
||||||
REG_M3SD_DAT = temp >> 4;
|
|
||||||
_M3SD_clkout();
|
|
||||||
REG_M3SD_DAT = temp;
|
|
||||||
_M3SD_clkout();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crc != NULL) {
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
temp = (*crc++);
|
|
||||||
REG_M3SD_DAT = temp >> 4;
|
|
||||||
_M3SD_clkout();
|
|
||||||
REG_M3SD_DAT = temp;
|
|
||||||
_M3SD_clkout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 32;
|
|
||||||
while (i--) {
|
|
||||||
temp += 2; // a NOP to stop the compiler optimising out the loop
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 32; i++) {
|
|
||||||
REG_M3SD_DAT = 0xff;
|
|
||||||
_M3SD_clkout();
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
_M3SD_clkin();
|
|
||||||
} while ((REG_M3SD_DAT & 0x100) == 0);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Functions needed for the external interface
|
|
||||||
|
|
||||||
bool _M3SD_startUp (void) {
|
|
||||||
_M3SD_unlock();
|
|
||||||
return _M3SD_initCard();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _M3SD_isInserted (void) {
|
|
||||||
u8 responseBuffer [6];
|
|
||||||
// Make sure the card receives the command
|
|
||||||
if (!_M3SD_sendCommand (SEND_STATUS, 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Make sure the card responds
|
|
||||||
if (!_M3SD_getResponse_R1 (responseBuffer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Make sure the card responded correctly
|
|
||||||
if (responseBuffer[0] != SEND_STATUS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _M3SD_readSectors (u32 sector, u32 numSectors, void* buffer) {
|
|
||||||
u32 i;
|
|
||||||
u8* dest = (u8*) buffer;
|
|
||||||
u8 responseBuffer[6];
|
|
||||||
|
|
||||||
if (numSectors == 1) {
|
|
||||||
// If it's only reading one sector, use the (slightly faster) READ_SINGLE_BLOCK
|
|
||||||
if (!_M3SD_sendCommand (READ_SINGLE_BLOCK, sector * BYTES_PER_READ)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_M3SD_readData (buffer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Stream the required number of sectors from the card
|
|
||||||
if (!_M3SD_sendCommand (READ_MULTIPLE_BLOCK, sector * BYTES_PER_READ)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i=0; i < numSectors; i++, dest+=BYTES_PER_READ) {
|
|
||||||
if (!_M3SD_readData(dest)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
REG_M3SD_STS = 0x8;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the streaming
|
|
||||||
_M3SD_sendCommand (STOP_TRANSMISSION, 0);
|
|
||||||
_M3SD_getResponse_R1b (responseBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _M3SD_writeSectors (u32 sector, u32 numSectors, const void* buffer) {
|
|
||||||
u8 crc[8];
|
|
||||||
u8 responseBuffer[6];
|
|
||||||
u32 offset = sector * BYTES_PER_READ;
|
|
||||||
u8* data = (u8*) buffer;
|
|
||||||
int i;
|
|
||||||
// Precalculate the data CRC
|
|
||||||
_SD_CRC16 ( data, BYTES_PER_READ, crc);
|
|
||||||
|
|
||||||
while (numSectors--) {
|
|
||||||
// Send a single sector write command
|
|
||||||
_M3SD_sendCommand (WRITE_BLOCK, offset);
|
|
||||||
if (!_M3SD_getResponse_R1 (responseBuffer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
REG_M3SD_DIR = 0x4;
|
|
||||||
REG_M3SD_STS = 0x0;
|
|
||||||
|
|
||||||
// Send the data
|
|
||||||
if (! _M3SD_writeData( data, crc)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numSectors > 0) {
|
|
||||||
offset += BYTES_PER_READ;
|
|
||||||
data += BYTES_PER_READ;
|
|
||||||
// Calculate the next CRC while waiting for the card to finish writing
|
|
||||||
_SD_CRC16 ( data, BYTES_PER_READ, crc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the card to be ready for the next transfer
|
|
||||||
i = WRITE_TIMEOUT;
|
|
||||||
responseBuffer[3] = 0;
|
|
||||||
do {
|
|
||||||
_M3SD_sendCommand (SEND_STATUS, _M3SD_relativeCardAddress);
|
|
||||||
_M3SD_getResponse_R1 (responseBuffer);
|
|
||||||
i--;
|
|
||||||
if (i <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} while (((responseBuffer[3] & 0x1f) != ((SD_STATE_TRAN << 1) | READY_FOR_DATA)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _M3SD_clearStatus (void) {
|
|
||||||
return _M3SD_initCard ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _M3SD_shutdown (void) {
|
|
||||||
_M3_changeMode (M3_MODE_ROM);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const IO_INTERFACE _io_m3sd = {
|
|
||||||
DEVICE_TYPE_M3SD,
|
|
||||||
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
|
|
||||||
(FN_MEDIUM_STARTUP)&_M3SD_startUp,
|
|
||||||
(FN_MEDIUM_ISINSERTED)&_M3SD_isInserted,
|
|
||||||
(FN_MEDIUM_READSECTORS)&_M3SD_readSectors,
|
|
||||||
(FN_MEDIUM_WRITESECTORS)&_M3SD_writeSectors,
|
|
||||||
(FN_MEDIUM_CLEARSTATUS)&_M3SD_clearStatus,
|
|
||||||
(FN_MEDIUM_SHUTDOWN)&_M3SD_shutdown
|
|
||||||
} ;
|
|
||||||
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
io_m3sd.h
|
|
||||||
|
|
||||||
Hardware Routines for reading a Secure Digital card
|
|
||||||
using the M3 SD
|
|
||||||
|
|
||||||
Some code based on M3 SD drivers supplied by M3Adapter.
|
|
||||||
Some code written by SaTa may have been unknowingly used.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_M3SD_H
|
|
||||||
#define IO_M3SD_H
|
|
||||||
|
|
||||||
// 'M3SD'
|
|
||||||
#define DEVICE_TYPE_M3SD 0x4453334D
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_m3sd ;
|
|
||||||
|
|
||||||
#endif // define IO_M3SD_H
|
|
@ -1,100 +0,0 @@
|
|||||||
/*
|
|
||||||
io_mpcf.c based on
|
|
||||||
|
|
||||||
compact_flash.c
|
|
||||||
By chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Hardware Routines for reading a compact flash card
|
|
||||||
using the GBA Movie Player
|
|
||||||
|
|
||||||
CF routines modified with help from Darkfader
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "io_mpcf.h"
|
|
||||||
#include "io_cf_common.h"
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// DMA
|
|
||||||
#ifdef _IO_USE_DMA
|
|
||||||
#ifndef NDS
|
|
||||||
#include "gba_dma.h"
|
|
||||||
#else
|
|
||||||
#include <nds/dma.h>
|
|
||||||
#ifdef ARM9
|
|
||||||
#include <nds/arm9/cache.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// GBAMP CF Addresses
|
|
||||||
#define REG_MPCF_STS ((vu16*)0x098C0000) // Status of the CF Card / Device control
|
|
||||||
#define REG_MPCF_CMD ((vu16*)0x090E0000) // Commands sent to control chip and status return
|
|
||||||
#define REG_MPCF_ERR ((vu16*)0x09020000) // Errors / Features
|
|
||||||
|
|
||||||
#define REG_MPCF_SEC ((vu16*)0x09040000) // Number of sector to transfer
|
|
||||||
#define REG_MPCF_LBA1 ((vu16*)0x09060000) // 1st byte of sector address
|
|
||||||
#define REG_MPCF_LBA2 ((vu16*)0x09080000) // 2nd byte of sector address
|
|
||||||
#define REG_MPCF_LBA3 ((vu16*)0x090A0000) // 3rd byte of sector address
|
|
||||||
#define REG_MPCF_LBA4 ((vu16*)0x090C0000) // last nibble of sector address | 0xE0
|
|
||||||
|
|
||||||
#define REG_MPCF_DATA ((vu16*)0x09000000) // Pointer to buffer of CF data transered from card
|
|
||||||
|
|
||||||
static const CF_REGISTERS _MPCF_Registers = {
|
|
||||||
REG_MPCF_DATA,
|
|
||||||
REG_MPCF_STS,
|
|
||||||
REG_MPCF_CMD,
|
|
||||||
REG_MPCF_ERR,
|
|
||||||
REG_MPCF_SEC,
|
|
||||||
REG_MPCF_LBA1,
|
|
||||||
REG_MPCF_LBA2,
|
|
||||||
REG_MPCF_LBA3,
|
|
||||||
REG_MPCF_LBA4
|
|
||||||
};
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_MPCF_startup
|
|
||||||
initializes the CF interface, returns true if successful,
|
|
||||||
otherwise returns false
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _MPCF_startup(void) {
|
|
||||||
return _CF_startup(&_MPCF_Registers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
the actual interface structure
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
const IO_INTERFACE _io_mpcf = {
|
|
||||||
DEVICE_TYPE_MPCF,
|
|
||||||
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
|
|
||||||
(FN_MEDIUM_STARTUP)&_MPCF_startup,
|
|
||||||
(FN_MEDIUM_ISINSERTED)&_CF_isInserted,
|
|
||||||
(FN_MEDIUM_READSECTORS)&_CF_readSectors,
|
|
||||||
(FN_MEDIUM_WRITESECTORS)&_CF_writeSectors,
|
|
||||||
(FN_MEDIUM_CLEARSTATUS)&_CF_clearStatus,
|
|
||||||
(FN_MEDIUM_SHUTDOWN)&_CF_shutdown
|
|
||||||
} ;
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
io_mpcf.h
|
|
||||||
|
|
||||||
Hardware Routines for reading a compact flash card
|
|
||||||
using the GBA Movie Player
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_MPCF_H
|
|
||||||
#define IO_MPCF_H
|
|
||||||
|
|
||||||
// 'MPCF'
|
|
||||||
#define DEVICE_TYPE_MPCF 0x4643504D
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_mpcf ;
|
|
||||||
|
|
||||||
#endif // define IO_MPCF_H
|
|
@ -1,595 +0,0 @@
|
|||||||
/*
|
|
||||||
io_njsd.c
|
|
||||||
|
|
||||||
Hardware Routines for reading an SD card using
|
|
||||||
a NinjaDS SD adapter.
|
|
||||||
|
|
||||||
Original code supplied by NinjaMod
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-08-05 - Chishm
|
|
||||||
* First release
|
|
||||||
|
|
||||||
2006-08-06 - Chishm
|
|
||||||
* Removed unneeded _NJSD_writeRAM function
|
|
||||||
* Removed casts for calls to cardWriteCommand
|
|
||||||
|
|
||||||
2006-08-07 - Chishm
|
|
||||||
* Moved the SD initialization to a common function
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "io_njsd.h"
|
|
||||||
|
|
||||||
#ifdef NDS
|
|
||||||
|
|
||||||
#include <nds.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "io_sd_common.h"
|
|
||||||
|
|
||||||
#define _NJSD_SYNC
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Card communication speeds
|
|
||||||
#define SD_CLK_167KHz 00
|
|
||||||
#define SD_CLK_250KHz 01
|
|
||||||
#define SD_CLK_5MHz 02
|
|
||||||
#define SD_CLK_25MHz 03
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Response types
|
|
||||||
#define SD_RSP_48 0
|
|
||||||
#define SD_RSP_136 1
|
|
||||||
#define SD_RSP_DATA 2
|
|
||||||
#define SD_RSP_STREAM 3
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Send / receive timeouts, to stop infinite wait loops
|
|
||||||
#define IRQ_TIMEOUT 1000000
|
|
||||||
#define RESET_TIMEOUT 10000
|
|
||||||
#define COMMAND_TIMEOUT 100000
|
|
||||||
#define WRITE_TIMEOUT 3000 // Time to wait for the card to finish writing
|
|
||||||
|
|
||||||
|
|
||||||
static const u8 _NJSD_read_cmd[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40};
|
|
||||||
static const u8 _NJSD_read_end_cmd[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x41};
|
|
||||||
|
|
||||||
static int _NJSD_speed = SD_CLK_5MHz; // Default speed;
|
|
||||||
|
|
||||||
static u32 _NJSD_cardFlags;
|
|
||||||
|
|
||||||
static u32 _NJSD_relativeCardAddress = 0;
|
|
||||||
|
|
||||||
static inline bool _NJSD_waitIRQ(void) {
|
|
||||||
int i = IRQ_TIMEOUT;
|
|
||||||
while (!(REG_IF & 0x100000) && --i);
|
|
||||||
REG_IF = 0x100000;
|
|
||||||
if (i <= 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _NJSD_writeCardCommand
|
|
||||||
(u8 cmd0, u8 cmd1, u8 cmd2, u8 cmd3, u8 cmd4, u8 cmd5, u8 cmd6, u8 cmd7)
|
|
||||||
{
|
|
||||||
CARD_COMMAND[0] = cmd0;
|
|
||||||
CARD_COMMAND[1] = cmd1;
|
|
||||||
CARD_COMMAND[2] = cmd2;
|
|
||||||
CARD_COMMAND[3] = cmd3;
|
|
||||||
CARD_COMMAND[4] = cmd4;
|
|
||||||
CARD_COMMAND[5] = cmd5;
|
|
||||||
CARD_COMMAND[6] = cmd6;
|
|
||||||
CARD_COMMAND[7] = cmd7;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _NJSD_reset (void) {
|
|
||||||
int i;
|
|
||||||
CARD_CR1H = CARD_CR1_ENABLE;
|
|
||||||
_NJSD_writeCardCommand (0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
|
||||||
CARD_CR2 = 0xA0406000;
|
|
||||||
i = RESET_TIMEOUT;
|
|
||||||
while ((CARD_CR2 & CARD_BUSY) && --i);
|
|
||||||
if (i <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _NJSD_init (u32 flags) {
|
|
||||||
_NJSD_cardFlags = flags;
|
|
||||||
|
|
||||||
REG_IF = 0x100000; // Clear cart IRQ.
|
|
||||||
|
|
||||||
irqDisable (IRQ_CARD_LINE);
|
|
||||||
|
|
||||||
if (! _NJSD_reset() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _NJSD_sendCMDR (int speed, u8 *rsp_buf, int type, u8 cmd, u32 param) {
|
|
||||||
int i;
|
|
||||||
u32 data;
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
u32 old_REG_IME;
|
|
||||||
old_REG_IME = REG_IME;
|
|
||||||
REG_IME = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
REG_IF = 0x100000;
|
|
||||||
|
|
||||||
CARD_CR1H = CARD_CR1_ENABLE;
|
|
||||||
|
|
||||||
if ((type & 3) < 2) {
|
|
||||||
CARD_COMMAND[0] = 0xF0 | (speed << 2) | 1 | (type << 1);
|
|
||||||
} else if ((type & 3) == 2) {
|
|
||||||
CARD_COMMAND[0] = 0xE0 | (speed << 2) | 0 | (1 << 1);
|
|
||||||
} else {
|
|
||||||
CARD_COMMAND[0] = 0xF0 | (speed << 2) | 0 | (1 << 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
CARD_COMMAND[1] = (type & 0x40) | ((( type >> 2) & 7) << 3);
|
|
||||||
CARD_COMMAND[2] = 0x40 | cmd;
|
|
||||||
CARD_COMMAND[3] = (param>>24) & 0xFF;
|
|
||||||
CARD_COMMAND[4] = (param>>16) & 0xFF;
|
|
||||||
CARD_COMMAND[5] = (param>>8) & 0xFF;
|
|
||||||
CARD_COMMAND[6] = (param>>0) & 0xFF;
|
|
||||||
CARD_COMMAND[7] = 0; // offset = 0
|
|
||||||
|
|
||||||
if ((type & 3) < 2) {
|
|
||||||
CARD_CR2 = _NJSD_cardFlags | 0x01000000;
|
|
||||||
|
|
||||||
// wait for ninja DS to be done!
|
|
||||||
if (!_NJSD_waitIRQ ()) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
do {
|
|
||||||
// Read data if available
|
|
||||||
if (CARD_CR2 & CARD_DATA_READY) {
|
|
||||||
data=CARD_DATA_RD;
|
|
||||||
if (rsp_buf != NULL) {
|
|
||||||
if (i == 4) {
|
|
||||||
rsp_buf[0] = (data>>0)&0xFF;
|
|
||||||
rsp_buf[1] = (data>>8)&0xFF;
|
|
||||||
rsp_buf[2] = (data>>16)&0xFF;
|
|
||||||
rsp_buf[3] = (data>>24)&0xFF;
|
|
||||||
} else if (i == 5) {
|
|
||||||
rsp_buf[4] = (data>>0)&0xFF;
|
|
||||||
rsp_buf[5] = (data>>8)&0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} while (CARD_CR2 & CARD_BUSY);
|
|
||||||
} else {
|
|
||||||
CARD_CR2 = _NJSD_cardFlags;
|
|
||||||
while (CARD_CR2 & CARD_BUSY);
|
|
||||||
|
|
||||||
// wait for ninja DS to be done!
|
|
||||||
if (!_NJSD_waitIRQ ()) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _NJSD_writeSector (u8 *buffer, u8 *crc_buf, u32 offset) {
|
|
||||||
int i;
|
|
||||||
u8 responseBuffer[6];
|
|
||||||
u32 data;
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
u32 old_REG_IME;
|
|
||||||
old_REG_IME = REG_IME;
|
|
||||||
REG_IME = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CARD_CR1H = CARD_CR1_ENABLE;
|
|
||||||
_NJSD_writeCardCommand (0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
|
||||||
CARD_CR2 = 0xA0406000;
|
|
||||||
i = COMMAND_TIMEOUT;
|
|
||||||
while ((CARD_CR2 & CARD_BUSY) && --i);
|
|
||||||
if (i <= 0) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 65; i++)
|
|
||||||
{
|
|
||||||
CARD_CR1H = CARD_CR1_ENABLE; // | CARD_CR1_IRQ;
|
|
||||||
if (i < 64)
|
|
||||||
{
|
|
||||||
_NJSD_writeCardCommand (buffer[i*8+0], buffer[i*8+1], buffer[i*8+2], buffer[i*8+3],
|
|
||||||
buffer[i*8+4], buffer[i*8+5], buffer[i*8+6], buffer[i*8+7]);
|
|
||||||
} else {
|
|
||||||
_NJSD_writeCardCommand (crc_buf[0], crc_buf[1], crc_buf[2], crc_buf[3],
|
|
||||||
crc_buf[4], crc_buf[5], crc_buf[6], crc_buf[7]);
|
|
||||||
}
|
|
||||||
CARD_CR2 = 0xA7406000;
|
|
||||||
|
|
||||||
do {
|
|
||||||
// Read data if available
|
|
||||||
if (CARD_CR2 & CARD_DATA_READY) {
|
|
||||||
data=CARD_DATA_RD;
|
|
||||||
}
|
|
||||||
} while (CARD_CR2 & CARD_BUSY);
|
|
||||||
}
|
|
||||||
|
|
||||||
CARD_CR1H = CARD_CR1_ENABLE;
|
|
||||||
_NJSD_writeCardCommand (0xF0 | (1 << 2) | 1, 0x80, 0x40 | WRITE_BLOCK, (u8)(offset>>24),
|
|
||||||
(u8)(offset>>16), (u8)(offset>>8), (u8)(offset>>0), 0x00);
|
|
||||||
CARD_CR2 = 0xA7406000;
|
|
||||||
|
|
||||||
// wait for ninja DS to be done!
|
|
||||||
if (!_NJSD_waitIRQ ()) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
do {
|
|
||||||
// Read data if available
|
|
||||||
if (CARD_CR2 & CARD_DATA_READY) {
|
|
||||||
data = CARD_DATA_RD;
|
|
||||||
if (i == 2) {
|
|
||||||
responseBuffer[0] = (u8)(data>>0);
|
|
||||||
responseBuffer[1] = (u8)(data>>8);
|
|
||||||
responseBuffer[2] = (u8)(data>>16);
|
|
||||||
responseBuffer[3] = (u8)(data>>24);
|
|
||||||
} else if (i == 3) {
|
|
||||||
responseBuffer[4] = (u8)(data>>0);
|
|
||||||
responseBuffer[5] = (u8)(data>>8);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} while (CARD_CR2 & CARD_BUSY);
|
|
||||||
|
|
||||||
i = WRITE_TIMEOUT;
|
|
||||||
responseBuffer[3] = 0;
|
|
||||||
do {
|
|
||||||
_NJSD_sendCMDR (SD_CLK_167KHz, responseBuffer, SD_RSP_48, SEND_STATUS, _NJSD_relativeCardAddress);
|
|
||||||
i--;
|
|
||||||
if (i <= 0) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} while (((responseBuffer[3] & 0x1f) != ((SD_STATE_TRAN << 1) | READY_FOR_DATA)));
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _NJSD_sendCLK (int speed, int count) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
u32 old_REG_IME;
|
|
||||||
old_REG_IME = REG_IME;
|
|
||||||
REG_IME = 0;
|
|
||||||
|
|
||||||
REG_IF = 0x100000;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//CARD_CR1H = CARD_CR1_ENABLE; // | CARD_CR1_IRQ;
|
|
||||||
_NJSD_writeCardCommand (0xE0 | ((speed & 3) << 2), 0, (count - 1), 0, 0, 0, 0, 0);
|
|
||||||
|
|
||||||
CARD_CR2 = _NJSD_cardFlags;
|
|
||||||
i = COMMAND_TIMEOUT;
|
|
||||||
while ((CARD_CR2 & CARD_BUSY) && --i);
|
|
||||||
if (i <= 0) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for ninja DS to be done!
|
|
||||||
if (!_NJSD_waitIRQ ()) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _NJSD_sendCMDN (int speed, u8 cmd, u32 param) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
u32 old_REG_IME;
|
|
||||||
old_REG_IME = REG_IME;
|
|
||||||
REG_IME = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
REG_IF = 0x100000;
|
|
||||||
|
|
||||||
CARD_CR1H = CARD_CR1_ENABLE; // | CARD_CR1_IRQ;
|
|
||||||
_NJSD_writeCardCommand (0xF0 | ((speed & 3) << 2), 0x00, 0x40 | cmd, (param>>24) & 0xFF,
|
|
||||||
(param>>16) & 0xFF, (param>>8) & 0xFF, (param>>0) & 0xFF, 0x00);
|
|
||||||
|
|
||||||
CARD_CR2 = _NJSD_cardFlags;
|
|
||||||
i = COMMAND_TIMEOUT;
|
|
||||||
while ((CARD_CR2 & CARD_BUSY) && --i);
|
|
||||||
if (i <= 0) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for ninja DS to be done!
|
|
||||||
if (!_NJSD_waitIRQ ()) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NJSD_cmd_6byte_response (u8* responseBuffer, u8 command, u32 data) {
|
|
||||||
return _NJSD_sendCMDR (SD_CLK_167KHz, responseBuffer, SD_RSP_48, command, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NJSD_cmd_17byte_response (u8* responseBuffer, u8 command, u32 data) {
|
|
||||||
return _NJSD_sendCMDR (SD_CLK_167KHz, responseBuffer, SD_RSP_136, command, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _NJSD_cardInit (void) {
|
|
||||||
// If the commands succeed the first time, assume they'll always succeed
|
|
||||||
if (! _NJSD_sendCLK (SD_CLK_167KHz, 256) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (! _NJSD_sendCMDN (SD_CLK_167KHz, GO_IDLE_STATE, 0) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_NJSD_sendCLK (SD_CLK_167KHz, 8);
|
|
||||||
|
|
||||||
_NJSD_sendCLK (SD_CLK_167KHz, 256);
|
|
||||||
_NJSD_sendCMDN (SD_CLK_167KHz, GO_IDLE_STATE, 0);
|
|
||||||
_NJSD_sendCLK (SD_CLK_167KHz, 8);
|
|
||||||
|
|
||||||
return _SD_InitCard (_NJSD_cmd_6byte_response,
|
|
||||||
_NJSD_cmd_17byte_response,
|
|
||||||
true,
|
|
||||||
&_NJSD_relativeCardAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool _NJSD_isInserted(void) {
|
|
||||||
u8 responseBuffer [8];
|
|
||||||
_NJSD_sendCMDR (SD_CLK_167KHz, responseBuffer, SD_RSP_48, SEND_STATUS, 0);
|
|
||||||
|
|
||||||
// Make sure the card responded correctly
|
|
||||||
if (responseBuffer[0] != SEND_STATUS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NJSD_clearStatus (void) {
|
|
||||||
return _NJSD_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NJSD_shutdown(void) {
|
|
||||||
return _NJSD_clearStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NJSD_startup(void) {
|
|
||||||
if (! _NJSD_init(0xA0406000) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (! _NJSD_cardInit() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool _NJSD_writeSectors (u32 sector, u32 numSectors, const void* buffer) {
|
|
||||||
u8 crc[8];
|
|
||||||
u32 offset = sector * BYTES_PER_READ;
|
|
||||||
u8* data = (u8*) buffer;
|
|
||||||
|
|
||||||
while (numSectors--) {
|
|
||||||
_SD_CRC16 ( data, BYTES_PER_READ, crc);
|
|
||||||
|
|
||||||
if (! _NJSD_writeSector (data, crc, offset)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
offset += BYTES_PER_READ;
|
|
||||||
data += BYTES_PER_READ;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _IO_ALLOW_UNALIGNED
|
|
||||||
bool _NJSD_readSectors (u32 sector, u32 numSectors, void* buffer) {
|
|
||||||
u32 tmp[BYTES_PER_READ>>2];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
u32 old_REG_IME;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u8* tbuf = (u8*)buffer;
|
|
||||||
|
|
||||||
if (numSectors == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
old_REG_IME = REG_IME;
|
|
||||||
REG_IME = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (numSectors > 1) {
|
|
||||||
_NJSD_sendCMDR (_NJSD_speed, NULL, SD_RSP_DATA, READ_MULTIPLE_BLOCK, sector * BYTES_PER_READ);
|
|
||||||
for (i = 0; i < numSectors - 2; i++) {
|
|
||||||
if (((int)buffer & 0x03) != 0){
|
|
||||||
cardPolledTransfer (0xA1406000, tmp, BYTES_PER_READ, _NJSD_read_cmd);
|
|
||||||
memcpy (tbuf + i * BYTES_PER_READ, tmp, BYTES_PER_READ);
|
|
||||||
} else {
|
|
||||||
cardPolledTransfer (0xA1406000, (u32*)(tbuf + i * BYTES_PER_READ), BYTES_PER_READ, _NJSD_read_cmd);
|
|
||||||
}
|
|
||||||
if (!_NJSD_waitIRQ ()) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (((int)buffer & 0x03) != 0){
|
|
||||||
cardPolledTransfer (0xA1406000, tmp, BYTES_PER_READ, _NJSD_read_end_cmd);
|
|
||||||
memcpy (tbuf + (numSectors - 2) * BYTES_PER_READ, tmp, BYTES_PER_READ);
|
|
||||||
} else {
|
|
||||||
cardPolledTransfer (0xA1406000, (u32*)(tbuf + (numSectors - 2) * BYTES_PER_READ), BYTES_PER_READ, _NJSD_read_end_cmd);
|
|
||||||
}
|
|
||||||
if (!_NJSD_waitIRQ ()) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (((int)buffer & 0x03) != 0){
|
|
||||||
cardPolledTransfer (0xA1406000, tmp, BYTES_PER_READ, _NJSD_read_cmd);
|
|
||||||
memcpy (tbuf + (numSectors - 1) * BYTES_PER_READ, tmp, BYTES_PER_READ);
|
|
||||||
} else {
|
|
||||||
cardPolledTransfer (0xA1406000, (u32*)(tbuf + (numSectors - 1) * BYTES_PER_READ), BYTES_PER_READ, _NJSD_read_cmd);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_NJSD_sendCMDR (_NJSD_speed, NULL, SD_RSP_STREAM, READ_SINGLE_BLOCK, sector * BYTES_PER_READ);
|
|
||||||
if (((int)buffer & 0x03) != 0){
|
|
||||||
cardPolledTransfer (0xA1406000, tmp, BYTES_PER_READ, _NJSD_read_cmd);
|
|
||||||
memcpy (tbuf, tmp, BYTES_PER_READ);
|
|
||||||
} else {
|
|
||||||
cardPolledTransfer (0xA1406000, (u32*)tbuf, BYTES_PER_READ, _NJSD_read_cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#else // not defined _IO_ALLOW_UNALIGNED
|
|
||||||
bool _NJSD_readSectors (u32 sector, u32 numSectors, void* buffer) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
u32 old_REG_IME;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u8* tbuf = (u8*)buffer;
|
|
||||||
|
|
||||||
if (numSectors == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
old_REG_IME = REG_IME;
|
|
||||||
REG_IME = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (numSectors > 1) {
|
|
||||||
_NJSD_sendCMDR (_NJSD_speed, NULL, SD_RSP_DATA, READ_MULTIPLE_BLOCK, sector * BYTES_PER_READ);
|
|
||||||
for (i = 0; i < numSectors - 2; i++) {
|
|
||||||
cardPolledTransfer (0xA1406000, (u32*)(tbuf + i * BYTES_PER_READ), BYTES_PER_READ, _NJSD_read_cmd);
|
|
||||||
if (!_NJSD_waitIRQ ()) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cardPolledTransfer (0xA1406000, (u32*)(tbuf + (numSectors - 2) * BYTES_PER_READ), BYTES_PER_READ, _NJSD_read_end_cmd);
|
|
||||||
if (!_NJSD_waitIRQ ()) {
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
cardPolledTransfer (0xA1406000, (u32*)(tbuf + (numSectors - 1) * BYTES_PER_READ), BYTES_PER_READ, _NJSD_read_cmd);
|
|
||||||
} else {
|
|
||||||
_NJSD_sendCMDR (_NJSD_speed, NULL, SD_RSP_STREAM, READ_SINGLE_BLOCK, sector * BYTES_PER_READ);
|
|
||||||
cardPolledTransfer (0xA1406000, (u32*)tbuf, BYTES_PER_READ, _NJSD_read_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _NJSD_SYNC
|
|
||||||
REG_IME = old_REG_IME;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif // _IO_ALLOW_UNALIGNED
|
|
||||||
|
|
||||||
const IO_INTERFACE _io_njsd = {
|
|
||||||
DEVICE_TYPE_NJSD,
|
|
||||||
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_NDS,
|
|
||||||
(FN_MEDIUM_STARTUP)&_NJSD_startup,
|
|
||||||
(FN_MEDIUM_ISINSERTED)&_NJSD_isInserted,
|
|
||||||
(FN_MEDIUM_READSECTORS)&_NJSD_readSectors,
|
|
||||||
(FN_MEDIUM_WRITESECTORS)&_NJSD_writeSectors,
|
|
||||||
(FN_MEDIUM_CLEARSTATUS)&_NJSD_clearStatus,
|
|
||||||
(FN_MEDIUM_SHUTDOWN)&_NJSD_shutdown
|
|
||||||
} ;
|
|
||||||
|
|
||||||
#endif // defined NDS
|
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
io_njsd.h
|
|
||||||
|
|
||||||
Hardware Routines for reading an SD card using
|
|
||||||
a NinjaDS SD adapter.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-08-02 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_NJSD_H
|
|
||||||
#define IO_NJSD_H
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
#ifdef NDS
|
|
||||||
|
|
||||||
// 'NJSD'
|
|
||||||
#define DEVICE_TYPE_NJSD 0x44534A4E
|
|
||||||
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_njsd;
|
|
||||||
|
|
||||||
#endif // defined NDS
|
|
||||||
|
|
||||||
#endif // define IO_NJSD_H
|
|
@ -1,348 +0,0 @@
|
|||||||
/*
|
|
||||||
io_nmmc.c
|
|
||||||
|
|
||||||
Hardware Routines for reading an SD or MMC card using
|
|
||||||
a Neoflash MK2 or MK3.
|
|
||||||
|
|
||||||
Written by www.neoflash.com
|
|
||||||
|
|
||||||
Submit bug reports for this device to the NeoFlash forums
|
|
||||||
|
|
||||||
See license.txt for license details.
|
|
||||||
|
|
||||||
2006-02-09 - www.neoflash.com:
|
|
||||||
* First stable release
|
|
||||||
|
|
||||||
2006-02-13 - Chishm
|
|
||||||
* Added ReadMK2Config function
|
|
||||||
* Added read config test to init function so no unnecessary card commands are sent
|
|
||||||
* Changed data read and write functions to use multiple block commands
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "io_nmmc.h"
|
|
||||||
|
|
||||||
#ifdef NDS
|
|
||||||
|
|
||||||
#include <nds/card.h>
|
|
||||||
|
|
||||||
int _NMMC_spi_freq = 3;
|
|
||||||
|
|
||||||
#define MK2_CONFIG_ZIP_RAM_CLOSE (1 << 5)
|
|
||||||
#define MK2_CONFIG_GAME_FLASH_CLOSE ((1 << 4) | (1 << 0))
|
|
||||||
//#define MK2_CONFIG_ZIP_RAM_CLOSE ((1 << 5) | (1 << 1))
|
|
||||||
//#define MK2_CONFIG_GAME_FLASH_CLOSE (1 << 4)
|
|
||||||
|
|
||||||
#define MMC_READ_MULTIPLE_BLOCK 18
|
|
||||||
#define MMC_READ_BLOCK 17
|
|
||||||
#define MMC_WRITE_MULTIPLE_BLOCK 25
|
|
||||||
#define MMC_WRITE_BLOCK 24
|
|
||||||
#define MMC_STOP_TRANSMISSION 12
|
|
||||||
#define MMC_SET_BLOCKLEN 16
|
|
||||||
#define MMC_SET_BLOCK_COUNT 23
|
|
||||||
#define MMC_SEND_CSD 9
|
|
||||||
|
|
||||||
// SPI functions
|
|
||||||
|
|
||||||
static inline void _Neo_OpenSPI( u8 frequency )
|
|
||||||
{
|
|
||||||
CARD_CR1 = 0x0000A040 | frequency;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u8 _Neo_SPI( u8 dataByte )
|
|
||||||
{
|
|
||||||
CARD_EEPDATA = dataByte;
|
|
||||||
while (CARD_CR1 & 0x80); // card busy
|
|
||||||
return CARD_EEPDATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _Neo_CloseSPI ( void )
|
|
||||||
{
|
|
||||||
CARD_CR1 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _Neo_MK2GameMode() {
|
|
||||||
_Neo_OpenSPI(_NMMC_spi_freq); // Enable DS Card's SPI port
|
|
||||||
_Neo_SPI(0xF1); // Switch to game mode
|
|
||||||
_Neo_CloseSPI(); // Disable DS Card's SPI port
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _Neo_EnableEEPROM( bool enable ) {
|
|
||||||
_Neo_OpenSPI(_NMMC_spi_freq);
|
|
||||||
if(enable) _Neo_SPI(0x06);
|
|
||||||
else _Neo_SPI(0x0E);
|
|
||||||
_Neo_CloseSPI();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _Neo_WriteMK2Config(u8 config) {
|
|
||||||
_Neo_EnableEEPROM(true);
|
|
||||||
_Neo_OpenSPI(_NMMC_spi_freq);
|
|
||||||
_Neo_SPI(0xFA); // Send mem conf write command
|
|
||||||
_Neo_SPI(0x01); // Send high byte (0x01)
|
|
||||||
_Neo_SPI(config); // Send low byte
|
|
||||||
_Neo_CloseSPI();
|
|
||||||
_Neo_EnableEEPROM(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static u8 _Neo_ReadMK2Config(void)
|
|
||||||
{
|
|
||||||
u8 config;
|
|
||||||
_Neo_EnableEEPROM(true);
|
|
||||||
_Neo_OpenSPI(_NMMC_spi_freq);
|
|
||||||
_Neo_SPI(0xf8); // Send mem conf read command
|
|
||||||
_Neo_SPI(0x01); // Send high byte
|
|
||||||
config = _Neo_SPI(0x00); // Get low byte
|
|
||||||
_Neo_CloseSPI();
|
|
||||||
_Neo_EnableEEPROM(false);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Low level functions
|
|
||||||
|
|
||||||
u8 selectMMC_command [8] = {0xFF, 0x00, 0x6A, 0xDF, 0x37, 0x59, 0x33, 0xA3};
|
|
||||||
|
|
||||||
static void _Neo_SelectMMC (u8 dataByte)
|
|
||||||
{
|
|
||||||
selectMMC_command[1] = dataByte; // Set enable / disable byte
|
|
||||||
cardWriteCommand (selectMMC_command); // Send "5. Use the EEPROM CS to access the MK2 MMC/SD card"
|
|
||||||
CARD_CR2 = CARD_ACTIVATE | CARD_nRESET;
|
|
||||||
while (CARD_CR2 & CARD_BUSY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _Neo_EnableMMC( bool enable )
|
|
||||||
{
|
|
||||||
if ( enable == false) {
|
|
||||||
_Neo_CloseSPI ();
|
|
||||||
_Neo_SelectMMC (0);
|
|
||||||
_Neo_SelectMMC (0);
|
|
||||||
} else {
|
|
||||||
_Neo_SelectMMC (1);
|
|
||||||
_Neo_SelectMMC (1);
|
|
||||||
_Neo_OpenSPI (_NMMC_spi_freq);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _Neo_SendMMCCommand( u8 command, u32 argument )
|
|
||||||
{
|
|
||||||
_Neo_SPI (0xFF);
|
|
||||||
_Neo_SPI (command | 0x40);
|
|
||||||
_Neo_SPI ((argument >> 24) & 0xff);
|
|
||||||
_Neo_SPI ((argument >> 16) & 0xff);
|
|
||||||
_Neo_SPI ((argument >> 8) & 0xff) ;
|
|
||||||
_Neo_SPI (argument & 0xff);
|
|
||||||
_Neo_SPI (0x95);
|
|
||||||
_Neo_SPI (0xFF);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _Neo_CheckMMCResponse( u8 response, u8 mask ) {
|
|
||||||
u32 i;
|
|
||||||
for(i=0;i<256;i++) {
|
|
||||||
if( ( _Neo_SPI( 0xFF ) & mask ) == response )
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neo MMC functions
|
|
||||||
|
|
||||||
static bool _Neo_InitMMC() {
|
|
||||||
_Neo_MK2GameMode();
|
|
||||||
_Neo_WriteMK2Config( MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE);
|
|
||||||
|
|
||||||
// Make sure the configuration was accepted
|
|
||||||
if (_Neo_ReadMK2Config() != (MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE)) {
|
|
||||||
return false; // If not, then it wasn't initialised properly
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neo MMC driver functions
|
|
||||||
|
|
||||||
bool _NMMC_isInserted(void) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
_Neo_EnableMMC( true ); // Open SPI port to MMC card
|
|
||||||
_Neo_SendMMCCommand(MMC_SEND_CSD, 0);
|
|
||||||
if( _Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if( _Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// consume data from card, and send clocks.
|
|
||||||
for (i = 0; i < 28; i++) {
|
|
||||||
_Neo_SPI(0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NMMC_clearStatus (void) {
|
|
||||||
u32 i;
|
|
||||||
|
|
||||||
_Neo_EnableMMC( true ); // Open SPI port to MMC card
|
|
||||||
for (i = 0; i < 10; i++) {
|
|
||||||
_Neo_SPI(0xFF); // Send 10 0xFF bytes to MMC card
|
|
||||||
}
|
|
||||||
_Neo_SendMMCCommand(0, 0); // Send GO_IDLE_STATE command
|
|
||||||
if( _Neo_CheckMMCResponse( 0x01, 0xFF ) == false ) { // Check that it replied with 0x01 (not idle, no other error)
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for(i=0;i<256;i++) {
|
|
||||||
_Neo_SendMMCCommand(1, 0); // Poll with SEND_OP_COND
|
|
||||||
if( _Neo_CheckMMCResponse( 0x00, 0x01 ) == true ) { // Check for idle state
|
|
||||||
_Neo_EnableMMC( false ); // Close SPI port to MMC card
|
|
||||||
return true; // Card is now idle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NMMC_shutdown(void) {
|
|
||||||
return _NMMC_clearStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NMMC_startUp(void) {
|
|
||||||
int i;
|
|
||||||
int transSpeed;
|
|
||||||
if (_Neo_InitMMC() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (_NMMC_clearStatus() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_Neo_EnableMMC( true ); // Open SPI port to MMC card
|
|
||||||
|
|
||||||
// Set block length
|
|
||||||
_Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTES_PER_READ );
|
|
||||||
if( _Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we can use a higher SPI frequency
|
|
||||||
_Neo_SendMMCCommand(MMC_SEND_CSD, 0);
|
|
||||||
if( _Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if( _Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (i = 0; i < 3; i++) {
|
|
||||||
_Neo_SPI(0xFF);
|
|
||||||
}
|
|
||||||
transSpeed = _Neo_SPI (0xFF);
|
|
||||||
for (i = 0; i < 24; i++) {
|
|
||||||
_Neo_SPI(0xFF);
|
|
||||||
}
|
|
||||||
if ((transSpeed & 0xf0) >= 0x30) {
|
|
||||||
_NMMC_spi_freq = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool _NMMC_writeSectors (u32 sector, u32 totalSecs, const void* buffer)
|
|
||||||
{
|
|
||||||
u32 i;
|
|
||||||
u8 *p=(u8*)buffer;
|
|
||||||
|
|
||||||
sector *= BYTES_PER_READ;
|
|
||||||
|
|
||||||
_Neo_EnableMMC( true ); // Open SPI port to MMC card
|
|
||||||
_Neo_SendMMCCommand( 25, sector );
|
|
||||||
if( _Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (totalSecs--) {
|
|
||||||
_Neo_SPI( 0xFC ); // Send Start Block token
|
|
||||||
for( i = 0; i < BYTES_PER_READ; i++ ) // Send a block of data
|
|
||||||
_Neo_SPI( *p++ );
|
|
||||||
_Neo_SPI( 0xFF ); // Send fake CRC16
|
|
||||||
_Neo_SPI( 0xFF ); // Send fake CRC16
|
|
||||||
|
|
||||||
if( ( _Neo_SPI( 0xFF ) & 0x0F ) != 0x05 ) { // Make sure the block was accepted
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
while( _Neo_SPI( 0xFF ) == 0x00 ); // Wait for the block to be written
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop transmission block
|
|
||||||
_Neo_SPI( 0xFD ); // Send Stop Transmission Block token
|
|
||||||
for( i = 0; i < BYTES_PER_READ; i++ ) // Send a block of fake data
|
|
||||||
_Neo_SPI( 0xFF );
|
|
||||||
_Neo_SPI( 0xFF ); // Send fake CRC16
|
|
||||||
_Neo_SPI( 0xFF ); // Send fake CRC16
|
|
||||||
|
|
||||||
_Neo_SPI (0xFF); // Send 8 clocks
|
|
||||||
while( _Neo_SPI( 0xFF ) == 0x00 ); // Wait for the busy signal to clear
|
|
||||||
|
|
||||||
|
|
||||||
for ( i = 0; i < 0x10; i++) {
|
|
||||||
_Neo_SPI (0xFF); // Send clocks for the MMC card to finish what it's doing
|
|
||||||
}
|
|
||||||
|
|
||||||
_Neo_EnableMMC( false ); // Close SPI port to MMC card
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NMMC_readSectors (u32 sector, u32 totalSecs, void* buffer)
|
|
||||||
{
|
|
||||||
u32 i;
|
|
||||||
u8 *p=(u8*)buffer;
|
|
||||||
|
|
||||||
sector *= BYTES_PER_READ;
|
|
||||||
|
|
||||||
_Neo_EnableMMC( true ); // Open SPI port to MMC card
|
|
||||||
|
|
||||||
while (totalSecs--) {
|
|
||||||
_Neo_SendMMCCommand(MMC_READ_BLOCK, sector );
|
|
||||||
if( _Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( _Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
|
|
||||||
_Neo_EnableMMC( false );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for( i = 0; i < BYTES_PER_READ; i++ ) // Read in a block of data
|
|
||||||
*p++ = _Neo_SPI( 0xFF );
|
|
||||||
_Neo_SPI( 0xFF ); // Ignore CRC16
|
|
||||||
_Neo_SPI( 0xFF ); // Ignore CRC16
|
|
||||||
sector += BYTES_PER_READ;
|
|
||||||
}
|
|
||||||
|
|
||||||
_Neo_EnableMMC( false ); // Close SPI port to MMC card
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const IO_INTERFACE _io_nmmc = {
|
|
||||||
DEVICE_TYPE_NMMC,
|
|
||||||
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_NDS,
|
|
||||||
(FN_MEDIUM_STARTUP)&_NMMC_startUp,
|
|
||||||
(FN_MEDIUM_ISINSERTED)&_NMMC_isInserted,
|
|
||||||
(FN_MEDIUM_READSECTORS)&_NMMC_readSectors,
|
|
||||||
(FN_MEDIUM_WRITESECTORS)&_NMMC_writeSectors,
|
|
||||||
(FN_MEDIUM_CLEARSTATUS)&_NMMC_clearStatus,
|
|
||||||
(FN_MEDIUM_SHUTDOWN)&_NMMC_shutdown
|
|
||||||
} ;
|
|
||||||
|
|
||||||
#endif // defined NDS
|
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
io_nmmc.h
|
|
||||||
|
|
||||||
Hardware Routines for reading an SD or MMC card using
|
|
||||||
a Neoflash MK2 or MK3.
|
|
||||||
|
|
||||||
Original version written by www.neoflash.com,
|
|
||||||
moddified and used with permission of www.neoflash.com
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_NMMC_H
|
|
||||||
#define IO_NMMC_H
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
#ifdef NDS
|
|
||||||
|
|
||||||
// 'NMMC'
|
|
||||||
#define DEVICE_TYPE_NMMC 0x434D4D4E
|
|
||||||
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_nmmc;
|
|
||||||
|
|
||||||
#endif // defined NDS
|
|
||||||
|
|
||||||
#endif // define IO_NMMC_H
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
io_m3_common.h
|
|
||||||
|
|
||||||
Routines common to all version of the Super Card
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "io_sc_common.h"
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_SC_changeMode (was SC_Unlock)
|
|
||||||
Added by MightyMax
|
|
||||||
Modified by Chishm
|
|
||||||
Modified again by loopy
|
|
||||||
1=ram(readonly), 5=ram, 3=SD interface?
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
void _SC_changeMode(u8 mode) {
|
|
||||||
vu16 *unlockAddress = (vu16*)0x09FFFFFE;
|
|
||||||
*unlockAddress = 0xA55A ;
|
|
||||||
*unlockAddress = 0xA55A ;
|
|
||||||
*unlockAddress = mode ;
|
|
||||||
*unlockAddress = mode ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
io_sc_common.h
|
|
||||||
|
|
||||||
Routines common to all version of the Super Card
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2007-01-29 - Chishm
|
|
||||||
* Added SC_MODE_FLASH
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_SC_COMMON_H
|
|
||||||
#define IO_SC_COMMON_H
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// Values for changing mode
|
|
||||||
#define SC_MODE_FLASH 0x1510
|
|
||||||
#define SC_MODE_RAM 0x5
|
|
||||||
#define SC_MODE_MEDIA 0x3
|
|
||||||
#define SC_MODE_RAM_RO 0x1
|
|
||||||
|
|
||||||
extern void _SC_changeMode (u8 mode);
|
|
||||||
|
|
||||||
#endif // IO_SC_COMMON_H
|
|
@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
io_sccf.c based on
|
|
||||||
|
|
||||||
compact_flash.c
|
|
||||||
By chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Hardware Routines for reading a compact flash card
|
|
||||||
using the Super Card CF
|
|
||||||
|
|
||||||
CF routines modified with help from Darkfader
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "io_sccf.h"
|
|
||||||
#include "io_sc_common.h"
|
|
||||||
#include "io_cf_common.h"
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// SC CF Addresses
|
|
||||||
#define REG_SCCF_STS ((vu16*)0x098C0000) // Status of the CF Card / Device control
|
|
||||||
#define REG_SCCF_CMD ((vu16*)0x090E0000) // Commands sent to control chip and status return
|
|
||||||
#define REG_SCCF_ERR ((vu16*)0x09020000) // Errors / Features
|
|
||||||
|
|
||||||
#define REG_SCCF_SEC ((vu16*)0x09040000) // Number of sector to transfer
|
|
||||||
#define REG_SCCF_LBA1 ((vu16*)0x09060000) // 1st byte of sector address
|
|
||||||
#define REG_SCCF_LBA2 ((vu16*)0x09080000) // 2nd byte of sector address
|
|
||||||
#define REG_SCCF_LBA3 ((vu16*)0x090A0000) // 3rd byte of sector address
|
|
||||||
#define REG_SCCF_LBA4 ((vu16*)0x090C0000) // last nibble of sector address | 0xE0
|
|
||||||
|
|
||||||
#define REG_SCCF_DATA ((vu16*)0x09000000) // Pointer to buffer of CF data transered from card
|
|
||||||
|
|
||||||
static const CF_REGISTERS _SCCF_Registers = {
|
|
||||||
REG_SCCF_DATA,
|
|
||||||
REG_SCCF_STS,
|
|
||||||
REG_SCCF_CMD,
|
|
||||||
REG_SCCF_ERR,
|
|
||||||
REG_SCCF_SEC,
|
|
||||||
REG_SCCF_LBA1,
|
|
||||||
REG_SCCF_LBA2,
|
|
||||||
REG_SCCF_LBA3,
|
|
||||||
REG_SCCF_LBA4
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
bool _SCCF_startup(void) {
|
|
||||||
_SC_changeMode (SC_MODE_MEDIA);
|
|
||||||
return _CF_startup(&_SCCF_Registers);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const IO_INTERFACE _io_sccf = {
|
|
||||||
DEVICE_TYPE_SCCF,
|
|
||||||
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
|
|
||||||
(FN_MEDIUM_STARTUP)&_SCCF_startup,
|
|
||||||
(FN_MEDIUM_ISINSERTED)&_CF_isInserted,
|
|
||||||
(FN_MEDIUM_READSECTORS)&_CF_readSectors,
|
|
||||||
(FN_MEDIUM_WRITESECTORS)&_CF_writeSectors,
|
|
||||||
(FN_MEDIUM_CLEARSTATUS)&_CF_clearStatus,
|
|
||||||
(FN_MEDIUM_SHUTDOWN)&_CF_shutdown
|
|
||||||
} ;
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
io_sccf.h
|
|
||||||
|
|
||||||
Hardware Routines for reading a compact flash card
|
|
||||||
using the Supercard CF
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_SCCF_H
|
|
||||||
#define IO_SCCF_H
|
|
||||||
|
|
||||||
// 'SCCF'
|
|
||||||
#define DEVICE_TYPE_SCCF 0x46434353
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_sccf;
|
|
||||||
|
|
||||||
#endif // define IO_SCCF_H
|
|
@ -1,399 +0,0 @@
|
|||||||
/*
|
|
||||||
io_scsd.c
|
|
||||||
|
|
||||||
Hardware Routines for reading a Secure Digital card
|
|
||||||
using the SC SD
|
|
||||||
|
|
||||||
Some code based on scsd_c.c, written by Amadeus
|
|
||||||
and Jean-Pierre Thomasset as part of DSLinux.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-22 - Chishm
|
|
||||||
* First release of stable code
|
|
||||||
|
|
||||||
2006-07-25 - Chishm
|
|
||||||
* Improved startup function that doesn't delay hundreds of seconds
|
|
||||||
before reporting no card inserted.
|
|
||||||
|
|
||||||
2006-08-05 - Chishm
|
|
||||||
* Tries multiple times to get a Relative Card Address at startup
|
|
||||||
|
|
||||||
2006-08-07 - Chishm
|
|
||||||
* Moved the SD initialization to a common function
|
|
||||||
|
|
||||||
2006-08-19 - Chishm
|
|
||||||
* Added SuperCard Lite support
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "io_scsd.h"
|
|
||||||
#include "io_sd_common.h"
|
|
||||||
#include "io_sc_common.h"
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// SCSD register addresses
|
|
||||||
#define REG_SCSD_CMD (*(vu16*)(0x09800000))
|
|
||||||
/* bit 0: command bit to read */
|
|
||||||
/* bit 7: command bit to write */
|
|
||||||
|
|
||||||
#define REG_SCSD_DATAWRITE (*(vu16*)(0x09000000))
|
|
||||||
#define REG_SCSD_DATAREAD (*(vu16*)(0x09100000))
|
|
||||||
#define REG_SCSD_DATAREAD_32 (*(vu32*)(0x09100000))
|
|
||||||
#define REG_SCSD_LITE_ENABLE (*(vu16*)(0x09440000))
|
|
||||||
#define REG_SCSD_LOCK (*(vu16*)(0x09FFFFFE))
|
|
||||||
/* bit 0: 1 */
|
|
||||||
/* bit 1: enable IO interface (SD,CF) */
|
|
||||||
/* bit 2: enable R/W SDRAM access */
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Responses
|
|
||||||
#define SCSD_STS_BUSY 0x100
|
|
||||||
#define SCSD_STS_INSERTED 0x300
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Send / receive timeouts, to stop infinite wait loops
|
|
||||||
#define NUM_STARTUP_CLOCKS 100 // Number of empty (0xFF when sending) bytes to send/receive to/from the card
|
|
||||||
#define TRANSMIT_TIMEOUT 100000 // Time to wait for the SC to respond to transmit or receive requests
|
|
||||||
#define RESPONSE_TIMEOUT 256 // Number of clocks sent to the SD card before giving up
|
|
||||||
#define BUSY_WAIT_TIMEOUT 500000
|
|
||||||
#define WRITE_TIMEOUT 3000 // Time to wait for the card to finish writing
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Variables required for tracking SD state
|
|
||||||
static u32 _SCSD_relativeCardAddress = 0; // Preshifted Relative Card Address
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Internal SC SD functions
|
|
||||||
|
|
||||||
extern bool _SCSD_writeData_s (u8 *data, u16* crc);
|
|
||||||
|
|
||||||
static inline void _SCSD_unlock (void) {
|
|
||||||
_SC_changeMode (SC_MODE_MEDIA);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _SCSD_enable_lite (void) {
|
|
||||||
REG_SCSD_LITE_ENABLE = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _SCSD_sendCommand (u8 command, u32 argument) {
|
|
||||||
u8 databuff[6];
|
|
||||||
u8 *tempDataPtr = databuff;
|
|
||||||
int length = 6;
|
|
||||||
u16 dataByte;
|
|
||||||
int curBit;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
*tempDataPtr++ = command | 0x40;
|
|
||||||
*tempDataPtr++ = argument>>24;
|
|
||||||
*tempDataPtr++ = argument>>16;
|
|
||||||
*tempDataPtr++ = argument>>8;
|
|
||||||
*tempDataPtr++ = argument;
|
|
||||||
*tempDataPtr = _SD_CRC7 (databuff, 5);
|
|
||||||
|
|
||||||
i = BUSY_WAIT_TIMEOUT;
|
|
||||||
while (((REG_SCSD_CMD & 0x01) == 0) && (--i));
|
|
||||||
if (i == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dataByte = REG_SCSD_CMD;
|
|
||||||
|
|
||||||
tempDataPtr = databuff;
|
|
||||||
|
|
||||||
while (length--) {
|
|
||||||
dataByte = *tempDataPtr++;
|
|
||||||
for (curBit = 7; curBit >=0; curBit--){
|
|
||||||
REG_SCSD_CMD = dataByte;
|
|
||||||
dataByte = dataByte << 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the response from the SD card to a previous command.
|
|
||||||
static bool _SCSD_getResponse (u8* dest, u32 length) {
|
|
||||||
u32 i;
|
|
||||||
int dataByte;
|
|
||||||
int numBits = length * 8;
|
|
||||||
|
|
||||||
// Wait for the card to be non-busy
|
|
||||||
i = BUSY_WAIT_TIMEOUT;
|
|
||||||
while (((REG_SCSD_CMD & 0x01) != 0) && (--i));
|
|
||||||
if (dest == NULL) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
// Still busy after the timeout has passed
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first bit is always 0
|
|
||||||
dataByte = 0;
|
|
||||||
numBits--;
|
|
||||||
// Read the remaining bits in the response.
|
|
||||||
// It's always most significant bit first
|
|
||||||
while (numBits--) {
|
|
||||||
dataByte = (dataByte << 1) | (REG_SCSD_CMD & 0x01);
|
|
||||||
if ((numBits & 0x7) == 0) {
|
|
||||||
// It's read a whole byte, so store it
|
|
||||||
*dest++ = (u8)dataByte;
|
|
||||||
dataByte = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send 16 more clocks, 8 more than the delay required between a response and the next command
|
|
||||||
for (i = 0; i < 16; i++) {
|
|
||||||
dataByte = REG_SCSD_CMD;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _SCSD_getResponse_R1 (u8* dest) {
|
|
||||||
return _SCSD_getResponse (dest, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _SCSD_getResponse_R1b (u8* dest) {
|
|
||||||
return _SCSD_getResponse (dest, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _SCSD_getResponse_R2 (u8* dest) {
|
|
||||||
return _SCSD_getResponse (dest, 17);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _SCSD_getResponse_R3 (u8* dest) {
|
|
||||||
return _SCSD_getResponse (dest, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _SCSD_getResponse_R6 (u8* dest) {
|
|
||||||
return _SCSD_getResponse (dest, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _SCSD_sendClocks (u32 numClocks) {
|
|
||||||
u16 temp;
|
|
||||||
do {
|
|
||||||
temp = REG_SCSD_CMD;
|
|
||||||
} while (numClocks--);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _SCSD_cmd_6byte_response (u8* responseBuffer, u8 command, u32 data) {
|
|
||||||
_SCSD_sendCommand (command, data);
|
|
||||||
return _SCSD_getResponse (responseBuffer, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _SCSD_cmd_17byte_response (u8* responseBuffer, u8 command, u32 data) {
|
|
||||||
_SCSD_sendCommand (command, data);
|
|
||||||
return _SCSD_getResponse (responseBuffer, 17);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool _SCSD_initCard (void) {
|
|
||||||
_SCSD_enable_lite();
|
|
||||||
|
|
||||||
// Give the card time to stabilise
|
|
||||||
_SCSD_sendClocks (NUM_STARTUP_CLOCKS);
|
|
||||||
|
|
||||||
// Reset the card
|
|
||||||
if (!_SCSD_sendCommand (GO_IDLE_STATE, 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_SCSD_sendClocks (NUM_STARTUP_CLOCKS);
|
|
||||||
|
|
||||||
// Card is now reset, including it's address
|
|
||||||
_SCSD_relativeCardAddress = 0;
|
|
||||||
|
|
||||||
// Init the card
|
|
||||||
return _SD_InitCard (_SCSD_cmd_6byte_response,
|
|
||||||
_SCSD_cmd_17byte_response,
|
|
||||||
true,
|
|
||||||
&_SCSD_relativeCardAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _SCSD_readData (void* buffer) {
|
|
||||||
u8* buff_u8 = (u8*)buffer;
|
|
||||||
u16* buff = (u16*)buffer;
|
|
||||||
volatile register u32 temp;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
i = BUSY_WAIT_TIMEOUT;
|
|
||||||
while ((REG_SCSD_DATAREAD & SCSD_STS_BUSY) && (--i));
|
|
||||||
if (i == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
i=256;
|
|
||||||
if ((u32)buff_u8 & 0x01) {
|
|
||||||
while(i--) {
|
|
||||||
temp = REG_SCSD_DATAREAD_32;
|
|
||||||
temp = REG_SCSD_DATAREAD_32 >> 16;
|
|
||||||
*buff_u8++ = (u8)temp;
|
|
||||||
*buff_u8++ = (u8)(temp >> 8);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while(i--) {
|
|
||||||
temp = REG_SCSD_DATAREAD_32;
|
|
||||||
temp = REG_SCSD_DATAREAD_32 >> 16;
|
|
||||||
*buff++ = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
temp = REG_SCSD_DATAREAD_32;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp = REG_SCSD_DATAREAD;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
// Functions needed for the external interface
|
|
||||||
|
|
||||||
bool _SCSD_startUp (void) {
|
|
||||||
_SCSD_unlock();
|
|
||||||
return _SCSD_initCard();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _SCSD_isInserted (void) {
|
|
||||||
u8 responseBuffer [6];
|
|
||||||
|
|
||||||
// Make sure the card receives the command
|
|
||||||
if (!_SCSD_sendCommand (SEND_STATUS, 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Make sure the card responds
|
|
||||||
if (!_SCSD_getResponse_R1 (responseBuffer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Make sure the card responded correctly
|
|
||||||
if (responseBuffer[0] != SEND_STATUS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _SCSD_readSectors (u32 sector, u32 numSectors, void* buffer) {
|
|
||||||
u32 i;
|
|
||||||
u8* dest = (u8*) buffer;
|
|
||||||
u8 responseBuffer[6];
|
|
||||||
|
|
||||||
if (numSectors == 1) {
|
|
||||||
// If it's only reading one sector, use the (slightly faster) READ_SINGLE_BLOCK
|
|
||||||
if (!_SCSD_sendCommand (READ_SINGLE_BLOCK, sector * BYTES_PER_READ)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_SCSD_readData (buffer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Stream the required number of sectors from the card
|
|
||||||
if (!_SCSD_sendCommand (READ_MULTIPLE_BLOCK, sector * BYTES_PER_READ)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i=0; i < numSectors; i++, dest+=BYTES_PER_READ) {
|
|
||||||
if (!_SCSD_readData(dest)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the streaming
|
|
||||||
_SCSD_sendCommand (STOP_TRANSMISSION, 0);
|
|
||||||
_SCSD_getResponse_R1b (responseBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
_SCSD_sendClocks(0x10);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _SCSD_writeSectors (u32 sector, u32 numSectors, const void* buffer) {
|
|
||||||
u16 crc[4]; // One per data line
|
|
||||||
u8 responseBuffer[6];
|
|
||||||
u32 offset = sector * BYTES_PER_READ;
|
|
||||||
u8* data = (u8*) buffer;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
while (numSectors--) {
|
|
||||||
// Calculate the CRC16
|
|
||||||
_SD_CRC16 ( data, BYTES_PER_READ, (u8*)crc);
|
|
||||||
|
|
||||||
// Send write command and get a response
|
|
||||||
_SCSD_sendCommand (WRITE_BLOCK, offset);
|
|
||||||
if (!_SCSD_getResponse_R1 (responseBuffer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the data and CRC
|
|
||||||
if (! _SCSD_writeData_s (data, crc)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a few clocks to the SD card
|
|
||||||
_SCSD_sendClocks(0x10);
|
|
||||||
|
|
||||||
offset += BYTES_PER_READ;
|
|
||||||
data += BYTES_PER_READ;
|
|
||||||
|
|
||||||
// Wait until card is finished programming
|
|
||||||
i = WRITE_TIMEOUT;
|
|
||||||
responseBuffer[3] = 0;
|
|
||||||
do {
|
|
||||||
_SCSD_sendCommand (SEND_STATUS, _SCSD_relativeCardAddress);
|
|
||||||
_SCSD_getResponse_R1 (responseBuffer);
|
|
||||||
i--;
|
|
||||||
if (i <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} while (((responseBuffer[3] & 0x1f) != ((SD_STATE_TRAN << 1) | READY_FOR_DATA)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _SCSD_clearStatus (void) {
|
|
||||||
return _SCSD_initCard ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _SCSD_shutdown (void) {
|
|
||||||
_SC_changeMode (SC_MODE_RAM_RO);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const IO_INTERFACE _io_scsd = {
|
|
||||||
DEVICE_TYPE_SCSD,
|
|
||||||
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
|
|
||||||
(FN_MEDIUM_STARTUP)&_SCSD_startUp,
|
|
||||||
(FN_MEDIUM_ISINSERTED)&_SCSD_isInserted,
|
|
||||||
(FN_MEDIUM_READSECTORS)&_SCSD_readSectors,
|
|
||||||
(FN_MEDIUM_WRITESECTORS)&_SCSD_writeSectors,
|
|
||||||
(FN_MEDIUM_CLEARSTATUS)&_SCSD_clearStatus,
|
|
||||||
(FN_MEDIUM_SHUTDOWN)&_SCSD_shutdown
|
|
||||||
} ;
|
|
||||||
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
io_scsd.h
|
|
||||||
|
|
||||||
Hardware Routines for reading a Secure Digital card
|
|
||||||
using the Supercard SD
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-07-22 - Chishm
|
|
||||||
* First release of stable code
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_SCSD_H
|
|
||||||
#define IO_SCSD_H
|
|
||||||
|
|
||||||
// 'SCSD'
|
|
||||||
#define DEVICE_TYPE_SCSD 0x44534353
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
// export interface
|
|
||||||
extern const IO_INTERFACE _io_scsd ;
|
|
||||||
|
|
||||||
#endif // define IO_SCSD_H
|
|
@ -1,139 +0,0 @@
|
|||||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
||||||
@ io_scsd_s.s
|
|
||||||
@
|
|
||||||
@ Hardware Routines for reading a Secure Digital card
|
|
||||||
@ using the SC SD
|
|
||||||
@
|
|
||||||
@ Based on code supplied by Romman
|
|
||||||
@
|
|
||||||
@ Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
@
|
|
||||||
@ Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
@ are permitted provided that the following conditions are met:
|
|
||||||
@
|
|
||||||
@ 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
@ this list of conditions and the following disclaimer.
|
|
||||||
@ 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
@ this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
@ other materials provided with the distribution.
|
|
||||||
@ 3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
@ from this software without specific prior written permission.
|
|
||||||
@
|
|
||||||
@ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
@ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
@ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
@ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@
|
|
||||||
@ 2006-07-22 - Chishm
|
|
||||||
@ * First release of stable code
|
|
||||||
@
|
|
||||||
@ 2006-08-19 - Chishm
|
|
||||||
@ * Added SuperCard Lite support
|
|
||||||
@
|
|
||||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
||||||
|
|
||||||
.align 4
|
|
||||||
.arm
|
|
||||||
|
|
||||||
.equ REG_SCSD_DATAWRITE, 0x09000000
|
|
||||||
.equ BYTES_PER_READ, 0x200
|
|
||||||
.equ SCSD_STS_BUSY, 0x100
|
|
||||||
.equ BUSY_WAIT_TIMEOUT, 0x10000
|
|
||||||
.equ FALSE, 0
|
|
||||||
.equ TRUE, 1
|
|
||||||
|
|
||||||
@ bool _SCSD_writeData_s (u8 *data, u16* crc)
|
|
||||||
|
|
||||||
.global _SCSD_writeData_s
|
|
||||||
|
|
||||||
_SCSD_writeData_s:
|
|
||||||
stmfd r13!, {r4-r5}
|
|
||||||
mov r5, #BYTES_PER_READ
|
|
||||||
mov r2, #REG_SCSD_DATAWRITE
|
|
||||||
|
|
||||||
@ Wait for a free data buffer on the SD card
|
|
||||||
mov r4, #BUSY_WAIT_TIMEOUT
|
|
||||||
_SCSD_writeData_busy_wait:
|
|
||||||
@ Test for timeout
|
|
||||||
subs r4, r4, #1
|
|
||||||
moveq r0, #FALSE @ return false on failure
|
|
||||||
beq _SCSD_writeData_return
|
|
||||||
@ Check the busy bit of the status register
|
|
||||||
ldrh r3, [r2]
|
|
||||||
tst r3, #SCSD_STS_BUSY
|
|
||||||
beq _SCSD_writeData_busy_wait
|
|
||||||
|
|
||||||
ldrh r3, [r2] @ extra clock
|
|
||||||
|
|
||||||
mov r3, #0 @ start bit
|
|
||||||
strh r3,[r2]
|
|
||||||
|
|
||||||
@ Check if the data buffer is aligned on a halfword boundary
|
|
||||||
tst r0, #1
|
|
||||||
beq _SCSD_writeData_data_loop
|
|
||||||
|
|
||||||
@ Used when the source data is unaligned
|
|
||||||
_SCSD_writeData_data_loop_unaligned:
|
|
||||||
ldrb r3, [r0], #1
|
|
||||||
ldrb r4, [r0], #1
|
|
||||||
orr r3, r3, r4, lsl #8
|
|
||||||
stmia r2, {r3-r4}
|
|
||||||
subs r5, r5, #2
|
|
||||||
bne _SCSD_writeData_data_loop_unaligned
|
|
||||||
b _SCSD_writeData_crc
|
|
||||||
|
|
||||||
@ Write the data to the card
|
|
||||||
@ 4 halfwords are transmitted to the Supercard at once, for timing purposes
|
|
||||||
@ Only the first halfword needs to contain data for standard SuperCards
|
|
||||||
@ For the SuperCard Lite, the data is split into 4 nibbles, one per halfword
|
|
||||||
_SCSD_writeData_data_loop:
|
|
||||||
ldrh r3, [r0], #2
|
|
||||||
|
|
||||||
@ This bit added for SCLite. Notice that the shift is not the same as in
|
|
||||||
@ the original (buggy) code supplied by Romman
|
|
||||||
add r3, r3, r3, lsl #20
|
|
||||||
mov r4, r3, lsr #8
|
|
||||||
|
|
||||||
stmia r2, {r3-r4}
|
|
||||||
|
|
||||||
subs r5, r5, #2
|
|
||||||
bne _SCSD_writeData_data_loop
|
|
||||||
|
|
||||||
@ Send the data CRC
|
|
||||||
_SCSD_writeData_crc:
|
|
||||||
cmp r1, #0
|
|
||||||
movne r0, r1
|
|
||||||
movne r1, #0
|
|
||||||
movne r5, #8
|
|
||||||
bne _SCSD_writeData_data_loop
|
|
||||||
|
|
||||||
mov r3, #0xff @ end bit
|
|
||||||
strh r3, [r2]
|
|
||||||
|
|
||||||
@ Wait for the SD card to signal that it is finished recieving
|
|
||||||
mov r4, #BUSY_WAIT_TIMEOUT
|
|
||||||
_SCSD_writeData_finished_wait:
|
|
||||||
@ Test for timeout
|
|
||||||
subs r4, r4, #1
|
|
||||||
moveq r0, #FALSE @ return false on failure
|
|
||||||
beq _SCSD_writeData_return
|
|
||||||
@ Check the busy bit of the status register
|
|
||||||
ldrh r3, [r2]
|
|
||||||
tst r3, #0x100
|
|
||||||
bne _SCSD_writeData_finished_wait
|
|
||||||
|
|
||||||
@ Send 8 more clocks, as required by the SD card
|
|
||||||
ldmia r2, {r3-r4}
|
|
||||||
|
|
||||||
@ return true for success
|
|
||||||
mov r0, #TRUE
|
|
||||||
|
|
||||||
_SCSD_writeData_return:
|
|
||||||
ldmfd r13!,{r4-r5}
|
|
||||||
bx r14
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
|||||||
/*
|
|
||||||
io_sd_common.c
|
|
||||||
|
|
||||||
By chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Common SD card routines
|
|
||||||
|
|
||||||
SD routines partially based on sd.s by Romman
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-08-07 - Chishm
|
|
||||||
* Moved the SD initialization to a common function
|
|
||||||
* Increased timeouts for slower cards
|
|
||||||
|
|
||||||
2006-08-08 - Chishm
|
|
||||||
* Init aborts when it doesn't get a valid response to APP_CMD - speeds up detection when no card is inserted
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "io_sd_common.h"
|
|
||||||
|
|
||||||
#define MAX_STARTUP_TRIES 1000 // Arbitrary value, check if the card is ready 20 times before giving up
|
|
||||||
#define RESPONSE_TIMEOUT 256 // Number of clocks sent to the SD card before giving up
|
|
||||||
|
|
||||||
/*
|
|
||||||
Improved CRC7 function provided by cory1492
|
|
||||||
Calculates the CRC of an SD command, and includes the end bit in the byte
|
|
||||||
*/
|
|
||||||
u8 _SD_CRC7(u8* data, int cnt) {
|
|
||||||
int i, a;
|
|
||||||
u8 crc, temp;
|
|
||||||
|
|
||||||
crc = 0;
|
|
||||||
for (a = 0; a < cnt; a++)
|
|
||||||
{
|
|
||||||
temp = data[a];
|
|
||||||
for (i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
crc <<= 1;
|
|
||||||
if ((temp & 0x80) ^ (crc & 0x80)) crc ^= 0x09;
|
|
||||||
temp <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crc = (crc << 1) | 1;
|
|
||||||
return(crc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Calculates the CRC16 for a sector of data. Calculates it
|
|
||||||
as 4 separate lots, merged into one buffer. This is used
|
|
||||||
for 4 SD data lines, not for 1 data line alone.
|
|
||||||
*/
|
|
||||||
void _SD_CRC16 (u8* buff, int buffLength, u8* crc16buff) {
|
|
||||||
u32 a, b, c, d;
|
|
||||||
int count;
|
|
||||||
u32 bitPattern = 0x80808080; // r7
|
|
||||||
u32 crcConst = 0x1021; // r8
|
|
||||||
u32 dataByte = 0; // r2
|
|
||||||
|
|
||||||
a = 0; // r3
|
|
||||||
b = 0; // r4
|
|
||||||
c = 0; // r5
|
|
||||||
d = 0; // r6
|
|
||||||
|
|
||||||
buffLength = buffLength * 8;
|
|
||||||
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (bitPattern & 0x80) dataByte = *buff++;
|
|
||||||
|
|
||||||
a = a << 1;
|
|
||||||
if ( a & 0x10000) a ^= crcConst;
|
|
||||||
if (dataByte & (bitPattern >> 24)) a ^= crcConst;
|
|
||||||
|
|
||||||
b = b << 1;
|
|
||||||
if (b & 0x10000) b ^= crcConst;
|
|
||||||
if (dataByte & (bitPattern >> 25)) b ^= crcConst;
|
|
||||||
|
|
||||||
c = c << 1;
|
|
||||||
if (c & 0x10000) c ^= crcConst;
|
|
||||||
if (dataByte & (bitPattern >> 26)) c ^= crcConst;
|
|
||||||
|
|
||||||
d = d << 1;
|
|
||||||
if (d & 0x10000) d ^= crcConst;
|
|
||||||
if (dataByte & (bitPattern >> 27)) d ^= crcConst;
|
|
||||||
|
|
||||||
bitPattern = (bitPattern >> 4) | (bitPattern << 28);
|
|
||||||
} while (buffLength-=4);
|
|
||||||
|
|
||||||
count = 16; // r8
|
|
||||||
|
|
||||||
do {
|
|
||||||
bitPattern = bitPattern << 4;
|
|
||||||
if (a & 0x8000) bitPattern |= 8;
|
|
||||||
if (b & 0x8000) bitPattern |= 4;
|
|
||||||
if (c & 0x8000) bitPattern |= 2;
|
|
||||||
if (d & 0x8000) bitPattern |= 1;
|
|
||||||
|
|
||||||
a = a << 1;
|
|
||||||
b = b << 1;
|
|
||||||
c = c << 1;
|
|
||||||
d = d << 1;
|
|
||||||
|
|
||||||
count--;
|
|
||||||
|
|
||||||
if (!(count & 0x01)) {
|
|
||||||
*crc16buff++ = (u8)(bitPattern & 0xff);
|
|
||||||
}
|
|
||||||
} while (count != 0);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialise the SD card, after it has been sent into an Idle state
|
|
||||||
cmd_6byte_response: a pointer to a function that sends the SD card a command and gets a 6 byte response
|
|
||||||
cmd_17byte_response: a pointer to a function that sends the SD card a command and gets a 17 byte response
|
|
||||||
use4bitBus: initialise card to use a 4 bit data bus when communicating with the card
|
|
||||||
RCA: a pointer to the location to store the card's Relative Card Address, preshifted up by 16 bits.
|
|
||||||
*/
|
|
||||||
bool _SD_InitCard (_SD_FN_CMD_6BYTE_RESPONSE cmd_6byte_response,
|
|
||||||
_SD_FN_CMD_17BYTE_RESPONSE cmd_17byte_response,
|
|
||||||
bool use4bitBus,
|
|
||||||
u32 *RCA)
|
|
||||||
{
|
|
||||||
u8 responseBuffer[17] = {0};
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_STARTUP_TRIES ; i++) {
|
|
||||||
cmd_6byte_response (responseBuffer, APP_CMD, 0);
|
|
||||||
// Check that the card gave the correct response
|
|
||||||
if (responseBuffer[0] != APP_CMD) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
cmd_6byte_response (responseBuffer, SD_APP_OP_COND, SD_OCR_VALUE) &&
|
|
||||||
((responseBuffer[1] & 0x80) != 0))
|
|
||||||
{
|
|
||||||
// Card is ready to receive commands now
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i >= MAX_STARTUP_TRIES) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The card's name, as assigned by the manufacturer
|
|
||||||
cmd_17byte_response (responseBuffer, ALL_SEND_CID, 0);
|
|
||||||
|
|
||||||
// Get a new address
|
|
||||||
for (i = 0; i < MAX_STARTUP_TRIES ; i++) {
|
|
||||||
cmd_6byte_response (responseBuffer, SEND_RELATIVE_ADDR, 0);
|
|
||||||
*RCA = (responseBuffer[1] << 24) | (responseBuffer[2] << 16);
|
|
||||||
if ((responseBuffer[3] & 0x1e) != (SD_STATE_STBY << 1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i >= MAX_STARTUP_TRIES) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some cards won't go to higher speeds unless they think you checked their capabilities
|
|
||||||
cmd_17byte_response (responseBuffer, SEND_CSD, *RCA);
|
|
||||||
|
|
||||||
// Only this card should respond to all future commands
|
|
||||||
cmd_6byte_response (responseBuffer, SELECT_CARD, *RCA);
|
|
||||||
|
|
||||||
if (use4bitBus) {
|
|
||||||
// Set a 4 bit data bus
|
|
||||||
cmd_6byte_response (responseBuffer, APP_CMD, *RCA);
|
|
||||||
cmd_6byte_response (responseBuffer, SET_BUS_WIDTH, 2); // 4-bit mode.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use 512 byte blocks
|
|
||||||
cmd_6byte_response (responseBuffer, SET_BLOCKLEN, 512); // 512 byte blocks
|
|
||||||
|
|
||||||
// Wait until card is ready for data
|
|
||||||
i = 0;
|
|
||||||
do {
|
|
||||||
if (i >= RESPONSE_TIMEOUT) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
} while (!cmd_6byte_response (responseBuffer, SEND_STATUS, *RCA) && ((responseBuffer[3] & 0x1f) != ((SD_STATE_TRAN << 1) | READY_FOR_DATA)));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
io_sd_common.h
|
|
||||||
|
|
||||||
By chishm (Michael Chisholm)
|
|
||||||
|
|
||||||
Common SD card routines
|
|
||||||
|
|
||||||
SD routines partially based on sd.s by Romman
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-07-28 - Chishm
|
|
||||||
* Changed voltage range that the SD card can use
|
|
||||||
|
|
||||||
2006-11-14 - Chishm
|
|
||||||
* Reduced voltage range again. Hopefully fixes MicroSD cards.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IO_SD_COMMON_H
|
|
||||||
#define IO_SD_COMMON_H
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
/* SD commands */
|
|
||||||
#define GO_IDLE_STATE 0
|
|
||||||
#define ALL_SEND_CID 2
|
|
||||||
#define SEND_RELATIVE_ADDR 3
|
|
||||||
#define SELECT_CARD 7
|
|
||||||
#define SEND_CSD 9
|
|
||||||
#define STOP_TRANSMISSION 12
|
|
||||||
#define SEND_STATUS 13
|
|
||||||
#define GO_INACTIVE_STATE 15
|
|
||||||
#define SET_BLOCKLEN 16
|
|
||||||
#define READ_SINGLE_BLOCK 17
|
|
||||||
#define READ_MULTIPLE_BLOCK 18
|
|
||||||
#define WRITE_BLOCK 24
|
|
||||||
#define WRITE_MULTIPLE_BLOCK 25
|
|
||||||
#define APP_CMD 55
|
|
||||||
|
|
||||||
/* SD App commands */
|
|
||||||
#define SET_BUS_WIDTH 6
|
|
||||||
#define SD_APP_OP_COND 41
|
|
||||||
|
|
||||||
/* OCR (Operating Conditions Register) send value */
|
|
||||||
#define SD_OCR_VALUE 0x00030000 /* 2.8V to 3.0V */
|
|
||||||
//#define SD_OCR_VALUE 0x003F8000 /* 2.7V to 3.4V */
|
|
||||||
//#define SD_OCR_VALUE 0x00FC0000
|
|
||||||
|
|
||||||
/* SD Data repsonses */
|
|
||||||
#define SD_CARD_BUSY 0xff
|
|
||||||
|
|
||||||
/* SD states */
|
|
||||||
#define SD_STATE_IDLE 0 // Idle state, after power on or GO_IDLE_STATE command
|
|
||||||
#define SD_STATE_READY 1 // Ready state, after card replies non-busy to SD_APP_OP_COND
|
|
||||||
#define SD_STATE_IDENT 2 // Identification state, after ALL_SEND_CID
|
|
||||||
#define SD_STATE_STBY 3 // Standby state, when card is deselected
|
|
||||||
#define SD_STATE_TRAN 4 // Transfer state, after card is selected and ready for data transfer
|
|
||||||
#define SD_STATE_DATA 5 //
|
|
||||||
#define SD_STATE_RCV 6 // Receive data state
|
|
||||||
#define SD_STATE_PRG 7 // Programming state
|
|
||||||
#define SD_STATE_DIS 8 // Disconnect state
|
|
||||||
#define SD_STATE_INA 9 // Inactive state, after GO_INACTIVE_STATE
|
|
||||||
|
|
||||||
#define READY_FOR_DATA 1 // bit 8 in card status
|
|
||||||
|
|
||||||
/*
|
|
||||||
Calculate the CRC7 of a command and return it preshifted with
|
|
||||||
an end bit added
|
|
||||||
*/
|
|
||||||
extern u8 _SD_CRC7(u8* data, int size);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Calculate the CRC16 of a block of data, ready for transmission on
|
|
||||||
four data lines at once
|
|
||||||
*/
|
|
||||||
extern void _SD_CRC16 (u8* buff, int buffLength, u8* crc16buff);
|
|
||||||
|
|
||||||
typedef bool (*_SD_FN_CMD_6BYTE_RESPONSE) (u8* responseBuffer, u8 command, u32 data);
|
|
||||||
typedef bool (*_SD_FN_CMD_17BYTE_RESPONSE) (u8* responseBuffer, u8 command, u32 data);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialise the SD card, after it has been sent into an Idle state
|
|
||||||
cmd_6byte_response: a pointer to a function that sends the SD card a command and gets a 6 byte response
|
|
||||||
cmd_17byte_response: a pointer to a function that sends the SD card a command and gets a 17 byte response
|
|
||||||
use4bitBus: initialise card to use a 4 bit data bus when communicating with the card
|
|
||||||
RCA: a pointer to the location to store the card's Relative Card Address, preshifted up by 16 bits.
|
|
||||||
*/
|
|
||||||
extern bool _SD_InitCard (_SD_FN_CMD_6BYTE_RESPONSE cmd_6byte_response,
|
|
||||||
_SD_FN_CMD_17BYTE_RESPONSE cmd_17byte_response,
|
|
||||||
bool use4bitBus,
|
|
||||||
u32 *RCA);
|
|
||||||
|
|
||||||
#endif // define IO_SD_COMMON_H
|
|
159
source/fatdir.c
159
source/fatdir.c
@ -26,29 +26,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-08-13 - Chishm
|
|
||||||
* Moved all externally visible directory related functions to fatdir
|
|
||||||
* Added _FAT_mkdir_r
|
|
||||||
|
|
||||||
2006-08-14 - Chishm
|
|
||||||
* Added directory iterator functions
|
|
||||||
|
|
||||||
2006-08-19 - Chishm
|
|
||||||
* Updated dirnext return values to return correctly
|
|
||||||
|
|
||||||
2006-10-01 - Chishm
|
|
||||||
* Now clears the whole cluster when creating a new directory, bug found by Hermes
|
|
||||||
|
|
||||||
2007-01-10 - Chishm
|
|
||||||
* Updated directory iterator functions for DevkitPro r20
|
|
||||||
|
|
||||||
2007-10-25 - Chishm
|
|
||||||
* Use CLUSTER_ERROR when an error occurs with the FAT, not CLUSTER_FREE
|
|
||||||
* Added statvfs functionality
|
|
||||||
|
|
||||||
2008-09-11 - Chishm
|
|
||||||
* Fixed unlink not freeing cluster chains -- discovered by rodries
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -70,15 +47,11 @@
|
|||||||
|
|
||||||
int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) {
|
int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) {
|
||||||
PARTITION* partition = NULL;
|
PARTITION* partition = NULL;
|
||||||
|
|
||||||
DIR_ENTRY dirEntry;
|
DIR_ENTRY dirEntry;
|
||||||
|
|
||||||
// Get the partition this file is on
|
// Get the partition this file is on
|
||||||
_FAT_lock();
|
|
||||||
partition = _FAT_partition_getPartitionFromPath (path);
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
|
||||||
if (partition == NULL) {
|
if (partition == NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = ENODEV;
|
r->_errno = ENODEV;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -88,14 +61,15 @@ int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) {
|
|||||||
path = strchr (path, ':') + 1;
|
path = strchr (path, ':') + 1;
|
||||||
}
|
}
|
||||||
if (strchr (path, ':') != NULL) {
|
if (strchr (path, ':') != NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
// Search for the file on the disc
|
// Search for the file on the disc
|
||||||
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
|
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOENT;
|
r->_errno = ENOENT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -103,7 +77,7 @@ int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) {
|
|||||||
// Fill in the stat struct
|
// Fill in the stat struct
|
||||||
_FAT_directory_entryStat (partition, &dirEntry, st);
|
_FAT_directory_entryStat (partition, &dirEntry, st);
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,23 +90,19 @@ int _FAT_unlink_r (struct _reent *r, const char *path) {
|
|||||||
PARTITION* partition = NULL;
|
PARTITION* partition = NULL;
|
||||||
DIR_ENTRY dirEntry;
|
DIR_ENTRY dirEntry;
|
||||||
DIR_ENTRY dirContents;
|
DIR_ENTRY dirContents;
|
||||||
u32 cluster;
|
uint32_t cluster;
|
||||||
bool nextEntry;
|
bool nextEntry;
|
||||||
bool errorOccured = false;
|
bool errorOccured = false;
|
||||||
|
|
||||||
// Get the partition this directory is on
|
// Get the partition this directory is on
|
||||||
_FAT_lock();
|
|
||||||
partition = _FAT_partition_getPartitionFromPath (path);
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
|
||||||
if (partition == NULL) {
|
if (partition == NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = ENODEV;
|
r->_errno = ENODEV;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we aren't trying to write to a read-only disc
|
// Make sure we aren't trying to write to a read-only disc
|
||||||
if (partition->readOnly) {
|
if (partition->readOnly) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EROFS;
|
r->_errno = EROFS;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -142,14 +112,15 @@ int _FAT_unlink_r (struct _reent *r, const char *path) {
|
|||||||
path = strchr (path, ':') + 1;
|
path = strchr (path, ':') + 1;
|
||||||
}
|
}
|
||||||
if (strchr (path, ':') != NULL) {
|
if (strchr (path, ':') != NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
// Search for the file on the disc
|
// Search for the file on the disc
|
||||||
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
|
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOENT;
|
r->_errno = ENOENT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -164,7 +135,7 @@ int _FAT_unlink_r (struct _reent *r, const char *path) {
|
|||||||
while (nextEntry) {
|
while (nextEntry) {
|
||||||
if (!_FAT_directory_isDot (&dirContents)) {
|
if (!_FAT_directory_isDot (&dirContents)) {
|
||||||
// The directory had something in it that isn't a reference to itself or it's parent
|
// The directory had something in it that isn't a reference to itself or it's parent
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EPERM;
|
r->_errno = EPERM;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -192,7 +163,7 @@ int _FAT_unlink_r (struct _reent *r, const char *path) {
|
|||||||
errorOccured = true;
|
errorOccured = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
if (errorOccured) {
|
if (errorOccured) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
@ -204,11 +175,8 @@ int _FAT_chdir_r (struct _reent *r, const char *path) {
|
|||||||
PARTITION* partition = NULL;
|
PARTITION* partition = NULL;
|
||||||
|
|
||||||
// Get the partition this directory is on
|
// Get the partition this directory is on
|
||||||
_FAT_lock();
|
|
||||||
partition = _FAT_partition_getPartitionFromPath (path);
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
|
||||||
if (partition == NULL) {
|
if (partition == NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = ENODEV;
|
r->_errno = ENODEV;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -218,26 +186,20 @@ int _FAT_chdir_r (struct _reent *r, const char *path) {
|
|||||||
path = strchr (path, ':') + 1;
|
path = strchr (path, ':') + 1;
|
||||||
}
|
}
|
||||||
if (strchr (path, ':') != NULL) {
|
if (strchr (path, ':') != NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the default device to match this one
|
_FAT_lock(&partition->lock);
|
||||||
if (!_FAT_partition_setDefaultPartition (partition)) {
|
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try changing directory
|
// Try changing directory
|
||||||
if (_FAT_directory_chdir (partition, path)) {
|
if (_FAT_directory_chdir (partition, path)) {
|
||||||
// Successful
|
// Successful
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
// Failed
|
// Failed
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOTDIR;
|
r->_errno = ENOTDIR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -248,28 +210,27 @@ int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) {
|
|||||||
DIR_ENTRY oldDirEntry;
|
DIR_ENTRY oldDirEntry;
|
||||||
DIR_ENTRY newDirEntry;
|
DIR_ENTRY newDirEntry;
|
||||||
const char *pathEnd;
|
const char *pathEnd;
|
||||||
u32 dirCluster;
|
uint32_t dirCluster;
|
||||||
|
|
||||||
// Get the partition this directory is on
|
// Get the partition this directory is on
|
||||||
_FAT_lock();
|
|
||||||
partition = _FAT_partition_getPartitionFromPath (oldName);
|
partition = _FAT_partition_getPartitionFromPath (oldName);
|
||||||
|
|
||||||
if (partition == NULL) {
|
if (partition == NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = ENODEV;
|
r->_errno = ENODEV;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
// Make sure the same partition is used for the old and new names
|
// Make sure the same partition is used for the old and new names
|
||||||
if (partition != _FAT_partition_getPartitionFromPath (newName)) {
|
if (partition != _FAT_partition_getPartitionFromPath (newName)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EXDEV;
|
r->_errno = EXDEV;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we aren't trying to write to a read-only disc
|
// Make sure we aren't trying to write to a read-only disc
|
||||||
if (partition->readOnly) {
|
if (partition->readOnly) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EROFS;
|
r->_errno = EROFS;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -279,7 +240,7 @@ int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) {
|
|||||||
oldName = strchr (oldName, ':') + 1;
|
oldName = strchr (oldName, ':') + 1;
|
||||||
}
|
}
|
||||||
if (strchr (oldName, ':') != NULL) {
|
if (strchr (oldName, ':') != NULL) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -287,21 +248,21 @@ int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) {
|
|||||||
newName = strchr (newName, ':') + 1;
|
newName = strchr (newName, ':') + 1;
|
||||||
}
|
}
|
||||||
if (strchr (newName, ':') != NULL) {
|
if (strchr (newName, ':') != NULL) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for the file on the disc
|
// Search for the file on the disc
|
||||||
if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) {
|
if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOENT;
|
r->_errno = ENOENT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure there is no existing file / directory with the new name
|
// Make sure there is no existing file / directory with the new name
|
||||||
if (_FAT_directory_entryFromPath (partition, &newDirEntry, newName, NULL)) {
|
if (_FAT_directory_entryFromPath (partition, &newDirEntry, newName, NULL)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EEXIST;
|
r->_errno = EEXIST;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -318,7 +279,7 @@ int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) {
|
|||||||
// Recycling newDirEntry, since it needs to be recreated anyway
|
// Recycling newDirEntry, since it needs to be recreated anyway
|
||||||
if (!_FAT_directory_entryFromPath (partition, &newDirEntry, newName, pathEnd) ||
|
if (!_FAT_directory_entryFromPath (partition, &newDirEntry, newName, pathEnd) ||
|
||||||
!_FAT_directory_isDirectory(&newDirEntry)) {
|
!_FAT_directory_isDirectory(&newDirEntry)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOTDIR;
|
r->_errno = ENOTDIR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -335,26 +296,26 @@ int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) {
|
|||||||
|
|
||||||
// Write the new entry
|
// Write the new entry
|
||||||
if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) {
|
if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOSPC;
|
r->_errno = ENOSPC;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the old entry
|
// Remove the old entry
|
||||||
if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) {
|
if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EIO;
|
r->_errno = EIO;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush any sectors in the disc cache
|
// Flush any sectors in the disc cache
|
||||||
if (!_FAT_cache_flush (partition->cache)) {
|
if (!_FAT_cache_flush (partition->cache)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EIO;
|
r->_errno = EIO;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,14 +324,11 @@ int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) {
|
|||||||
bool fileExists;
|
bool fileExists;
|
||||||
DIR_ENTRY dirEntry;
|
DIR_ENTRY dirEntry;
|
||||||
const char* pathEnd;
|
const char* pathEnd;
|
||||||
u32 parentCluster, dirCluster;
|
uint32_t parentCluster, dirCluster;
|
||||||
u8 newEntryData[DIR_ENTRY_DATA_SIZE];
|
uint8_t newEntryData[DIR_ENTRY_DATA_SIZE];
|
||||||
|
|
||||||
_FAT_lock();
|
|
||||||
partition = _FAT_partition_getPartitionFromPath (path);
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
|
||||||
if (partition == NULL) {
|
if (partition == NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = ENODEV;
|
r->_errno = ENODEV;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -380,24 +338,25 @@ int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) {
|
|||||||
path = strchr (path, ':') + 1;
|
path = strchr (path, ':') + 1;
|
||||||
}
|
}
|
||||||
if (strchr (path, ':') != NULL) {
|
if (strchr (path, ':') != NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
// Search for the file/directory on the disc
|
// Search for the file/directory on the disc
|
||||||
fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
|
fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
|
||||||
|
|
||||||
// Make sure it doesn't exist
|
// Make sure it doesn't exist
|
||||||
if (fileExists) {
|
if (fileExists) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EEXIST;
|
r->_errno = EEXIST;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (partition->readOnly) {
|
if (partition->readOnly) {
|
||||||
// We can't write to a read-only partition
|
// We can't write to a read-only partition
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EROFS;
|
r->_errno = EROFS;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -413,7 +372,7 @@ int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) {
|
|||||||
// Recycling dirEntry, since it needs to be recreated anyway
|
// Recycling dirEntry, since it needs to be recreated anyway
|
||||||
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
|
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
|
||||||
!_FAT_directory_isDirectory(&dirEntry)) {
|
!_FAT_directory_isDirectory(&dirEntry)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOTDIR;
|
r->_errno = ENOTDIR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -437,7 +396,7 @@ int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) {
|
|||||||
dirCluster = _FAT_fat_linkFreeClusterCleared (partition, CLUSTER_FREE);
|
dirCluster = _FAT_fat_linkFreeClusterCleared (partition, CLUSTER_FREE);
|
||||||
if (!_FAT_fat_isValidCluster(partition, dirCluster)) {
|
if (!_FAT_fat_isValidCluster(partition, dirCluster)) {
|
||||||
// No space left on disc for the cluster
|
// No space left on disc for the cluster
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOSPC;
|
r->_errno = ENOSPC;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -446,7 +405,7 @@ int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) {
|
|||||||
|
|
||||||
// Write the new directory's entry to it's parent
|
// Write the new directory's entry to it's parent
|
||||||
if (!_FAT_directory_addEntry (partition, &dirEntry, parentCluster)) {
|
if (!_FAT_directory_addEntry (partition, &dirEntry, parentCluster)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOSPC;
|
r->_errno = ENOSPC;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -475,30 +434,29 @@ int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) {
|
|||||||
|
|
||||||
// Flush any sectors in the disc cache
|
// Flush any sectors in the disc cache
|
||||||
if (!_FAT_cache_flush(partition->cache)) {
|
if (!_FAT_cache_flush(partition->cache)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EIO;
|
r->_errno = EIO;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
|
int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
|
||||||
{
|
{
|
||||||
PARTITION* partition = NULL;
|
PARTITION* partition = NULL;
|
||||||
u32 freeClusterCount;
|
unsigned int freeClusterCount;
|
||||||
|
|
||||||
// Get the partition of the requested path
|
// Get the partition of the requested path
|
||||||
_FAT_lock();
|
|
||||||
partition = _FAT_partition_getPartitionFromPath (path);
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
|
||||||
if (partition == NULL) {
|
if (partition == NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = ENODEV;
|
r->_errno = ENODEV;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
freeClusterCount = _FAT_fat_freeClusterCount (partition);
|
freeClusterCount = _FAT_fat_freeClusterCount (partition);
|
||||||
|
|
||||||
// FAT clusters = POSIX blocks
|
// FAT clusters = POSIX blocks
|
||||||
@ -523,7 +481,7 @@ int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
|
|||||||
// Maximum filename length.
|
// Maximum filename length.
|
||||||
buf->f_namemax = MAX_FILENAME_LENGTH;
|
buf->f_namemax = MAX_FILENAME_LENGTH;
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,11 +490,8 @@ DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path)
|
|||||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||||
bool fileExists;
|
bool fileExists;
|
||||||
|
|
||||||
_FAT_lock();
|
|
||||||
state->partition = _FAT_partition_getPartitionFromPath (path);
|
state->partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
|
||||||
if (state->partition == NULL) {
|
if (state->partition == NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = ENODEV;
|
r->_errno = ENODEV;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -546,22 +501,24 @@ DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path)
|
|||||||
path = strchr (path, ':') + 1;
|
path = strchr (path, ':') + 1;
|
||||||
}
|
}
|
||||||
if (strchr (path, ':') != NULL) {
|
if (strchr (path, ':') != NULL) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&state->partition->lock);
|
||||||
|
|
||||||
// Get the start cluster of the directory
|
// Get the start cluster of the directory
|
||||||
fileExists = _FAT_directory_entryFromPath (state->partition, &dirEntry, path, NULL);
|
fileExists = _FAT_directory_entryFromPath (state->partition, &dirEntry, path, NULL);
|
||||||
|
|
||||||
if (!fileExists) {
|
if (!fileExists) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&state->partition->lock);
|
||||||
r->_errno = ENOENT;
|
r->_errno = ENOENT;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure it is a directory
|
// Make sure it is a directory
|
||||||
if (! _FAT_directory_isDirectory (&dirEntry)) {
|
if (! _FAT_directory_isDirectory (&dirEntry)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&state->partition->lock);
|
||||||
r->_errno = ENOTDIR;
|
r->_errno = ENOTDIR;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -575,17 +532,18 @@ DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path)
|
|||||||
|
|
||||||
// We are now using this entry
|
// We are now using this entry
|
||||||
state->inUse = true;
|
state->inUse = true;
|
||||||
_FAT_unlock();
|
_FAT_unlock(&state->partition->lock);
|
||||||
return (DIR_ITER*) state;
|
return (DIR_ITER*) state;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState) {
|
int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState) {
|
||||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||||
|
|
||||||
|
_FAT_lock(&state->partition->lock);
|
||||||
|
|
||||||
// Make sure we are still using this entry
|
// Make sure we are still using this entry
|
||||||
_FAT_lock();
|
|
||||||
if (!state->inUse) {
|
if (!state->inUse) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&state->partition->lock);
|
||||||
r->_errno = EBADF;
|
r->_errno = EBADF;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -594,24 +552,25 @@ int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState) {
|
|||||||
state->validEntry =
|
state->validEntry =
|
||||||
_FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
|
_FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&state->partition->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
|
int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
|
||||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||||
|
|
||||||
|
_FAT_lock(&state->partition->lock);
|
||||||
|
|
||||||
// Make sure we are still using this entry
|
// Make sure we are still using this entry
|
||||||
_FAT_lock();
|
|
||||||
if (!state->inUse) {
|
if (!state->inUse) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&state->partition->lock);
|
||||||
r->_errno = EBADF;
|
r->_errno = EBADF;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure there is another file to report on
|
// Make sure there is another file to report on
|
||||||
if (! state->validEntry) {
|
if (! state->validEntry) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&state->partition->lock);
|
||||||
r->_errno = ENOENT;
|
r->_errno = ENOENT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -627,7 +586,7 @@ int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct
|
|||||||
state->validEntry =
|
state->validEntry =
|
||||||
_FAT_directory_getNextEntry (state->partition, &(state->currentEntry));
|
_FAT_directory_getNextEntry (state->partition, &(state->currentEntry));
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&state->partition->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -635,9 +594,9 @@ int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState) {
|
|||||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||||
|
|
||||||
// We are no longer using this entry
|
// We are no longer using this entry
|
||||||
_FAT_lock();
|
_FAT_lock(&state->partition->lock);
|
||||||
state->inUse = false;
|
state->inUse = false;
|
||||||
_FAT_unlock();
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -26,19 +26,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-08-13 - Chishm
|
|
||||||
* Moved all externally visible directory related functions to fatdir
|
|
||||||
* Added _FAT_mkdir_r
|
|
||||||
|
|
||||||
2006-08-14 - Chishm
|
|
||||||
* Added directory iterator functions
|
|
||||||
|
|
||||||
2007-01-10 - Chishm
|
|
||||||
* Updated directory iterator functions for DevkitPro r20
|
|
||||||
|
|
||||||
2007-10-25 - Chishm
|
|
||||||
* Added statvfs functionality
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -55,7 +42,7 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
PARTITION* partition;
|
PARTITION* partition;
|
||||||
DIR_ENTRY currentEntry;
|
DIR_ENTRY currentEntry;
|
||||||
u32 startCluster;
|
uint32_t startCluster;
|
||||||
bool inUse;
|
bool inUse;
|
||||||
bool validEntry;
|
bool validEntry;
|
||||||
} DIR_STATE_STRUCT;
|
} DIR_STATE_STRUCT;
|
||||||
|
421
source/fatfile.c
421
source/fatfile.c
@ -26,42 +26,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-07-17 - Chishm
|
|
||||||
* Made all path inputs const char*
|
|
||||||
* Added _FAT_rename_r
|
|
||||||
|
|
||||||
2006-08-02 - Chishm
|
|
||||||
* Fixed _FAT_seek_r
|
|
||||||
|
|
||||||
2006-08-13 - Chishm
|
|
||||||
* Moved all externally visible directory related functions to fatdir
|
|
||||||
|
|
||||||
2007-02-11 - Chishm
|
|
||||||
* Propagate disc errors up to the user app
|
|
||||||
|
|
||||||
2007-02-25 - Chishm
|
|
||||||
* Fixed seek to the end of a file bug
|
|
||||||
|
|
||||||
2007-04-12 - Chishm
|
|
||||||
* Fixed seek to end of file when reading
|
|
||||||
|
|
||||||
2007-11-04 - Chishm
|
|
||||||
* file_extend_r renamed to _FAT_file_extend_r
|
|
||||||
* A cluster is only allocated for a file when data is written, instead of when the file is opened
|
|
||||||
|
|
||||||
2008-05-12 - Chishm
|
|
||||||
* Modified WinterMute's seek then write fix for elegance
|
|
||||||
* Removed resetPosition
|
|
||||||
|
|
||||||
2008-05-12 - WinterMute
|
|
||||||
* Modified Chishm's elegant fix to reset the read/write positions
|
|
||||||
|
|
||||||
2008-11-12 - Chishm
|
|
||||||
* Only set append position when file is opened for append, as suggested by rodries. Improves open speed for large files.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -84,7 +48,7 @@ int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags
|
|||||||
bool fileExists;
|
bool fileExists;
|
||||||
DIR_ENTRY dirEntry;
|
DIR_ENTRY dirEntry;
|
||||||
const char* pathEnd;
|
const char* pathEnd;
|
||||||
u32 dirCluster;
|
uint32_t dirCluster;
|
||||||
FILE_STRUCT* file = (FILE_STRUCT*) fileStruct;
|
FILE_STRUCT* file = (FILE_STRUCT*) fileStruct;
|
||||||
partition = _FAT_partition_getPartitionFromPath (path);
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
|
||||||
@ -130,19 +94,19 @@ int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search for the file on the disc
|
// Search for the file on the disc
|
||||||
_FAT_lock();
|
_FAT_lock(&partition->lock);
|
||||||
fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
|
fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
|
||||||
|
|
||||||
// The file shouldn't exist if we are trying to create it
|
// The file shouldn't exist if we are trying to create it
|
||||||
if ((flags & O_CREAT) && (flags & O_EXCL) && fileExists) {
|
if ((flags & O_CREAT) && (flags & O_EXCL) && fileExists) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EEXIST;
|
r->_errno = EEXIST;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It should not be a directory if we're openning a file,
|
// It should not be a directory if we're openning a file,
|
||||||
if (fileExists && _FAT_directory_isDirectory(&dirEntry)) {
|
if (fileExists && _FAT_directory_isDirectory(&dirEntry)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EISDIR;
|
r->_errno = EISDIR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -152,7 +116,7 @@ int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags
|
|||||||
if (flags & O_CREAT) {
|
if (flags & O_CREAT) {
|
||||||
if (partition->readOnly) {
|
if (partition->readOnly) {
|
||||||
// We can't write to a read-only partition
|
// We can't write to a read-only partition
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EROFS;
|
r->_errno = EROFS;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -168,7 +132,7 @@ int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags
|
|||||||
// Recycling dirEntry, since it needs to be recreated anyway
|
// Recycling dirEntry, since it needs to be recreated anyway
|
||||||
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
|
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
|
||||||
!_FAT_directory_isDirectory(&dirEntry)) {
|
!_FAT_directory_isDirectory(&dirEntry)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOTDIR;
|
r->_errno = ENOTDIR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -186,13 +150,13 @@ int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags
|
|||||||
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC());
|
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC());
|
||||||
|
|
||||||
if (!_FAT_directory_addEntry (partition, &dirEntry, dirCluster)) {
|
if (!_FAT_directory_addEntry (partition, &dirEntry, dirCluster)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOSPC;
|
r->_errno = ENOSPC;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// file doesn't exist, and we aren't creating it
|
// file doesn't exist, and we aren't creating it
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOENT;
|
r->_errno = ENOENT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -210,7 +174,7 @@ int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags
|
|||||||
|
|
||||||
// Make sure we aren't trying to write to a read-only file
|
// Make sure we aren't trying to write to a read-only file
|
||||||
if (file->write && !_FAT_directory_isWritable(&dirEntry)) {
|
if (file->write && !_FAT_directory_isWritable(&dirEntry)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EROFS;
|
r->_errno = EROFS;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -257,25 +221,39 @@ int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags
|
|||||||
file->appendPosition = file->rwPosition;
|
file->appendPosition = file->rwPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file->modified = false;
|
||||||
|
|
||||||
file->inUse = true;
|
file->inUse = true;
|
||||||
|
|
||||||
|
// Insert this file into the double-linked list of open files
|
||||||
partition->openFileCount += 1;
|
partition->openFileCount += 1;
|
||||||
_FAT_unlock();
|
if (partition->firstOpenFile) {
|
||||||
|
file->nextOpenFile = partition->firstOpenFile;
|
||||||
|
partition->firstOpenFile->prevOpenFile = file;
|
||||||
|
} else {
|
||||||
|
file->nextOpenFile = NULL;
|
||||||
|
}
|
||||||
|
file->prevOpenFile = NULL;
|
||||||
|
partition->firstOpenFile = file;
|
||||||
|
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
|
||||||
return (int) file;
|
return (int) file;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _FAT_close_r (struct _reent *r, int fd) {
|
/*
|
||||||
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
Synchronizes the file data to disc.
|
||||||
u8 dirEntryData[DIR_ENTRY_DATA_SIZE];
|
Does no locking of its own -- lock the partition before calling.
|
||||||
|
Returns 0 on success, an error code on failure.
|
||||||
|
*/
|
||||||
|
int _FAT_syncToDisc (FILE_STRUCT* file) {
|
||||||
|
uint8_t dirEntryData[DIR_ENTRY_DATA_SIZE];
|
||||||
|
|
||||||
_FAT_lock();
|
if (!file || !file->inUse) {
|
||||||
if (!file->inUse) {
|
return EBADF;
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EBADF;
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
if (file->write) {
|
|
||||||
|
if (file->write && file->modified) {
|
||||||
// Load the old entry
|
// Load the old entry
|
||||||
_FAT_cache_readPartialSector (file->partition->cache, dirEntryData,
|
_FAT_cache_readPartialSector (file->partition->cache, dirEntryData,
|
||||||
_FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
|
_FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
|
||||||
@ -296,6 +274,9 @@ int _FAT_close_r (struct _reent *r, int fd) {
|
|||||||
// Access date
|
// Access date
|
||||||
u16_to_u8array (dirEntryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC());
|
u16_to_u8array (dirEntryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC());
|
||||||
|
|
||||||
|
// Set archive attribute
|
||||||
|
dirEntryData[DIR_ENTRY_attributes] |= ATTRIB_ARCH;
|
||||||
|
|
||||||
// Write the new entry
|
// Write the new entry
|
||||||
_FAT_cache_writePartialSector (file->partition->cache, dirEntryData,
|
_FAT_cache_writePartialSector (file->partition->cache, dirEntryData,
|
||||||
_FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
|
_FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
|
||||||
@ -303,47 +284,73 @@ int _FAT_close_r (struct _reent *r, int fd) {
|
|||||||
|
|
||||||
// Flush any sectors in the disc cache
|
// Flush any sectors in the disc cache
|
||||||
if (!_FAT_cache_flush(file->partition->cache)) {
|
if (!_FAT_cache_flush(file->partition->cache)) {
|
||||||
_FAT_unlock();
|
return EIO;
|
||||||
r->_errno = EIO;
|
}
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
file->modified = false;
|
||||||
|
|
||||||
file->inUse = false;
|
|
||||||
file->partition->openFileCount -= 1;
|
|
||||||
_FAT_unlock();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _FAT_read_r (struct _reent *r, int fd, char *ptr, int len) {
|
|
||||||
|
int _FAT_close_r (struct _reent *r, int fd) {
|
||||||
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
PARTITION* partition;
|
if (!file->inUse) {
|
||||||
CACHE* cache;
|
|
||||||
|
|
||||||
FILE_POSITION position;
|
|
||||||
u32 tempNextCluster;
|
|
||||||
|
|
||||||
int tempVar;
|
|
||||||
|
|
||||||
u32 remain;
|
|
||||||
|
|
||||||
bool flagNoError = true;
|
|
||||||
|
|
||||||
// Make sure we can actually read from the file
|
|
||||||
_FAT_lock();
|
|
||||||
if ((file == NULL) || !file->inUse || !file->read) {
|
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EBADF;
|
r->_errno = EBADF;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&file->partition->lock);
|
||||||
|
|
||||||
|
ret = _FAT_syncToDisc (file);
|
||||||
|
if (ret != 0) {
|
||||||
|
r->_errno = ret;
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
file->inUse = false;
|
||||||
|
|
||||||
|
// Remove this file from the double-linked list of open files
|
||||||
|
file->partition->openFileCount -= 1;
|
||||||
|
if (file->nextOpenFile) {
|
||||||
|
file->nextOpenFile->prevOpenFile = file->prevOpenFile;
|
||||||
|
}
|
||||||
|
if (file->prevOpenFile) {
|
||||||
|
file->prevOpenFile->nextOpenFile = file->nextOpenFile;
|
||||||
|
} else {
|
||||||
|
file->partition->firstOpenFile = file->nextOpenFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_unlock(&file->partition->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) {
|
||||||
|
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
||||||
|
PARTITION* partition;
|
||||||
|
CACHE* cache;
|
||||||
|
FILE_POSITION position;
|
||||||
|
uint32_t tempNextCluster;
|
||||||
|
unsigned int tempVar;
|
||||||
|
size_t remain;
|
||||||
|
bool flagNoError = true;
|
||||||
|
|
||||||
|
// Make sure we can actually read from the file
|
||||||
|
if ((file == NULL) || !file->inUse || !file->read) {
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
partition = file->partition;
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
// Don't try to read if the read pointer is past the end of file
|
// Don't try to read if the read pointer is past the end of file
|
||||||
if (file->currentPosition >= file->filesize || file->startCluster == CLUSTER_FREE) {
|
if (file->currentPosition >= file->filesize || file->startCluster == CLUSTER_FREE) {
|
||||||
r->_errno = EOVERFLOW;
|
r->_errno = EOVERFLOW;
|
||||||
_FAT_unlock();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,15 +362,11 @@ int _FAT_read_r (struct _reent *r, int fd, char *ptr, int len) {
|
|||||||
|
|
||||||
// Short circuit cases where len is 0 (or less)
|
// Short circuit cases where len is 0 (or less)
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
_FAT_unlock();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
remain = len;
|
remain = len;
|
||||||
|
|
||||||
position = file->rwPosition;
|
position = file->rwPosition;
|
||||||
|
|
||||||
partition = file->partition;
|
|
||||||
cache = file->partition->cache;
|
cache = file->partition->cache;
|
||||||
|
|
||||||
// Align to sector
|
// Align to sector
|
||||||
@ -480,7 +483,7 @@ int _FAT_read_r (struct _reent *r, int fd, char *ptr, int len) {
|
|||||||
file->rwPosition = position;
|
file->rwPosition = position;
|
||||||
file->currentPosition += len;
|
file->currentPosition += len;
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,14 +493,10 @@ Extend a file so that the size is the same as the rwPosition
|
|||||||
static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) {
|
static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) {
|
||||||
PARTITION* partition = file->partition;
|
PARTITION* partition = file->partition;
|
||||||
CACHE* cache = file->partition->cache;
|
CACHE* cache = file->partition->cache;
|
||||||
|
|
||||||
FILE_POSITION position;
|
FILE_POSITION position;
|
||||||
|
uint8_t zeroBuffer [BYTES_PER_READ] = {0};
|
||||||
u32 remain;
|
uint32_t remain;
|
||||||
|
uint32_t tempNextCluster;
|
||||||
u8 zeroBuffer [BYTES_PER_READ] = {0};
|
|
||||||
|
|
||||||
u32 tempNextCluster;
|
|
||||||
|
|
||||||
position.byte = file->filesize % BYTES_PER_READ;
|
position.byte = file->filesize % BYTES_PER_READ;
|
||||||
position.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ;
|
position.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ;
|
||||||
@ -507,20 +506,20 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) {
|
|||||||
|
|
||||||
remain = file->currentPosition - file->filesize;
|
remain = file->currentPosition - file->filesize;
|
||||||
|
|
||||||
if ((remain > 0) && (file->filesize > 0) && (position.sector == 0)) {
|
if ((remain > 0) && (file->filesize > 0) && (position.sector == 0) && (position.byte == 0)) {
|
||||||
// Get a new cluster on the edge of a cluster boundary
|
// Get a new cluster on the edge of a cluster boundary
|
||||||
tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
|
tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
|
||||||
if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
|
if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
|
||||||
// Couldn't get a cluster, so abort
|
// Couldn't get a cluster, so abort
|
||||||
r->_errno = ENOSPC;
|
r->_errno = ENOSPC;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
position.cluster = tempNextCluster;
|
|
||||||
}
|
}
|
||||||
|
position.cluster = tempNextCluster;
|
||||||
|
position.sector = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only need to clear to the end of the sector
|
|
||||||
if (remain + position.byte < BYTES_PER_READ) {
|
if (remain + position.byte < BYTES_PER_READ) {
|
||||||
|
// Only need to clear to the end of the sector
|
||||||
_FAT_cache_writePartialSector (cache, zeroBuffer,
|
_FAT_cache_writePartialSector (cache, zeroBuffer,
|
||||||
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, remain);
|
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, remain);
|
||||||
position.byte += remain;
|
position.byte += remain;
|
||||||
@ -543,9 +542,8 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) {
|
|||||||
// Couldn't get a cluster, so abort
|
// Couldn't get a cluster, so abort
|
||||||
r->_errno = ENOSPC;
|
r->_errno = ENOSPC;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
position.cluster = tempNextCluster;
|
|
||||||
}
|
}
|
||||||
|
position.cluster = tempNextCluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
_FAT_disc_writeSectors (partition->disc,
|
_FAT_disc_writeSectors (partition->disc,
|
||||||
@ -566,9 +564,8 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) {
|
|||||||
// Couldn't get a cluster, so abort
|
// Couldn't get a cluster, so abort
|
||||||
r->_errno = ENOSPC;
|
r->_errno = ENOSPC;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
position.cluster = tempNextCluster;
|
|
||||||
}
|
}
|
||||||
|
position.cluster = tempNextCluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remain > 0) {
|
if (remain > 0) {
|
||||||
@ -584,46 +581,45 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int _FAT_write_r (struct _reent *r,int fd, const char *ptr, int len) {
|
ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) {
|
||||||
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
||||||
|
|
||||||
PARTITION* partition;
|
PARTITION* partition;
|
||||||
CACHE* cache;
|
CACHE* cache;
|
||||||
|
|
||||||
FILE_POSITION position;
|
FILE_POSITION position;
|
||||||
u32 tempNextCluster;
|
uint32_t tempNextCluster;
|
||||||
|
unsigned int tempVar;
|
||||||
int tempVar;
|
size_t remain;
|
||||||
|
|
||||||
u32 remain;
|
|
||||||
|
|
||||||
bool flagNoError = true;
|
bool flagNoError = true;
|
||||||
bool flagAppending = false;
|
bool flagAppending = false;
|
||||||
|
|
||||||
// Make sure we can actually write to the file
|
// Make sure we can actually write to the file
|
||||||
_FAT_lock();
|
|
||||||
if ((file == NULL) || !file->inUse || !file->write) {
|
if ((file == NULL) || !file->inUse || !file->write) {
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EBADF;
|
r->_errno = EBADF;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short circuit cases where len is 0 (or less)
|
|
||||||
if (len <= 0) {
|
|
||||||
_FAT_unlock();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
partition = file->partition;
|
partition = file->partition;
|
||||||
cache = file->partition->cache;
|
cache = file->partition->cache;
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
// Only write up to the maximum file size, taking into account wrap-around of ints
|
||||||
|
if (remain + file->filesize > FILE_MAX_SIZE || len + file->filesize < file->filesize) {
|
||||||
|
len = FILE_MAX_SIZE - file->filesize;
|
||||||
|
}
|
||||||
remain = len;
|
remain = len;
|
||||||
|
|
||||||
|
// Short circuit cases where len is 0 (or less)
|
||||||
|
if (len <= 0) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Get a new cluster for the start of the file if required
|
// Get a new cluster for the start of the file if required
|
||||||
if (file->startCluster == CLUSTER_FREE) {
|
if (file->startCluster == CLUSTER_FREE) {
|
||||||
tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE);
|
tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE);
|
||||||
if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
|
if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
|
||||||
// Couldn't get a cluster, so abort immediately
|
// Couldn't get a cluster, so abort immediately
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = ENOSPC;
|
r->_errno = ENOSPC;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -646,7 +642,7 @@ int _FAT_write_r (struct _reent *r,int fd, const char *ptr, int len) {
|
|||||||
// If the write pointer is past the end of the file, extend the file to that size
|
// If the write pointer is past the end of the file, extend the file to that size
|
||||||
if (file->currentPosition > file->filesize) {
|
if (file->currentPosition > file->filesize) {
|
||||||
if (!_FAT_file_extend_r (r, file)) {
|
if (!_FAT_file_extend_r (r, file)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -796,10 +792,11 @@ int _FAT_write_r (struct _reent *r,int fd, const char *ptr, int len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Amount read is the originally requested amount minus stuff remaining
|
// Amount written is the originally requested amount minus stuff remaining
|
||||||
len = len - remain;
|
len = len - remain;
|
||||||
|
|
||||||
// Update file information
|
// Update file information
|
||||||
|
file->modified = true;
|
||||||
if (file->append) {
|
if (file->append) {
|
||||||
// Appending doesn't affect the read pointer
|
// Appending doesn't affect the read pointer
|
||||||
file->appendPosition = position;
|
file->appendPosition = position;
|
||||||
@ -813,59 +810,61 @@ int _FAT_write_r (struct _reent *r,int fd, const char *ptr, int len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int _FAT_seek_r (struct _reent *r, int fd, int pos, int dir) {
|
off_t _FAT_seek_r (struct _reent *r, int fd, off_t pos, int dir) {
|
||||||
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
||||||
|
|
||||||
PARTITION* partition;
|
PARTITION* partition;
|
||||||
|
uint32_t cluster, nextCluster;
|
||||||
u32 cluster, nextCluster;
|
|
||||||
int clusCount;
|
int clusCount;
|
||||||
int position;
|
off_t newPosition;
|
||||||
|
uint32_t position;
|
||||||
|
|
||||||
_FAT_lock();
|
|
||||||
if ((file == NULL) || (file->inUse == false)) {
|
if ((file == NULL) || (file->inUse == false)) {
|
||||||
// invalid file
|
// invalid file
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EBADF;
|
r->_errno = EBADF;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
partition = file->partition;
|
partition = file->partition;
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case SEEK_SET:
|
case SEEK_SET:
|
||||||
position = pos;
|
newPosition = pos;
|
||||||
break;
|
break;
|
||||||
case SEEK_CUR:
|
case SEEK_CUR:
|
||||||
position = file->currentPosition + pos;
|
newPosition = (off_t)file->currentPosition + pos;
|
||||||
break;
|
break;
|
||||||
case SEEK_END:
|
case SEEK_END:
|
||||||
position = file->filesize + pos;
|
newPosition = (off_t)file->filesize + pos;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pos > 0) && (position < 0)) {
|
if ((pos > 0) && (newPosition < 0)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EOVERFLOW;
|
r->_errno = EOVERFLOW;
|
||||||
_FAT_unlock();
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (position < 0) {
|
// newPosition can only be larger than the FILE_MAX_SIZE on platforms where
|
||||||
_FAT_unlock();
|
// off_t is larger than 32 bits.
|
||||||
|
if (newPosition < 0 || ((sizeof(newPosition) > 4) && newPosition > (off_t)FILE_MAX_SIZE)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
position = (uint32_t)newPosition;
|
||||||
|
|
||||||
// Only change the read/write position if it is within the bounds of the current filesize,
|
// Only change the read/write position if it is within the bounds of the current filesize,
|
||||||
// or at the very edge of the file
|
// or at the very edge of the file
|
||||||
if (position <= file->filesize && file->startCluster != CLUSTER_FREE) {
|
if (position <= file->filesize && file->startCluster != CLUSTER_FREE) {
|
||||||
@ -897,7 +896,7 @@ int _FAT_seek_r (struct _reent *r, int fd, int pos, int dir) {
|
|||||||
file->rwPosition.sector = partition->sectorsPerCluster;
|
file->rwPosition.sector = partition->sectorsPerCluster;
|
||||||
file->rwPosition.byte = 0;
|
file->rwPosition.byte = 0;
|
||||||
} else {
|
} else {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EINVAL;
|
r->_errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -909,7 +908,7 @@ int _FAT_seek_r (struct _reent *r, int fd, int pos, int dir) {
|
|||||||
// Save position
|
// Save position
|
||||||
file->currentPosition = position;
|
file->currentPosition = position;
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -917,27 +916,24 @@ int _FAT_seek_r (struct _reent *r, int fd, int pos, int dir) {
|
|||||||
|
|
||||||
int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st) {
|
int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st) {
|
||||||
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
||||||
|
|
||||||
PARTITION* partition;
|
PARTITION* partition;
|
||||||
|
|
||||||
DIR_ENTRY fileEntry;
|
DIR_ENTRY fileEntry;
|
||||||
|
|
||||||
_FAT_lock();
|
|
||||||
if ((file == NULL) || (file->inUse == false)) {
|
if ((file == NULL) || (file->inUse == false)) {
|
||||||
// invalid file
|
// invalid file
|
||||||
_FAT_unlock();
|
|
||||||
r->_errno = EBADF;
|
r->_errno = EBADF;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
partition = file->partition;
|
partition = file->partition;
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
// Get the file's entry data
|
// Get the file's entry data
|
||||||
fileEntry.dataStart = file->dirEntryStart;
|
fileEntry.dataStart = file->dirEntryStart;
|
||||||
fileEntry.dataEnd = file->dirEntryEnd;
|
fileEntry.dataEnd = file->dirEntryEnd;
|
||||||
|
|
||||||
if (!_FAT_directory_entryFromPosition (partition, &fileEntry)) {
|
if (!_FAT_directory_entryFromPosition (partition, &fileEntry)) {
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
r->_errno = EIO;
|
r->_errno = EIO;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -949,7 +945,142 @@ int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st) {
|
|||||||
st->st_ino = (ino_t)(file->startCluster); // The file serial number is the start cluster
|
st->st_ino = (ino_t)(file->startCluster); // The file serial number is the start cluster
|
||||||
st->st_size = file->filesize; // File size
|
st->st_size = file->filesize; // File size
|
||||||
|
|
||||||
_FAT_unlock();
|
_FAT_unlock(&partition->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len) {
|
||||||
|
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
||||||
|
PARTITION* partition;
|
||||||
|
int ret=0;
|
||||||
|
uint32_t newSize = (uint32_t)len;
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
// Trying to truncate to a negative size
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sizeof(len) > 4) && len > (off_t)FILE_MAX_SIZE) {
|
||||||
|
// Trying to extend the file beyond what FAT supports
|
||||||
|
r->_errno = EFBIG;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file || !file->inUse) {
|
||||||
|
// invalid file
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file->write) {
|
||||||
|
// Read-only file
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
partition = file->partition;
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
if (newSize > file->filesize) {
|
||||||
|
// Expanding the file
|
||||||
|
FILE_POSITION savedPosition;
|
||||||
|
uint32_t savedOffset;
|
||||||
|
// Get a new cluster for the start of the file if required
|
||||||
|
if (file->startCluster == CLUSTER_FREE) {
|
||||||
|
uint32_t tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE);
|
||||||
|
if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
|
||||||
|
// Couldn't get a cluster, so abort immediately
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOSPC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
file->startCluster = tempNextCluster;
|
||||||
|
|
||||||
|
file->rwPosition.cluster = file->startCluster;
|
||||||
|
file->rwPosition.sector = 0;
|
||||||
|
file->rwPosition.byte = 0;
|
||||||
|
}
|
||||||
|
// Save the read/write pointer
|
||||||
|
savedPosition = file->rwPosition;
|
||||||
|
savedOffset = file->currentPosition;
|
||||||
|
// Set the position to the new size
|
||||||
|
file->currentPosition = newSize;
|
||||||
|
// Extend the file to the new position
|
||||||
|
if (!_FAT_file_extend_r (r, file)) {
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
// Set the append position to the new rwPointer
|
||||||
|
if (file->append) {
|
||||||
|
file->appendPosition = file->rwPosition;
|
||||||
|
}
|
||||||
|
// Restore the old rwPointer;
|
||||||
|
file->rwPosition = savedPosition;
|
||||||
|
file->currentPosition = savedOffset;
|
||||||
|
} else if (newSize < file->filesize){
|
||||||
|
// Shrinking the file
|
||||||
|
if (len == 0) {
|
||||||
|
// Cutting the file down to nothing, clear all clusters used
|
||||||
|
_FAT_fat_clearLinks (partition, file->startCluster);
|
||||||
|
file->startCluster = CLUSTER_FREE;
|
||||||
|
|
||||||
|
file->appendPosition.cluster = CLUSTER_FREE;
|
||||||
|
file->appendPosition.sector = 0;
|
||||||
|
file->appendPosition.byte = 0;
|
||||||
|
} else {
|
||||||
|
// Trimming the file down to the required size
|
||||||
|
unsigned int chainLength;
|
||||||
|
uint32_t lastCluster;
|
||||||
|
|
||||||
|
// Drop the unneeded end of the cluster chain.
|
||||||
|
// If the end falls on a cluster boundary, drop that cluster too,
|
||||||
|
// then set a flag to allocate a cluster as needed
|
||||||
|
chainLength = ((newSize-1) / partition->bytesPerCluster) + 1;
|
||||||
|
lastCluster = _FAT_fat_trimChain (partition, file->startCluster, chainLength);
|
||||||
|
|
||||||
|
if (file->append) {
|
||||||
|
file->appendPosition.byte = newSize % BYTES_PER_READ;
|
||||||
|
// Does the end of the file fall on the edge of a cluster?
|
||||||
|
if (newSize % partition->bytesPerCluster == 0) {
|
||||||
|
// Set a flag to allocate a new cluster
|
||||||
|
file->appendPosition.sector = partition->sectorsPerCluster;
|
||||||
|
} else {
|
||||||
|
file->appendPosition.sector = (newSize % partition->bytesPerCluster) / BYTES_PER_READ;
|
||||||
|
}
|
||||||
|
file->appendPosition.cluster = lastCluster;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Truncating to same length, so don't do anything
|
||||||
|
}
|
||||||
|
|
||||||
|
file->filesize = newSize;
|
||||||
|
file->modified = true;
|
||||||
|
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_fsync_r (struct _reent *r, int fd) {
|
||||||
|
FILE_STRUCT* file = (FILE_STRUCT*) fd;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!file->inUse) {
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&file->partition->lock);
|
||||||
|
|
||||||
|
ret = _FAT_syncToDisc (file);
|
||||||
|
if (ret != 0) {
|
||||||
|
r->_errno = ret;
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_unlock(&file->partition->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,19 +26,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-07-17 - Chishm
|
|
||||||
* Made all path inputs const char*
|
|
||||||
* Added _FAT_rename_r
|
|
||||||
|
|
||||||
2006-07-24 - Chishm
|
|
||||||
* Removed padding workaround from FILE_STRUCT
|
|
||||||
|
|
||||||
2006-08-13 - Chishm
|
|
||||||
* Moved all externally visible directory related functions to fatdir
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -52,36 +39,45 @@
|
|||||||
#include "partition.h"
|
#include "partition.h"
|
||||||
#include "directory.h"
|
#include "directory.h"
|
||||||
|
|
||||||
|
#define FILE_MAX_SIZE ((uint32_t)0xFFFFFFFF) // 4GiB - 1B
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 cluster;
|
u32 cluster;
|
||||||
u32 sector;
|
sec_t sector;
|
||||||
s32 byte;
|
s32 byte;
|
||||||
} FILE_POSITION;
|
} FILE_POSITION;
|
||||||
|
|
||||||
typedef struct {
|
struct _FILE_STRUCT;
|
||||||
u32 filesize;
|
|
||||||
u32 startCluster;
|
struct _FILE_STRUCT {
|
||||||
u32 currentPosition;
|
uint32_t filesize;
|
||||||
|
uint32_t startCluster;
|
||||||
|
uint32_t currentPosition;
|
||||||
FILE_POSITION rwPosition;
|
FILE_POSITION rwPosition;
|
||||||
FILE_POSITION appendPosition;
|
FILE_POSITION appendPosition;
|
||||||
bool read;
|
bool read;
|
||||||
bool write;
|
bool write;
|
||||||
bool append;
|
bool append;
|
||||||
bool inUse;
|
bool inUse;
|
||||||
PARTITION* partition;
|
bool modified;
|
||||||
DIR_ENTRY_POSITION dirEntryStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
DIR_ENTRY_POSITION dirEntryStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
||||||
DIR_ENTRY_POSITION dirEntryEnd; // Always points to the file's alias entry
|
DIR_ENTRY_POSITION dirEntryEnd; // Always points to the file's alias entry
|
||||||
} FILE_STRUCT;
|
PARTITION* partition;
|
||||||
|
struct _FILE_STRUCT* prevOpenFile; // The previous entry in a double-linked list of open files
|
||||||
|
struct _FILE_STRUCT* nextOpenFile; // The next entry in a double-linked list of open files
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _FILE_STRUCT FILE_STRUCT;
|
||||||
|
|
||||||
extern int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
extern int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
||||||
|
|
||||||
extern int _FAT_close_r (struct _reent *r, int fd);
|
extern int _FAT_close_r (struct _reent *r, int fd);
|
||||||
|
|
||||||
extern int _FAT_write_r (struct _reent *r,int fd, const char *ptr, int len);
|
extern ssize_t _FAT_write_r (struct _reent *r,int fd, const char *ptr, size_t len);
|
||||||
|
|
||||||
extern int _FAT_read_r (struct _reent *r, int fd, char *ptr, int len);
|
extern ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len);
|
||||||
|
|
||||||
extern int _FAT_seek_r (struct _reent *r, int fd,int pos, int dir);
|
extern off_t _FAT_seek_r (struct _reent *r, int fd, off_t pos, int dir);
|
||||||
|
|
||||||
extern int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st);
|
extern int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st);
|
||||||
|
|
||||||
@ -95,4 +91,15 @@ extern int _FAT_chdir_r (struct _reent *r, const char *name);
|
|||||||
|
|
||||||
extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName);
|
extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName);
|
||||||
|
|
||||||
|
extern int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len);
|
||||||
|
|
||||||
|
extern int _FAT_fsync_r (struct _reent *r, int fd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Synchronizes the file data to disc.
|
||||||
|
Does no locking of its own -- lock the partition before calling.
|
||||||
|
Returns 0 on success, an error code on failure.
|
||||||
|
*/
|
||||||
|
extern int _FAT_syncToDisc (FILE_STRUCT* file);
|
||||||
|
|
||||||
#endif // _FATFILE_H
|
#endif // _FATFILE_H
|
||||||
|
@ -25,21 +25,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Made several fixes related to free clusters, thanks to Loopy
|
|
||||||
|
|
||||||
2006-10-01 - Chishm
|
|
||||||
* Added _FAT_fat_linkFreeClusterCleared to clear a cluster when it is allocated
|
|
||||||
|
|
||||||
2007-10-25 - Chishm
|
|
||||||
* Use CLUSTER_ERROR when an error occurs with the FAT, not CLUSTER_FREE
|
|
||||||
|
|
||||||
2007-11-04 - Chishm
|
|
||||||
* _FAT_fat_nextCluster returns CLUSTER_FREE when input cluster is CLUSTER_FREE
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -50,10 +35,10 @@
|
|||||||
/*
|
/*
|
||||||
Gets the cluster linked from input cluster
|
Gets the cluster linked from input cluster
|
||||||
*/
|
*/
|
||||||
u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster)
|
uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster)
|
||||||
{
|
{
|
||||||
u32 nextCluster = CLUSTER_FREE;
|
uint32_t nextCluster = CLUSTER_FREE;
|
||||||
u32 sector;
|
sec_t sector;
|
||||||
int offset;
|
int offset;
|
||||||
|
|
||||||
if (cluster == CLUSTER_FREE) {
|
if (cluster == CLUSTER_FREE) {
|
||||||
@ -67,6 +52,8 @@ u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case FS_FAT12:
|
case FS_FAT12:
|
||||||
|
{
|
||||||
|
u32 nextCluster_h;
|
||||||
sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
|
sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
|
||||||
offset = ((cluster * 3) / 2) % BYTES_PER_READ;
|
offset = ((cluster * 3) / 2) % BYTES_PER_READ;
|
||||||
|
|
||||||
@ -79,7 +66,7 @@ u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster)
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
sector++;
|
sector++;
|
||||||
}
|
}
|
||||||
u32 nextCluster_h = 0;
|
nextCluster_h = 0;
|
||||||
|
|
||||||
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster_h, sector, offset, sizeof(u8));
|
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster_h, sector, offset, sizeof(u8));
|
||||||
nextCluster |= (nextCluster_h << 8);
|
nextCluster |= (nextCluster_h << 8);
|
||||||
@ -96,15 +83,14 @@ u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster)
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case FS_FAT16:
|
case FS_FAT16:
|
||||||
sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
|
sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
|
||||||
offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
|
offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
|
||||||
|
|
||||||
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u16));
|
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u16));
|
||||||
|
|
||||||
if (nextCluster >= 0xFFF7)
|
if (nextCluster >= 0xFFF7) {
|
||||||
{
|
|
||||||
nextCluster = CLUSTER_EOF;
|
nextCluster = CLUSTER_EOF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -115,8 +101,7 @@ u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster)
|
|||||||
|
|
||||||
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u32));
|
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u32));
|
||||||
|
|
||||||
if (nextCluster >= 0x0FFFFFF7)
|
if (nextCluster >= 0x0FFFFFF7) {
|
||||||
{
|
|
||||||
nextCluster = CLUSTER_EOF;
|
nextCluster = CLUSTER_EOF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -133,10 +118,10 @@ u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster)
|
|||||||
writes value into the correct offset within a partition's FAT, based
|
writes value into the correct offset within a partition's FAT, based
|
||||||
on the cluster number.
|
on the cluster number.
|
||||||
*/
|
*/
|
||||||
static bool _FAT_fat_writeFatEntry (PARTITION* partition, u32 cluster, u32 value) {
|
static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint32_t value) {
|
||||||
u32 sector;
|
sec_t sector;
|
||||||
int offset;
|
int offset;
|
||||||
u32 oldValue;
|
uint32_t oldValue;
|
||||||
|
|
||||||
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
|
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
|
||||||
{
|
{
|
||||||
@ -218,10 +203,10 @@ to end of file, links the input cluster to it then returns the
|
|||||||
cluster number
|
cluster number
|
||||||
If an error occurs, return CLUSTER_ERROR
|
If an error occurs, return CLUSTER_ERROR
|
||||||
-----------------------------------------------------------------*/
|
-----------------------------------------------------------------*/
|
||||||
u32 _FAT_fat_linkFreeCluster(PARTITION* partition, u32 cluster) {
|
uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster) {
|
||||||
u32 firstFree;
|
uint32_t firstFree;
|
||||||
u32 curLink;
|
uint32_t curLink;
|
||||||
u32 lastCluster;
|
uint32_t lastCluster;
|
||||||
bool loopedAroundFAT = false;
|
bool loopedAroundFAT = false;
|
||||||
|
|
||||||
lastCluster = partition->fat.lastCluster;
|
lastCluster = partition->fat.lastCluster;
|
||||||
@ -278,10 +263,10 @@ to end of file, links the input cluster to it, clears the new
|
|||||||
cluster to 0 valued bytes, then returns the cluster number
|
cluster to 0 valued bytes, then returns the cluster number
|
||||||
If an error occurs, return CLUSTER_ERROR
|
If an error occurs, return CLUSTER_ERROR
|
||||||
-----------------------------------------------------------------*/
|
-----------------------------------------------------------------*/
|
||||||
u32 _FAT_fat_linkFreeClusterCleared (PARTITION* partition, u32 cluster) {
|
uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster) {
|
||||||
u32 newCluster;
|
uint32_t newCluster;
|
||||||
int i;
|
uint32_t i;
|
||||||
u8 emptySector[BYTES_PER_READ];
|
uint8_t emptySector[BYTES_PER_READ];
|
||||||
|
|
||||||
// Link the cluster
|
// Link the cluster
|
||||||
newCluster = _FAT_fat_linkFreeCluster(partition, cluster);
|
newCluster = _FAT_fat_linkFreeCluster(partition, cluster);
|
||||||
@ -306,8 +291,8 @@ u32 _FAT_fat_linkFreeClusterCleared (PARTITION* partition, u32 cluster) {
|
|||||||
_FAT_fat_clearLinks
|
_FAT_fat_clearLinks
|
||||||
frees any cluster used by a file
|
frees any cluster used by a file
|
||||||
-----------------------------------------------------------------*/
|
-----------------------------------------------------------------*/
|
||||||
bool _FAT_fat_clearLinks (PARTITION* partition, u32 cluster) {
|
bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster) {
|
||||||
u32 nextCluster;
|
uint32_t nextCluster;
|
||||||
|
|
||||||
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
|
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
|
||||||
return false;
|
return false;
|
||||||
@ -331,11 +316,48 @@ bool _FAT_fat_clearLinks (PARTITION* partition, u32 cluster) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
_FAT_fat_trimChain
|
||||||
|
Drop all clusters past the chainLength.
|
||||||
|
If chainLength is 0, all clusters are dropped.
|
||||||
|
If chainLength is 1, the first cluster is kept and the rest are
|
||||||
|
dropped, and so on.
|
||||||
|
Return the last cluster left in the chain.
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength) {
|
||||||
|
uint32_t nextCluster;
|
||||||
|
|
||||||
|
if (chainLength == 0) {
|
||||||
|
// Drop the entire chain
|
||||||
|
_FAT_fat_clearLinks (partition, startCluster);
|
||||||
|
return CLUSTER_FREE;
|
||||||
|
} else {
|
||||||
|
// Find the last cluster in the chain, and the one after it
|
||||||
|
chainLength--;
|
||||||
|
nextCluster = _FAT_fat_nextCluster (partition, startCluster);
|
||||||
|
while ((chainLength > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) {
|
||||||
|
chainLength--;
|
||||||
|
startCluster = nextCluster;
|
||||||
|
nextCluster = _FAT_fat_nextCluster (partition, startCluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop all clusters after the last in the chain
|
||||||
|
if (nextCluster != CLUSTER_FREE && nextCluster != CLUSTER_EOF) {
|
||||||
|
_FAT_fat_clearLinks (partition, nextCluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the last cluster in the chain as the end of the file
|
||||||
|
_FAT_fat_writeFatEntry (partition, startCluster, CLUSTER_EOF);
|
||||||
|
|
||||||
|
return startCluster;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
/*-----------------------------------------------------------------
|
||||||
_FAT_fat_lastCluster
|
_FAT_fat_lastCluster
|
||||||
Trace the cluster links until the last one is found
|
Trace the cluster links until the last one is found
|
||||||
-----------------------------------------------------------------*/
|
-----------------------------------------------------------------*/
|
||||||
u32 _FAT_fat_lastCluster (PARTITION* partition, u32 cluster) {
|
uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster) {
|
||||||
while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) {
|
while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) {
|
||||||
cluster = _FAT_fat_nextCluster(partition, cluster);
|
cluster = _FAT_fat_nextCluster(partition, cluster);
|
||||||
}
|
}
|
||||||
@ -346,9 +368,9 @@ u32 _FAT_fat_lastCluster (PARTITION* partition, u32 cluster) {
|
|||||||
_FAT_fat_freeClusterCount
|
_FAT_fat_freeClusterCount
|
||||||
Return the number of free clusters available
|
Return the number of free clusters available
|
||||||
-----------------------------------------------------------------*/
|
-----------------------------------------------------------------*/
|
||||||
u32 _FAT_fat_freeClusterCount (PARTITION* partition) {
|
unsigned int _FAT_fat_freeClusterCount (PARTITION* partition) {
|
||||||
u32 count = 0;
|
unsigned int count = 0;
|
||||||
u32 curCluster;
|
uint32_t curCluster;
|
||||||
|
|
||||||
for (curCluster = CLUSTER_FIRST; curCluster <= partition->fat.lastCluster; curCluster++) {
|
for (curCluster = CLUSTER_FIRST; curCluster <= partition->fat.lastCluster; curCluster++) {
|
||||||
if (_FAT_fat_nextCluster(partition, curCluster) == CLUSTER_FREE) {
|
if (_FAT_fat_nextCluster(partition, curCluster) == CLUSTER_FREE) {
|
||||||
|
@ -25,18 +25,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-10-01 - Chishm
|
|
||||||
* Added _FAT_fat_linkFreeClusterCleared to clear a cluster when it is allocated
|
|
||||||
|
|
||||||
2007-10-25 - Chishm
|
|
||||||
* Use CLUSTER_ERROR when an error occurs with the FAT, not CLUSTER_FREE
|
|
||||||
|
|
||||||
2008-08-17 - Chishm
|
|
||||||
* Added CLUSTER_ROOT definition
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _FAT_H
|
#ifndef _FAT_H
|
||||||
@ -56,22 +44,26 @@
|
|||||||
#define CLUSTERS_PER_FAT16 65525
|
#define CLUSTERS_PER_FAT16 65525
|
||||||
|
|
||||||
|
|
||||||
u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster);
|
uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster);
|
||||||
|
|
||||||
u32 _FAT_fat_linkFreeCluster(PARTITION* partition, u32 cluster);
|
uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster);
|
||||||
u32 _FAT_fat_linkFreeClusterCleared (PARTITION* partition, u32 cluster);
|
uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster);
|
||||||
|
|
||||||
bool _FAT_fat_clearLinks (PARTITION* partition, u32 cluster);
|
bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster);
|
||||||
|
|
||||||
u32 _FAT_fat_lastCluster (PARTITION* partition, u32 cluster);
|
uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength);
|
||||||
|
|
||||||
u32 _FAT_fat_freeClusterCount (PARTITION* partition);
|
uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster);
|
||||||
|
|
||||||
static inline u32 _FAT_fat_clusterToSector (PARTITION* partition, u32 cluster) {
|
unsigned int _FAT_fat_freeClusterCount (PARTITION* partition);
|
||||||
return (cluster >= CLUSTER_FIRST) ? ((cluster - CLUSTER_FIRST) * partition->sectorsPerCluster) + partition->dataStart : partition->rootDirStart;
|
|
||||||
|
static inline sec_t _FAT_fat_clusterToSector (PARTITION* partition, uint32_t cluster) {
|
||||||
|
return (cluster >= CLUSTER_FIRST) ?
|
||||||
|
((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart :
|
||||||
|
partition->rootDirStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool _FAT_fat_isValidCluster (PARTITION* partition, u32 cluster) {
|
static inline bool _FAT_fat_isValidCluster (PARTITION* partition, uint32_t cluster) {
|
||||||
return (cluster >= CLUSTER_FIRST) && (cluster <= partition->fat.lastCluster /* This will catch CLUSTER_ERROR */);
|
return (cluster >= CLUSTER_FIRST) && (cluster <= partition->fat.lastCluster /* This will catch CLUSTER_ERROR */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,27 +24,12 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-09-30 - Chishm
|
|
||||||
* Validity checks performed on the time supplied by the IPC
|
|
||||||
* Cleaned up magic numbers
|
|
||||||
|
|
||||||
2006-10-01 - Chishm
|
|
||||||
* Fixed incorrect use of bitwise-or instead of logical-or
|
|
||||||
|
|
||||||
2007-10-30 - Chishm
|
|
||||||
* Uses standard POSIX time functions
|
|
||||||
|
|
||||||
2007-11-04 - Chishm
|
|
||||||
* Fix off-by-one error for months value
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "filetime.h"
|
#include "filetime.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
#define MAX_HOUR 23
|
#define MAX_HOUR 23
|
||||||
#define MAX_MINUTE 59
|
#define MAX_MINUTE 59
|
||||||
@ -55,8 +40,8 @@
|
|||||||
#define MAX_DAY 31
|
#define MAX_DAY 31
|
||||||
#define MIN_DAY 1
|
#define MIN_DAY 1
|
||||||
|
|
||||||
u16 _FAT_filetime_getTimeFromRTC (void) {
|
uint16_t _FAT_filetime_getTimeFromRTC (void) {
|
||||||
#ifndef GBA
|
#ifdef USE_RTC_TIME
|
||||||
struct tm timeParts;
|
struct tm timeParts;
|
||||||
time_t epochTime;
|
time_t epochTime;
|
||||||
|
|
||||||
@ -82,8 +67,8 @@ u16 _FAT_filetime_getTimeFromRTC (void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
u16 _FAT_filetime_getDateFromRTC (void) {
|
uint16_t _FAT_filetime_getDateFromRTC (void) {
|
||||||
#ifndef GBA
|
#ifdef USE_RTC_TIME
|
||||||
struct tm timeParts;
|
struct tm timeParts;
|
||||||
time_t epochTime;
|
time_t epochTime;
|
||||||
|
|
||||||
@ -105,7 +90,7 @@ u16 _FAT_filetime_getDateFromRTC (void) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t _FAT_filetime_to_time_t (u16 t, u16 d) {
|
time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d) {
|
||||||
struct tm timeParts;
|
struct tm timeParts;
|
||||||
|
|
||||||
timeParts.tm_hour = t >> 11;
|
timeParts.tm_hour = t >> 11;
|
||||||
|
@ -24,12 +24,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2007-10-30 - Chishm
|
|
||||||
* Uses standard POSIX time functions
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _FILETIME_H
|
#ifndef _FILETIME_H
|
||||||
@ -38,10 +32,10 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
u16 _FAT_filetime_getTimeFromRTC (void);
|
uint16_t _FAT_filetime_getTimeFromRTC (void);
|
||||||
u16 _FAT_filetime_getDateFromRTC (void);
|
uint16_t _FAT_filetime_getDateFromRTC (void);
|
||||||
|
|
||||||
time_t _FAT_filetime_to_time_t (u16 t, u16 d);
|
time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d);
|
||||||
|
|
||||||
|
|
||||||
#endif // _FILETIME_H
|
#endif // _FILETIME_H
|
||||||
|
165
source/libfat.c
165
source/libfat.c
@ -24,24 +24,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-08-13 - Chishm
|
|
||||||
* Moved all externally visible directory related functions to fatdir
|
|
||||||
|
|
||||||
2006-08-14 - Chishm
|
|
||||||
* Added extended devoptab_t functions
|
|
||||||
|
|
||||||
2007-01-10 - Chishm
|
|
||||||
* fatInit now sets the correct path when setAsDefaultDevice
|
|
||||||
|
|
||||||
2007-01-11 - Chishm
|
|
||||||
* Added missing #include <unistd.h>
|
|
||||||
|
|
||||||
2007-10-25 - Chishm
|
|
||||||
* Added statvfs functionality
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/iosupport.h>
|
#include <sys/iosupport.h>
|
||||||
@ -53,14 +35,10 @@
|
|||||||
#include "fatfile.h"
|
#include "fatfile.h"
|
||||||
#include "fatdir.h"
|
#include "fatdir.h"
|
||||||
#include "lock.h"
|
#include "lock.h"
|
||||||
|
#include "mem_allocate.h"
|
||||||
|
#include "disc.h"
|
||||||
|
|
||||||
#ifdef GBA
|
static const devoptab_t dotab_fat = {
|
||||||
#define DEFAULT_CACHE_PAGES 2
|
|
||||||
#else
|
|
||||||
#define DEFAULT_CACHE_PAGES 8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const devoptab_t dotab_fat = {
|
|
||||||
"fat",
|
"fat",
|
||||||
sizeof (FILE_STRUCT),
|
sizeof (FILE_STRUCT),
|
||||||
_FAT_open_r,
|
_FAT_open_r,
|
||||||
@ -80,44 +58,111 @@ const devoptab_t dotab_fat = {
|
|||||||
_FAT_dirreset_r,
|
_FAT_dirreset_r,
|
||||||
_FAT_dirnext_r,
|
_FAT_dirnext_r,
|
||||||
_FAT_dirclose_r,
|
_FAT_dirclose_r,
|
||||||
_FAT_statvfs_r
|
_FAT_statvfs_r,
|
||||||
|
_FAT_ftruncate_r,
|
||||||
|
_FAT_fsync_r,
|
||||||
|
NULL /* Device data */
|
||||||
};
|
};
|
||||||
|
|
||||||
bool fatInit (u32 cacheSize, bool setAsDefaultDevice) {
|
|
||||||
|
|
||||||
int i;
|
bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize) {
|
||||||
bool device = false, setDefault = false;
|
PARTITION* partition;
|
||||||
|
devoptab_t* devops;
|
||||||
|
char* nameCopy;
|
||||||
|
|
||||||
if ( PI_MAX_PARTITIONS == 1 ) {
|
devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1);
|
||||||
if ( _FAT_partition_mount ( 0 , cacheSize) ) {
|
if (!devops) {
|
||||||
_FAT_partition_setDefaultInterface (0);
|
return false;
|
||||||
} else {
|
}
|
||||||
|
// Use the space allocated at the end of the devoptab struct for storing the name
|
||||||
|
nameCopy = (char*)(devops+1);
|
||||||
|
|
||||||
|
// Initialize the file system
|
||||||
|
partition = _FAT_partition_constructor (interface, cacheSize, startSector);
|
||||||
|
if (!partition) {
|
||||||
|
_FAT_mem_free (devops);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
// Add an entry for this device to the devoptab table
|
||||||
|
memcpy (devops, &dotab_fat, sizeof(dotab_fat));
|
||||||
|
strcpy (nameCopy, name);
|
||||||
|
devops->name = nameCopy;
|
||||||
|
devops->deviceData = partition;
|
||||||
|
|
||||||
for ( i = 1; i < PI_MAX_PARTITIONS; i++ ) {
|
AddDevice (devops);
|
||||||
device = _FAT_partition_mount ( i , cacheSize);
|
|
||||||
if ( device && !setDefault ) {
|
return true;
|
||||||
_FAT_partition_setDefaultInterface (i);
|
|
||||||
setDefault = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !setDefault ) return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AddDevice (&dotab_fat);
|
bool fatMountSimple (const char* name, const DISC_INTERFACE* interface) {
|
||||||
|
return fatMount (name, interface, 0, DEFAULT_CACHE_PAGES);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fatUnmount (const char* name) {
|
||||||
|
devoptab_t *devops;
|
||||||
|
PARTITION* partition;
|
||||||
|
|
||||||
|
devops = (devoptab_t*)GetDeviceOpTab (name);
|
||||||
|
if (!devops) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a quick check to make sure we're dealing with a libfat controlled device
|
||||||
|
if (devops->open_r != dotab_fat.open_r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RemoveDevice (name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
partition = (PARTITION*)devops->deviceData;
|
||||||
|
_FAT_partition_destructor (partition);
|
||||||
|
_FAT_mem_free (devops);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) {
|
||||||
|
int i;
|
||||||
|
int defaultDevice = -1;
|
||||||
|
const DISC_INTERFACE *disc;
|
||||||
|
|
||||||
|
for (i = 0;
|
||||||
|
_FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL;
|
||||||
|
i++)
|
||||||
|
{
|
||||||
|
disc = _FAT_disc_interfaces[i].getInterface();
|
||||||
|
if (disc->startup() && fatMount (_FAT_disc_interfaces[i].name, disc, 0, cacheSize)) {
|
||||||
|
// The first device to successfully mount is set as the default
|
||||||
|
if (defaultDevice < 0) {
|
||||||
|
defaultDevice = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultDevice < 0) {
|
||||||
|
// None of our devices mounted
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (setAsDefaultDevice) {
|
if (setAsDefaultDevice) {
|
||||||
char filePath[MAXPATHLEN * 2] = "fat:/";
|
char filePath[MAXPATHLEN * 2];
|
||||||
#ifndef GBA
|
strcpy (filePath, _FAT_disc_interfaces[defaultDevice].name);
|
||||||
|
strcat (filePath, ":/");
|
||||||
|
#ifdef ARGV_MAGIC
|
||||||
if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 ) {
|
if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 ) {
|
||||||
|
// Check the app's path against each of our mounted devices, to see
|
||||||
if ( !strncasecmp( __system_argv->argv[0], "fat", 3)) {
|
// if we can support it. If so, change to that path.
|
||||||
|
for (i = 0;
|
||||||
|
_FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL;
|
||||||
|
i++)
|
||||||
|
{
|
||||||
|
if ( !strncasecmp( __system_argv->argv[0], _FAT_disc_interfaces[i].name,
|
||||||
|
strlen(_FAT_disc_interfaces[i].name)))
|
||||||
|
{
|
||||||
|
char *lastSlash;
|
||||||
strcpy(filePath, __system_argv->argv[0]);
|
strcpy(filePath, __system_argv->argv[0]);
|
||||||
char *lastSlash = strrchr( filePath, '/' );
|
lastSlash = strrchr( filePath, '/' );
|
||||||
|
|
||||||
if ( NULL != lastSlash) {
|
if ( NULL != lastSlash) {
|
||||||
if ( *(lastSlash - 1) == ':') lastSlash++;
|
if ( *(lastSlash - 1) == ':') lastSlash++;
|
||||||
@ -125,12 +170,11 @@ bool fatInit (u32 cacheSize, bool setAsDefaultDevice) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
chdir (filePath);
|
chdir (filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
_FAT_lock_init();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,23 +182,4 @@ bool fatInitDefault (void) {
|
|||||||
return fatInit (DEFAULT_CACHE_PAGES, true);
|
return fatInit (DEFAULT_CACHE_PAGES, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize) {
|
|
||||||
return _FAT_partition_mount (partitionNumber, cacheSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fatMountCustomInterface (const IO_INTERFACE* device, u32 cacheSize) {
|
|
||||||
return _FAT_partition_mountCustomInterface (device, cacheSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fatUnmount (PARTITION_INTERFACE partitionNumber) {
|
|
||||||
return _FAT_partition_unmount (partitionNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool fatUnsafeUnmount (PARTITION_INTERFACE partitionNumber) {
|
|
||||||
return _FAT_partition_unsafeUnmount (partitionNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fatSetDefaultInterface (PARTITION_INTERFACE partitionNumber) {
|
|
||||||
return _FAT_partition_setDefaultInterface (partitionNumber);
|
|
||||||
}
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
lock.h
|
|
||||||
|
|
||||||
Copyright (c) 2008 Sven Peter <svpe@gmx.net>
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lock.h"
|
|
||||||
|
|
||||||
#if defined(__wii__) || defined(__gamecube__)
|
|
||||||
|
|
||||||
mutex_t _FAT_mutex;
|
|
||||||
|
|
||||||
#endif /* __wii__ || __gamecube__ */
|
|
@ -31,55 +31,56 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#if defined(__wii__) || defined(__gamecube__)
|
#ifdef USE_LWP_LOCK
|
||||||
|
|
||||||
#include <gccore.h>
|
static inline void _FAT_lock_init(mutex_t *mutex)
|
||||||
|
|
||||||
extern mutex_t _FAT_mutex;
|
|
||||||
|
|
||||||
static inline void _FAT_lock_init()
|
|
||||||
{
|
{
|
||||||
LWP_MutexInit(&_FAT_mutex, false);
|
LWP_MutexInit(mutex, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_lock_deinit()
|
static inline void _FAT_lock_deinit(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
LWP_MutexDestroy(_FAT_mutex);
|
LWP_MutexDestroy(*mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_lock()
|
static inline void _FAT_lock(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
LWP_MutexLock(_FAT_mutex);
|
LWP_MutexLock(*mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_unlock()
|
static inline void _FAT_unlock(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
LWP_MutexUnlock(_FAT_mutex);
|
LWP_MutexUnlock(*mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline void _FAT_lock_init()
|
// We still need a blank lock type
|
||||||
|
#ifndef mutex_t
|
||||||
|
typedef int mutex_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void _FAT_lock_init(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_lock_deinit()
|
static inline void _FAT_lock_deinit(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_lock()
|
static inline void _FAT_lock(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_unlock()
|
static inline void _FAT_unlock(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __wii__ || __gamecube__ */
|
#endif // USE_LWP_LOCK
|
||||||
|
|
||||||
|
|
||||||
#endif // _CACHE_H
|
#endif // _CACHE_H
|
||||||
|
@ -26,9 +26,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _MEM_ALLOCATE_H
|
#ifndef _MEM_ALLOCATE_H
|
||||||
@ -41,7 +38,7 @@ static inline void* _FAT_mem_allocate (size_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_mem_free (void* mem) {
|
static inline void _FAT_mem_free (void* mem) {
|
||||||
return free (mem);
|
free (mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _MEM_ALLOCATE_H
|
#endif // _MEM_ALLOCATE_H
|
||||||
|
@ -1,169 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
disc.c
|
|
||||||
|
|
||||||
uniformed io-interface to work with Chishm's FAT library
|
|
||||||
|
|
||||||
Written by MightyMax
|
|
||||||
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
2005-11-06 - Chishm
|
|
||||||
* Added WAIT_CR modifications for NDS
|
|
||||||
|
|
||||||
2006-02-03 www.neoflash.com
|
|
||||||
* Added SUPPORT_* defines, comment out any of the SUPPORT_* defines in disc_io.h to remove support
|
|
||||||
for the given interface and stop code being linked to the binary
|
|
||||||
|
|
||||||
* Added support for MK2 MMC interface
|
|
||||||
|
|
||||||
* Added disc_Cache* functions
|
|
||||||
|
|
||||||
2006-02-05 - Chishm
|
|
||||||
* Added Supercard SD support
|
|
||||||
|
|
||||||
2006-02-26 - Cytex
|
|
||||||
* Added EFA2 support
|
|
||||||
|
|
||||||
2006-05-18 - Chishm
|
|
||||||
* Rewritten for FATlib disc.c
|
|
||||||
|
|
||||||
2006-06-19 - Chishm
|
|
||||||
* Changed read and write interface to accept a u32 instead of a u8 for the number of sectors
|
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Removed disc_Cache* functions, since there is now a proper unified cache
|
|
||||||
* Removed SUPPORT_* defines
|
|
||||||
* Rewrote device detection functions
|
|
||||||
* First libfat release
|
|
||||||
|
|
||||||
2006-07-25 - Chishm
|
|
||||||
* Changed IO_INTERFACEs to const
|
|
||||||
|
|
||||||
2006-08-02 - Chishm
|
|
||||||
* Added NinjaDS
|
|
||||||
|
|
||||||
2006-12-25 - Chishm
|
|
||||||
* Added DLDI
|
|
||||||
* Removed experimental interfaces
|
|
||||||
|
|
||||||
2007-05-01 - Chishm
|
|
||||||
* Removed FCSR
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "../disc.h"
|
|
||||||
#include "wiisd.h"
|
|
||||||
#include "gcsd.h"
|
|
||||||
|
|
||||||
|
|
||||||
const IO_INTERFACE* ioInterfaces[] = {
|
|
||||||
#ifdef __gamecube__
|
|
||||||
&__io_gcsda,
|
|
||||||
&__io_gcsdb,
|
|
||||||
#endif
|
|
||||||
#ifdef __wii__
|
|
||||||
&__io_wiisd
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __wii__
|
|
||||||
const IO_INTERFACE* _FAT_disc_wiiFindInterface(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
|
|
||||||
if ((ioInterfaces[i]->ioType == DEVICE_TYPE_WII) && (ioInterfaces[i]->fn_startup())) {
|
|
||||||
return ioInterfaces[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const IO_INTERFACE* _FAT_disc_gcFindInterface(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
|
|
||||||
if ((ioInterfaces[i]->ioType == DEVICE_TYPE_GC) && (ioInterfaces[i]->fn_startup())) {
|
|
||||||
return ioInterfaces[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const IO_INTERFACE* _FAT_disc_gcFindInterfaceSlot(int slot)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int mask;
|
|
||||||
|
|
||||||
if(slot == 0)
|
|
||||||
mask = FEATURE_GAMECUBE_SLOTA;
|
|
||||||
else if(slot == 1)
|
|
||||||
mask = FEATURE_GAMECUBE_SLOTB;
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
|
|
||||||
if ((ioInterfaces[i]->ioType == DEVICE_TYPE_GC) && (ioInterfaces[i]->features & mask) && (ioInterfaces[i]->fn_startup())) {
|
|
||||||
return ioInterfaces[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const IO_INTERFACE* _FAT_disc_findInterface(void)
|
|
||||||
{
|
|
||||||
#ifdef __wii__
|
|
||||||
return _FAT_disc_wiiFindInterface();
|
|
||||||
#else
|
|
||||||
return _FAT_disc_gcFindInterface();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const IO_INTERFACE* _FAT_disc_findInterfaceSlot (PARTITION_INTERFACE partitionNumber)
|
|
||||||
{
|
|
||||||
switch(partitionNumber)
|
|
||||||
{
|
|
||||||
#ifdef __gamecube__
|
|
||||||
case PI_SDGECKO_A:
|
|
||||||
return _FAT_disc_gcFindInterfaceSlot(0);
|
|
||||||
break;
|
|
||||||
case PI_SDGECKO_B:
|
|
||||||
return _FAT_disc_gcFindInterfaceSlot(1);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
#ifdef __wii__
|
|
||||||
case PI_INTERNAL_SD:
|
|
||||||
return _FAT_disc_wiiFindInterface();
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
return NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
gcsd.h
|
|
||||||
|
|
||||||
Hardware routines for reading and writing to SD geckos connected
|
|
||||||
to the memory card ports.
|
|
||||||
|
|
||||||
These functions are just wrappers around libsdcard's functions.
|
|
||||||
|
|
||||||
Copyright (c) 2008 Sven "svpe" Peter <svpe@gmx.net>
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __GCSD_H__
|
|
||||||
#define __GCSD_H__
|
|
||||||
|
|
||||||
#include <gccore.h>
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
extern const IO_INTERFACE __io_gcsda;
|
|
||||||
extern const IO_INTERFACE __io_gcsdb;
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
wiisd.c
|
|
||||||
|
|
||||||
Hardware routines for reading and writing to the Wii's internal
|
|
||||||
SD slot.
|
|
||||||
|
|
||||||
Copyright (c) 2008 Sven "svpe" Peter <svpe@gmx.net>
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WIISD_H__
|
|
||||||
#define __WIISD_H__
|
|
||||||
|
|
||||||
#include <gccore.h>
|
|
||||||
#include "../disc.h"
|
|
||||||
|
|
||||||
|
|
||||||
extern const IO_INTERFACE __io_wiisd;
|
|
||||||
|
|
||||||
#endif
|
|
@ -25,15 +25,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
|
|
||||||
2006-08-10 - Chishm
|
|
||||||
* Fixed problem when openning files starting with "fat"
|
|
||||||
|
|
||||||
2006-10-28 - Chishm
|
|
||||||
* _partitions changed to _FAT_partitions to maintain the same style of naming as the functions
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -41,11 +32,12 @@
|
|||||||
#include "bit_ops.h"
|
#include "bit_ops.h"
|
||||||
#include "file_allocation_table.h"
|
#include "file_allocation_table.h"
|
||||||
#include "directory.h"
|
#include "directory.h"
|
||||||
|
#include "mem_allocate.h"
|
||||||
|
#include "fatfile.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <sys/iosupport.h>
|
||||||
#include "mem_allocate.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This device name, as known by devkitPro toolchains
|
This device name, as known by devkitPro toolchains
|
||||||
@ -102,25 +94,13 @@ enum BPB {
|
|||||||
BPB_bootSig_AA = 0x1FF
|
BPB_bootSig_AA = 0x1FF
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(__wii__)
|
static const char FAT_SIG[3] = {'F', 'A', 'T'};
|
||||||
#define MAXIMUM_PARTITIONS 5
|
|
||||||
#elif defined(__gamecube__)
|
|
||||||
#define MAXIMUM_PARTITIONS 4
|
|
||||||
#elif defined(NDS)
|
|
||||||
#define MAXIMUM_PARTITIONS 4
|
|
||||||
#else // not defined NDS
|
|
||||||
#define MAXIMUM_PARTITIONS 1
|
|
||||||
#endif // defined NDS
|
|
||||||
PARTITION* _FAT_partitions[MAXIMUM_PARTITIONS] = {NULL};
|
|
||||||
|
|
||||||
// Use a single static buffer for the partitions
|
|
||||||
|
|
||||||
|
|
||||||
static PARTITION* _FAT_partition_constructor ( const IO_INTERFACE* disc, u32 cacheSize) {
|
PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, sec_t startSector) {
|
||||||
PARTITION* partition;
|
PARTITION* partition;
|
||||||
int i;
|
int i;
|
||||||
u32 bootSector;
|
uint8_t sectorBuffer[BYTES_PER_READ] = {0};
|
||||||
u8 sectorBuffer[BYTES_PER_READ] = {0};
|
|
||||||
|
|
||||||
// Read first sector of disc
|
// Read first sector of disc
|
||||||
if ( !_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) {
|
if ( !_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) {
|
||||||
@ -132,12 +112,14 @@ static PARTITION* _FAT_partition_constructor ( const IO_INTERFACE* disc, u32 cac
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (startSector != 0) {
|
||||||
|
// We're told where to start the partition, so just accept it
|
||||||
|
} else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
||||||
// Check if there is a FAT string, which indicates this is a boot sector
|
// Check if there is a FAT string, which indicates this is a boot sector
|
||||||
if ((sectorBuffer[0x36] == 'F') && (sectorBuffer[0x37] == 'A') && (sectorBuffer[0x38] == 'T')) {
|
startSector = 0;
|
||||||
bootSector = 0;
|
} else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
||||||
} else if ((sectorBuffer[0x52] == 'F') && (sectorBuffer[0x53] == 'A') && (sectorBuffer[0x54] == 'T')) {
|
|
||||||
// Check for FAT32
|
// Check for FAT32
|
||||||
bootSector = 0;
|
startSector = 0;
|
||||||
} else {
|
} else {
|
||||||
// This is an MBR
|
// This is an MBR
|
||||||
// Find first valid partition from MBR
|
// Find first valid partition from MBR
|
||||||
@ -151,14 +133,21 @@ static PARTITION* _FAT_partition_constructor ( const IO_INTERFACE* disc, u32 cac
|
|||||||
// Go to first valid partition
|
// Go to first valid partition
|
||||||
if ( i != 0x1FE) {
|
if ( i != 0x1FE) {
|
||||||
// Make sure it found a partition
|
// Make sure it found a partition
|
||||||
bootSector = u8array_to_u32(sectorBuffer, 0x8 + i);
|
startSector = u8array_to_u32(sectorBuffer, 0x8 + i);
|
||||||
} else {
|
} else {
|
||||||
bootSector = 0; // No partition found, assume this is a MBR free disk
|
startSector = 0; // No partition found, assume this is a MBR free disk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now verify that this is indeed a FAT partition
|
||||||
|
if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) &&
|
||||||
|
memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Read in boot sector
|
// Read in boot sector
|
||||||
if ( !_FAT_disc_readSectors (disc, bootSector, 1, sectorBuffer)) {
|
if ( !_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +156,9 @@ static PARTITION* _FAT_partition_constructor ( const IO_INTERFACE* disc, u32 cac
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init the partition lock
|
||||||
|
_FAT_lock_init(&partition->lock);
|
||||||
|
|
||||||
// Set partition's disc interface
|
// Set partition's disc interface
|
||||||
partition->disc = disc;
|
partition->disc = disc;
|
||||||
|
|
||||||
@ -184,15 +176,16 @@ static PARTITION* _FAT_partition_constructor ( const IO_INTERFACE* disc, u32 cac
|
|||||||
partition->bytesPerSector = BYTES_PER_READ; // Sector size is redefined to be 512 bytes
|
partition->bytesPerSector = BYTES_PER_READ; // Sector size is redefined to be 512 bytes
|
||||||
partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer, BPB_bytesPerSector) / BYTES_PER_READ;
|
partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer, BPB_bytesPerSector) / BYTES_PER_READ;
|
||||||
partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
|
partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
|
||||||
partition->fat.fatStart = bootSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
|
partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
|
||||||
|
|
||||||
partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat);
|
partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat);
|
||||||
partition->dataStart = partition->rootDirStart + (( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector);
|
partition->dataStart = partition->rootDirStart +
|
||||||
|
(( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector);
|
||||||
|
|
||||||
partition->totalSize = (partition->numberOfSectors - partition->dataStart) * partition->bytesPerSector;
|
partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector;
|
||||||
|
|
||||||
// Store info about FAT
|
// Store info about FAT
|
||||||
partition->fat.lastCluster = (partition->numberOfSectors - partition->dataStart) / partition->sectorsPerCluster;
|
partition->fat.lastCluster = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster;
|
||||||
partition->fat.firstFree = CLUSTER_FIRST;
|
partition->fat.firstFree = CLUSTER_FIRST;
|
||||||
|
|
||||||
if (partition->fat.lastCluster < CLUSTERS_PER_FAT12) {
|
if (partition->fat.lastCluster < CLUSTERS_PER_FAT12) {
|
||||||
@ -230,195 +223,38 @@ static PARTITION* _FAT_partition_constructor ( const IO_INTERFACE* disc, u32 cac
|
|||||||
return partition;
|
return partition;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _FAT_partition_destructor (PARTITION* partition) {
|
void _FAT_partition_destructor (PARTITION* partition) {
|
||||||
|
FILE_STRUCT* nextFile;
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
// Synchronize open files
|
||||||
|
nextFile = partition->firstOpenFile;
|
||||||
|
while (nextFile) {
|
||||||
|
_FAT_syncToDisc (nextFile);
|
||||||
|
nextFile = nextFile->nextOpenFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory used by the cache, writing it to disc at the same time
|
||||||
_FAT_cache_destructor (partition->cache);
|
_FAT_cache_destructor (partition->cache);
|
||||||
_FAT_disc_shutdown (partition->disc);
|
|
||||||
|
// Unlock the partition and destroy the lock
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
_FAT_lock_deinit(&partition->lock);
|
||||||
|
|
||||||
|
// Free memory used by the partition
|
||||||
_FAT_mem_free (partition);
|
_FAT_mem_free (partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _FAT_partition_mount (PARTITION_INTERFACE partitionNumber, u32 cacheSize) {
|
|
||||||
int i;
|
|
||||||
const IO_INTERFACE* disc = NULL;
|
|
||||||
|
|
||||||
if (_FAT_partitions[partitionNumber] != NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
disc = _FAT_disc_findInterfaceSlot (partitionNumber);
|
|
||||||
if (disc == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if that disc is already in use, if so, then just copy the partition pointer
|
|
||||||
for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
|
|
||||||
if ((_FAT_partitions[i] != NULL) && (_FAT_partitions[i]->disc == disc)) {
|
|
||||||
_FAT_partitions[partitionNumber] = _FAT_partitions[i];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_partitions[partitionNumber] = _FAT_partition_constructor (disc, cacheSize);
|
|
||||||
|
|
||||||
if (_FAT_partitions[partitionNumber] == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_partition_mountCustomInterface (const IO_INTERFACE* device, u32 cacheSize) {
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef GBA
|
|
||||||
if (_FAT_partitions[0] != NULL) return false;
|
|
||||||
|
|
||||||
if (device == NULL) return false;
|
|
||||||
|
|
||||||
// Only ever one partition on GBA
|
|
||||||
_FAT_partitions[0] = _FAT_partition_constructor (device, cacheSize);
|
|
||||||
#else
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (_FAT_partitions[PI_CUSTOM] != NULL) return false;
|
|
||||||
|
|
||||||
if (device == NULL) return false;
|
|
||||||
|
|
||||||
// See if that disc is already in use, if so, then just copy the partition pointer
|
|
||||||
for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
|
|
||||||
if ((_FAT_partitions[i] != NULL) && (_FAT_partitions[i]->disc == device)) {
|
|
||||||
_FAT_partitions[PI_CUSTOM] = _FAT_partitions[i];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_partitions[PI_CUSTOM] = _FAT_partition_constructor (device, cacheSize);
|
|
||||||
|
|
||||||
if (_FAT_partitions[PI_CUSTOM] == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_partition_setDefaultInterface (PARTITION_INTERFACE partitionNumber) {
|
|
||||||
#ifndef GBA // Can only set the default partition when there is more than 1, so doesn't apply to GBA
|
|
||||||
if ((partitionNumber < 1) || (partitionNumber >= MAXIMUM_PARTITIONS)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_FAT_partitions[partitionNumber] == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_partitions[PI_DEFAULT] = _FAT_partitions[partitionNumber];
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_partition_setDefaultPartition (PARTITION* partition) {
|
|
||||||
#ifndef GBA // Can only set the default partition when there is more than 1, so doesn't apply to GBA
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (partition == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that this device is already in the list
|
|
||||||
for (i = 1; i < MAXIMUM_PARTITIONS; i++) {
|
|
||||||
if (_FAT_partitions[i] == partition) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// It wasn't in the list, so fail
|
|
||||||
if (i == MAXIMUM_PARTITIONS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change the default partition / device to this one
|
|
||||||
_FAT_partitions[PI_DEFAULT] = partition;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_partition_unmount (PARTITION_INTERFACE partitionNumber) {
|
|
||||||
int i;
|
|
||||||
PARTITION* partition = _FAT_partitions[partitionNumber];
|
|
||||||
|
|
||||||
if (partition == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (partition->openFileCount > 0) {
|
|
||||||
// There are still open files that need closing
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all references to this partition
|
|
||||||
for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
|
|
||||||
if (_FAT_partitions[i] == partition) {
|
|
||||||
_FAT_partitions[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_partition_destructor (partition);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_partition_unsafeUnmount (PARTITION_INTERFACE partitionNumber) {
|
|
||||||
int i;
|
|
||||||
PARTITION* partition = _FAT_partitions[partitionNumber];
|
|
||||||
|
|
||||||
if (partition == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all references to this partition
|
|
||||||
for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
|
|
||||||
if (_FAT_partitions[i] == partition) {
|
|
||||||
_FAT_partitions[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_cache_invalidate (partition->cache);
|
|
||||||
_FAT_partition_destructor (partition);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
|
PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
|
||||||
|
const devoptab_t *devops;
|
||||||
|
|
||||||
#ifdef GBA
|
devops = GetDeviceOpTab (path);
|
||||||
return _FAT_partitions[0];
|
|
||||||
#else
|
|
||||||
int namelen;
|
|
||||||
int partitionNumber;
|
|
||||||
|
|
||||||
// Device name extraction code taken from DevKitPro
|
if (!devops) {
|
||||||
namelen = strlen(DEVICE_NAME);
|
|
||||||
if (strchr (path, ':') == NULL) {
|
|
||||||
// No device specified
|
|
||||||
partitionNumber = PI_DEFAULT;
|
|
||||||
} else if( strncmp(DEVICE_NAME, path, namelen) == 0 ) {
|
|
||||||
if ( path[namelen] == ':' ) {
|
|
||||||
// Only the device name is specified
|
|
||||||
partitionNumber = PI_DEFAULT;
|
|
||||||
} else if (isdigit(path[namelen]) && path[namelen+1] ==':' ) {
|
|
||||||
// Device name and number specified
|
|
||||||
partitionNumber = path[namelen] - '0';
|
|
||||||
} else {
|
|
||||||
// Incorrect device name
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Incorrect device name
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((partitionNumber < 0) || (partitionNumber >= MAXIMUM_PARTITIONS)) {
|
return (PARTITION*)devops->deviceData;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _FAT_partitions[partitionNumber];
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,6 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
2006-07-11 - Chishm
|
|
||||||
* Original release
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _PARTITION_H
|
#ifndef _PARTITION_H
|
||||||
@ -35,6 +32,7 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#include "lock.h"
|
||||||
|
|
||||||
// Device name
|
// Device name
|
||||||
extern const char* DEVICE_NAME;
|
extern const char* DEVICE_NAME;
|
||||||
@ -42,75 +40,48 @@ extern const char* DEVICE_NAME;
|
|||||||
// Filesystem type
|
// Filesystem type
|
||||||
typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
|
typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 fatStart;
|
sec_t fatStart;
|
||||||
u32 sectorsPerFat;
|
uint32_t sectorsPerFat;
|
||||||
u32 lastCluster;
|
uint32_t lastCluster;
|
||||||
u32 firstFree;
|
uint32_t firstFree;
|
||||||
} FAT;
|
} FAT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const IO_INTERFACE* disc;
|
const DISC_INTERFACE* disc;
|
||||||
CACHE* cache;
|
CACHE* cache;
|
||||||
// Info about the partition
|
// Info about the partition
|
||||||
bool readOnly; // If this is set, then do not try writing to the disc
|
bool readOnly; // If this is set, then do not try writing to the disc
|
||||||
FS_TYPE filesysType;
|
FS_TYPE filesysType;
|
||||||
u32 totalSize;
|
uint64_t totalSize;
|
||||||
u32 rootDirStart;
|
sec_t rootDirStart;
|
||||||
u32 rootDirCluster;
|
uint32_t rootDirCluster;
|
||||||
u32 numberOfSectors;
|
uint32_t numberOfSectors;
|
||||||
u32 dataStart;
|
sec_t dataStart;
|
||||||
u32 bytesPerSector;
|
uint32_t bytesPerSector;
|
||||||
u32 sectorsPerCluster;
|
uint32_t sectorsPerCluster;
|
||||||
u32 bytesPerCluster;
|
uint32_t bytesPerCluster;
|
||||||
FAT fat;
|
FAT fat;
|
||||||
// Values that may change after construction
|
// Values that may change after construction
|
||||||
u32 cwdCluster; // Current working directory cluser
|
uint32_t cwdCluster; // Current working directory cluster
|
||||||
u32 openFileCount;
|
int openFileCount;
|
||||||
|
struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files
|
||||||
|
mutex_t lock; // A lock for partition operations
|
||||||
} PARTITION;
|
} PARTITION;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount the device specified by partitionDevice
|
Mount the supplied device and return a pointer to the struct necessary to use it
|
||||||
PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
|
|
||||||
PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
|
|
||||||
*/
|
*/
|
||||||
bool _FAT_partition_mount (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
|
PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, sec_t startSector);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mount a partition on a custom device
|
Dismount the device and free all structures used.
|
||||||
|
Will also attempt to synchronise all open files to disc.
|
||||||
*/
|
*/
|
||||||
bool _FAT_partition_mountCustomInterface (const IO_INTERFACE* device, u32 cacheSize);
|
void _FAT_partition_destructor (PARTITION* partition);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Unmount the partition specified by partitionNumber
|
Return the partition specified in a path, as taken from the devoptab.
|
||||||
If there are open files, it will fail
|
|
||||||
*/
|
|
||||||
bool _FAT_partition_unmount (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Forcibly unmount the partition specified by partitionNumber
|
|
||||||
Any open files on the partition will become invalid
|
|
||||||
The cache will be invalidated, and any unflushed writes will be lost
|
|
||||||
*/
|
|
||||||
bool _FAT_partition_unsafeUnmount (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Set the default device for access by fat: and fat0:,
|
|
||||||
based on the device number
|
|
||||||
*/
|
|
||||||
bool _FAT_partition_setDefaultInterface (PARTITION_INTERFACE partitionNumber);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Set the default device for access by fat: and fat0:,
|
|
||||||
based on the partition pointer
|
|
||||||
*/
|
|
||||||
bool _FAT_partition_setDefaultPartition (PARTITION* partition);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return the partition specified in a path
|
|
||||||
For instance, "fat0:", "fat:", "/" and "fat:/" will all
|
|
||||||
return the default partition
|
|
||||||
*/
|
*/
|
||||||
PARTITION* _FAT_partition_getPartitionFromPath (const char* path);
|
PARTITION* _FAT_partition_getPartitionFromPath (const char* path);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user