Initial import into devkitpro CVS.

This commit is contained in:
Michael Chisholm 2006-07-14 02:42:37 +00:00
commit cc64edfab8
50 changed files with 8814 additions and 0 deletions

45
Makefile Normal file
View File

@ -0,0 +1,45 @@
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro)
endif
export TOPDIR := $(CURDIR)
export DATESTRING := $(shell date +%Y)$(shell date +%m)$(shell date +%d)
default: release
all: release dist
release:
make -C nds BUILD=release
make -C gba BUILD=release
debug: lib
make -C nds BUILD=debug
make -C gba BUILD=debug
clean:
make -C nds clean
make -C gba clean
dist-bin: distribute/$(DATESTRING)
make -C nds dist-bin
make -C gba dist-bin
dist-src: distribute/$(DATESTRING)
@tar --exclude=*CVS* -cvjf distribute/$(DATESTRING)/libfat-src-$(DATESTRING).tar.bz2 \
source include Makefile \
nds/Makefile nds/include \
gba/Makefile gba/include
dist: dist-bin dist-src
distribute/$(DATESTRING): distribute
@[ -d $@ ] || mkdir -p $@
distribute:
@[ -d $@ ] || mkdir -p $@
install:
make -C nds install
make -C gba install

159
gba/Makefile Normal file
View File

@ -0,0 +1,159 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM)
endif
include $(DEVKITARM)/gba_rules
TOPDIR ?= $(CURDIR)/..
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# LIB is where the built library will be placed
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD ?= release
SOURCES := ../source ../source/disc_io
INCLUDES := ../include
DATA :=
LIB := $(TOPDIR)/gba/lib
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
CFLAGS := -g -Wall -O2\
-mcpu=arm7tdmi -mtune=arm7tdmi\
-fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DGBA
CXXFLAGS := $(CFLAGS)
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=gba.specs -g $(ARCH) -mno-fpu -Wl,-Map,$(notdir $*.map)
ifneq ($(BUILD),debug)
export GBABIN := $(LIB)/libfat.a
else
export GBABIN := $(LIB)/libfatd.a
CFLAGS += -DFAT_DEBUG
endif
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS :=
#-lnds9
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBGBA)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export CC := $(PREFIX)gcc
export CXX := $(PREFIX)g++
export AR := $(PREFIX)ar
export OBJCOPY := $(PREFIX)objcopy
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr debug release $(LIB)
all: $(GBABIN)
dist-bin:
@tar --exclude=*CVS* -cvjf $(TOPDIR)/distribute/$(DATESTRING)/libfat-gba-$(DATESTRING).tar.bz2 include lib
install: dist-bin
bzip2 -cd $(TOPDIR)/distribute/$(DATESTRING)/libfat-gba-$(DATESTRING).tar.bz2 | tar -xv -C $(DEVKITPRO)/libgba
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(GBABIN) : $(OFILES) $(LIB)
@rm -f "$(GBABIN)"
@$(AR) rcs "$(GBABIN)" $(OFILES)
@echo built ... $(notdir $@)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------
$(LIB):
mkdir $(LIB)

91
gba/include/fat.h Normal file
View File

@ -0,0 +1,91 @@
/*
fat.h
Simple functionality for startup, mounting and unmounting of FAT-based devices.
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 _LIBFAT_H
#define _LIBFAT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "gba_types.h"
typedef enum {PI_CART_SLOT} PARTITION_INTERFACE;
struct IO_INTERFACE_STRUCT ;
/*
Initialise any inserted block-devices.
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
setAsDefaultDevice: if true, make this the default device driver for file operations
*/
bool fatInit (u32 cacheSize, bool setAsDefaultDevice);
/*
Mount the device specified by partitionNumber
PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
*/
bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
/*
Mount a partition on a custom device
*/
bool fatMountCustomInterface (struct IO_INTERFACE_STRUCT* device, u32 cacheSize);
/*
Unmount the partition specified by partitionNumber
If there are open files, it will fail
*/
bool fatUnmount (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 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
}
#endif
#endif // _LIBFAT_H

106
include/fat.h Normal file
View File

@ -0,0 +1,106 @@
/*
fat.h
Simple functionality for startup, mounting and unmounting of FAT-based devices.
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 _LIBFAT_H
#define _LIBFAT_H
#ifdef __cplusplus
extern "C" {
#endif
// When compiling for NDS, make sure NDS is defined
#ifndef NDS
#if defined ARM9 || defined ARM7
#define NDS
#endif
#endif
#ifdef NDS
#include <nds/jtypes.h>
#else
#include "gba_types.h"
#endif
#ifdef NDS
typedef enum {PI_DEFAULT, PI_SLOT_1, PI_SLOT_2, PI_CUSTOM} PARTITION_INTERFACE;
#else
typedef enum {PI_CART_SLOT} PARTITION_INTERFACE;
#endif
struct IO_INTERFACE_STRUCT ;
/*
Initialise any inserted block-devices.
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
setAsDefaultDevice: if true, make this the default device driver for file operations
*/
bool fatInit (u32 cacheSize, bool setAsDefaultDevice);
/*
Mount the device specified by partitionNumber
PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
*/
bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
/*
Mount a partition on a custom device
*/
bool fatMountCustomInterface (struct IO_INTERFACE_STRUCT* device, u32 cacheSize);
/*
Unmount the partition specified by partitionNumber
If there are open files, it will fail
*/
bool fatUnmount (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 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
}
#endif
#endif // _LIBFAT_H

160
nds/Makefile Normal file
View File

@ -0,0 +1,160 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM)
endif
include $(DEVKITARM)/ds_rules
TOPDIR ?= $(CURDIR)/..
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# LIB is where the built library will be placed
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD ?= release
SOURCES := ../source ../source/disc_io
INCLUDES := ../include
DATA :=
LIB := $(TOPDIR)/nds/lib
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
# note: arm9tdmi isn't the correct CPU arch, but anything newer and LD
# *insists* it has a FPU or VFP, and it won't take no for an answer!
CFLAGS := -g -Wall -O2\
-mcpu=arm9tdmi -mtune=arm9tdmi -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM9 -DNDS
CXXFLAGS := $(CFLAGS)
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -mno-fpu -Wl,-Map,$(notdir $*.map)
ifneq ($(BUILD),debug)
export ARM9BIN := $(LIB)/libfat.a
else
export ARM9BIN := $(LIB)/libfatd.a
CFLAGS += -DFAT_DEBUG
endif
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS :=
#-lnds9
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export CC := $(PREFIX)gcc
export CXX := $(PREFIX)g++
export AR := $(PREFIX)ar
export OBJCOPY := $(PREFIX)objcopy
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr debug release $(LIB)
all: $(ARM9BIN)
dist-bin:
@tar --exclude=*CVS* -cvjf $(TOPDIR)/distribute/$(DATESTRING)/libfat-nds-$(DATESTRING).tar.bz2 include lib
install: dist-bin
bzip2 -cd $(TOPDIR)/distribute/$(DATESTRING)/libfat-nds-$(DATESTRING).tar.bz2 | tar -xv -C $(DEVKITPRO)/libnds
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM9BIN) : $(OFILES) $(LIB)
@rm -f "$(ARM9BIN)"
@$(AR) rcs "$(ARM9BIN)" $(OFILES)
@echo built ... $(notdir $@)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------
$(LIB):
mkdir $(LIB)

96
nds/include/fat.h Normal file
View File

@ -0,0 +1,96 @@
/*
fat.h
Simple functionality for startup, mounting and unmounting of FAT-based devices.
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 _LIBFAT_H
#define _LIBFAT_H
#ifdef __cplusplus
extern "C" {
#endif
// When compiling for NDS, make sure NDS is defined
#ifndef NDS
#define NDS
#endif
#include <nds/jtypes.h>
typedef enum {PI_DEFAULT, PI_SLOT_1, PI_SLOT_2, PI_CUSTOM} PARTITION_INTERFACE;
struct IO_INTERFACE_STRUCT ;
/*
Initialise any inserted block-devices.
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
setAsDefaultDevice: if true, make this the default device driver for file operations
*/
bool fatInit (u32 cacheSize, bool setAsDefaultDevice);
/*
Mount the device specified by partitionNumber
PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
*/
bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
/*
Mount a partition on a custom device
*/
bool fatMountCustomInterface (struct IO_INTERFACE_STRUCT* device, u32 cacheSize);
/*
Unmount the partition specified by partitionNumber
If there are open files, it will fail
*/
bool fatUnmount (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 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
}
#endif
#endif // _LIBFAT_H

58
source/bit_ops.h Normal file
View File

@ -0,0 +1,58 @@
/*
bit_ops.h
Functions for dealing with conversion of data between types
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 _BIT_OPS_H
#define _BIT_OPS_H
/*-----------------------------------------------------------------
Functions to deal with little endian values stored in u8 arrays
-----------------------------------------------------------------*/
static inline u16 u8array_to_u16 (const u8* item, int offset) {
return ( item[offset] | (item[offset + 1] << 8));
}
static inline u32 u8array_to_u32 (const u8* item, int offset) {
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) {
item[offset] = (u8)value;
item[offset + 1] = (u8)(value >> 8);
}
static inline void u32_to_u8array (u8* item, int offset, u32 value) {
item[offset] = (u8)value;
item[offset + 1] = (u8)(value >> 8);
item[offset + 2] = (u8)(value >> 16);
item[offset + 3] = (u8)(value >> 24);
}
#endif // _BIT_OPS_H

237
source/cache.c Normal file
View File

@ -0,0 +1,237 @@
/*
cache.c
The cache is not visible to the user. It should be flushed
when any file is closed or changes are made to the filesystem.
This cache implements a least-used-page replacement policy. This will
distribute sectors evenly over the pages, so if less than the maximum
pages are used at once, they should all eventually remain in the cache.
This also has the benefit of throwing out old sectors, so as not to keep
too many stale pages around.
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 <string.h>
#include "common.h"
#include "cache.h"
#include "disc_io/disc.h"
#include "mem_allocate.h"
#define CACHE_FREE 0xFFFFFFFF
CACHE* _FAT_cache_constructor (u32 numberOfPages, IO_INTERFACE* discInterface) {
CACHE* cache;
u32 i;
CACHE_ENTRY* cacheEntries;
if (numberOfPages < 2) {
numberOfPages = 2;
}
cache = (CACHE*) _FAT_mem_allocate (sizeof(CACHE));
if (cache == NULL) {
return false;
}
cache->disc = discInterface;
cache->numberOfPages = numberOfPages;
cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate ( sizeof(CACHE_ENTRY) * numberOfPages);
if (cacheEntries == NULL) {
_FAT_mem_free (cache);
return false;
}
for (i = 0; i < numberOfPages; i++) {
cacheEntries[i].sector = CACHE_FREE;
cacheEntries[i].count = 0;
cacheEntries[i].dirty = false;
}
cache->cacheEntries = cacheEntries;
cache->pages = (u8*) _FAT_mem_allocate ( CACHE_PAGE_SIZE * numberOfPages);
if (cache->pages == NULL) {
_FAT_mem_free (cache->cacheEntries);
_FAT_mem_free (cache);
return false;
}
return cache;
}
void _FAT_cache_destructor (CACHE* cache) {
// Clear out cache before destroying it
_FAT_cache_flush(cache);
// Free memory in reverse allocation order
_FAT_mem_free (cache->pages);
_FAT_mem_free (cache->cacheEntries);
_FAT_mem_free (cache);
return;
}
/*
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.
Return CACHE_FREE on error.
*/
static u32 _FAT_cache_getSector (CACHE* cache, u32 sector) {
u32 i;
CACHE_ENTRY* cacheEntries = cache->cacheEntries;
u32 numberOfPages = cache->numberOfPages;
u32 leastUsed = 0;
u32 lowestCount = 0xFFFFFFFF;
for (i = 0; (i < numberOfPages) && (cacheEntries[i].sector != sector); i++) {
// While searching for the desired sector, also search for the leased used page
if ( (cacheEntries[i].sector == CACHE_FREE) || (cacheEntries[i].count < lowestCount) ) {
leastUsed = i;
lowestCount = cacheEntries[i].count;
}
}
// If it found the sector in the cache, return it
if ((i < numberOfPages) && (cacheEntries[i].sector == sector)) {
// Increment usage counter
cacheEntries[i].count += 1;
return i;
}
// If it didn't, replace the least used cache page with the desired sector
if ((cacheEntries[leastUsed].sector != CACHE_FREE) && (cacheEntries[leastUsed].dirty == true)) {
// Write the page back to disc if it has been written to
if (!_FAT_disc_writeSectors (cache->disc, cacheEntries[leastUsed].sector, 1, cache->pages + CACHE_PAGE_SIZE * leastUsed)) {
return CACHE_FREE;
}
cacheEntries[leastUsed].dirty = false;
}
// Load the new sector into the cache
if (!_FAT_disc_readSectors (cache->disc, sector, 1, cache->pages + CACHE_PAGE_SIZE * leastUsed)) {
return CACHE_FREE;
}
cacheEntries[leastUsed].sector = sector;
// Increment the usage count, don't reset it
// This creates a paging policy of least used PAGE, not sector
cacheEntries[leastUsed].count += 1;
return leastUsed;
}
/*
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) {
u32 page;
if (offset + size > BYTES_PER_READ) {
return false;
}
page = _FAT_cache_getSector (cache, sector);
if (page == CACHE_FREE) {
return false;
}
memcpy (buffer, cache->pages + (CACHE_PAGE_SIZE * page) + offset, size);
return true;
}
/*
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) {
u32 page;
if (offset + size > BYTES_PER_READ) {
return false;
}
page = _FAT_cache_getSector (cache, sector);
if (page == CACHE_FREE) {
return false;
}
memcpy (cache->pages + (CACHE_PAGE_SIZE * page) + offset, buffer, size);
cache->cacheEntries[page].dirty = true;
return true;
}
/*
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) {
u32 page;
if (offset + size > BYTES_PER_READ) {
return false;
}
page = _FAT_cache_getSector (cache, sector);
if (page == CACHE_FREE) {
return false;
}
memset (cache->pages + (CACHE_PAGE_SIZE * page), 0, CACHE_PAGE_SIZE);
memcpy (cache->pages + (CACHE_PAGE_SIZE * page) + offset, buffer, size);
cache->cacheEntries[page].dirty = true;
return true;
}
/*
Flushes all dirty pages to disc, clearing the dirty flag.
Also resets all pages' page count to 0.
*/
bool _FAT_cache_flush (CACHE* cache) {
u32 i;
for (i = 0; i < cache->numberOfPages; i++) {
if (cache->cacheEntries[i].dirty) {
if (!_FAT_disc_writeSectors (cache->disc, cache->cacheEntries[i].sector, 1, cache->pages + CACHE_PAGE_SIZE * i)) {
return CACHE_FREE;
}
}
cache->cacheEntries[i].count = 0;
cache->cacheEntries[i].dirty = false;
}
return true;
}
void _FAT_cache_invalidate (CACHE* cache) {
int i;
for (i = 0; i < cache->numberOfPages; i++) {
cache->cacheEntries[i].sector = CACHE_FREE;
cache->cacheEntries[i].count = 0;
cache->cacheEntries[i].dirty = false;
}
}

118
source/cache.h Normal file
View File

@ -0,0 +1,118 @@
/*
cache.h
The cache is not visible to the user. It should be flushed
when any file is closed or changes are made to the filesystem.
This cache implements a least-used-page replacement policy. This will
distribute sectors evenly over the pages, so if less than the maximum
pages are used at once, they should all eventually remain in the cache.
This also has the benefit of throwing out old sectors, so as not to keep
too many stale pages around.
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 _CACHE_H
#define _CACHE_H
#include "common.h"
#include "disc_io/disc_io.h"
#define CACHE_PAGE_SIZE BYTES_PER_READ
typedef struct {
u32 sector;
u32 count;
bool dirty;
} CACHE_ENTRY;
typedef struct {
IO_INTERFACE* disc;
u32 numberOfPages;
CACHE_ENTRY* cacheEntries;
u8* pages;
} CACHE;
/*
Read data from a sector in the cache
If the sector is not in the cache, it will be swapped in
offset is the position to start reading from
size is the amount of data to read
Precondition: offset + size <= BYTES_PER_READ
*/
bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, u32 sector, u32 offset, u32 size);
/*
Write data to a sector in the cache
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
offset is the position to start reading from
size is the amount of data to read
Precondition: offset + size <= BYTES_PER_READ
*/
bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size);
/*
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.
When the sector is swapped out, the data will be written to the disc
offset is the position to start reading from
size is the amount of data to read
Precondition: offset + size <= BYTES_PER_READ
*/
bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size);
/*
Read a full sector from the cache
*/
static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, u32 sector) {
return _FAT_cache_readPartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
}
/*
Write a full sector to the cache
*/
static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, u32 sector) {
return _FAT_cache_writePartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
}
/*
Write any dirty sectors back to disc and clear out the contents of the cache
*/
bool _FAT_cache_flush (CACHE* cache);
/*
Clear out the contents of the cache without writing any dirty sectors first
*/
void _FAT_cache_invalidate (CACHE* cache);
CACHE* _FAT_cache_constructor (u32 numberOfPages, IO_INTERFACE* discInterface);
void _FAT_cache_destructor (CACHE* cache);
#endif // _CACHE_H

54
source/common.h Normal file
View File

@ -0,0 +1,54 @@
/*
common.h
Common definitions and included files for the FATlib
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 _COMMON_H
#define _COMMON_H
// When compiling for NDS, make sure NDS is defined
#ifndef NDS
#if defined ARM9 || defined ARM7
#define NDS
#endif
#endif
#ifdef NDS
#include <nds/jtypes.h>
#else
#include "gba_types.h"
#endif
#define BYTES_PER_READ 512
#ifndef NULL
#define NULL 0
#endif
#endif // _COMMON_H

877
source/directory.c Normal file
View File

@ -0,0 +1,877 @@
/*
directory.c
Reading, writing and manipulation of the directory structure on
a FAT partition
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 <string.h>
#include <ctype.h>
#include "directory.h"
#include "common.h"
#include "partition.h"
#include "file_allocation_table.h"
#include "bit_ops.h"
#include "filetime.h"
// Directory entry codes
#define DIR_ENTRY_LAST 0x00
#define DIR_ENTRY_FREE 0xE5
// Long file name directory entry
enum LFN_offset {
LFN_offset_ordinal = 0x00, // Position within LFN
LFN_offset_char0 = 0x01,
LFN_offset_char1 = 0x03,
LFN_offset_char2 = 0x05,
LFN_offset_char3 = 0x07,
LFN_offset_char4 = 0x09,
LFN_offset_flag = 0x0B, // Should be equal to ATTRIB_LFN
LFN_offset_reserved1 = 0x0C, // Always 0x00
LFN_offset_checkSum = 0x0D, // Checksum of short file name (alias)
LFN_offset_char5 = 0x0E,
LFN_offset_char6 = 0x10,
LFN_offset_char7 = 0x12,
LFN_offset_char8 = 0x14,
LFN_offset_char9 = 0x16,
LFN_offset_char10 = 0x18,
LFN_offset_reserved2 = 0x1A, // Always 0x0000
LFN_offset_char11 = 0x1C,
LFN_offset_char12 = 0x1E
};
const int LFN_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
#define LFN_END 0x40
#define LFN_DEL 0x80
bool _FAT_directory_isValidLfn (const char* name) {
u32 i;
u32 nameLength;
// Make sure the name is short enough to be valid
if ( strnlen(name, MAX_FILENAME_LENGTH) >= MAX_FILENAME_LENGTH) {
return false;
}
// Make sure it doesn't contain any invalid characters
if (strpbrk (name, "\\/:*?\"<>|") != NULL) {
return false;
}
nameLength = strnlen(name, MAX_FILENAME_LENGTH);
// Make sure the name doesn't contain any control codes
for (i = 0; i < nameLength; i++) {
if (name[i] < 0x20) {
return false;
}
}
// Otherwise it is valid
return true;
}
bool _FAT_directory_isValidAlias (const char* name) {
u32 i;
u32 nameLength;
const char* dot;
// Make sure the name is short enough to be valid
if ( strnlen(name, MAX_ALIAS_LENGTH) >= MAX_ALIAS_LENGTH) {
return false;
}
// Make sure it doesn't contain any invalid characters
if (strpbrk (name, "\\/:;*?\"<>|&+,=[]") != NULL) {
return false;
}
nameLength = strnlen(name, MAX_ALIAS_LENGTH);
// Make sure the name doesn't contain any control codes
for (i = 0; i < nameLength; i++) {
if (name[i] < 0x20) {
return false;
}
}
dot = strchr ( name, '.');
// Make sure there is only one '.'
if ((dot != NULL) && (strrchr ( name, '.') != dot)) {
return false;
}
// If there is a '.':
if (dot != NULL) {
// Make sure the filename portion is 1-8 characters long
if (((dot - 1 - name) > 8) || ((dot - 1 - name) < 1)) {
return false;
}
// Make sure the extension is 1-3 characters long, if it exists
if ((strnlen(dot + 1, MAX_ALIAS_LENGTH) > 3) || (strnlen(dot + 1, MAX_ALIAS_LENGTH) < 1)) {
return false;
}
} else {
// Make sure the entire file name is 1-8 characters long
if ((nameLength > 8) || (nameLength < 1)) {
return false;
}
}
// Since we made it through all those tests, it must be valid
return true;
}
static bool _FAT_directory_entryGetAlias (const u8* entryData, char* destName) {
int i=0;
int j=0;
destName[0] = '\0';
if (entryData[0] != DIR_ENTRY_FREE) {
if (entryData[0] == '.') {
destName[0] = '.';
if (entryData[1] == '.') {
destName[1] = '.';
destName[2] = '\0';
} else {
destName[1] = '\0';
}
} else {
// Copy the filename from the dirEntry to the string
for (i = 0; (i < 8) && (entryData[DIR_ENTRY_name + i] != ' '); i++) {
destName[i] = entryData[DIR_ENTRY_name + i];
}
// Copy the extension from the dirEntry to the string
if (entryData[DIR_ENTRY_extension] != ' ') {
destName[i++] = '.';
for ( j = 0; (j < 3) && (entryData[DIR_ENTRY_extension + j] != ' '); j++) {
destName[i++] = entryData[DIR_ENTRY_extension + j];
}
}
destName[i] = '\0';
}
}
return (destName[0] != '\0');
}
u32 _FAT_directory_entryGetCluster (const u8* entryData) {
return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16);
}
static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ENTRY_POSITION* entryPosition, bool extendDirectory) {
DIR_ENTRY_POSITION position;
position = *entryPosition;
u32 tempCluster;
// Increment offset, wrapping at the end of a sector
++ position.offset;
if (position.offset == BYTES_PER_READ / DIR_ENTRY_DATA_SIZE) {
position.offset = 0;
// Increment sector when wrapping
++ position.sector;
// But wrap at the end of a cluster
if ((position.sector == partition->sectorsPerCluster) && (position.cluster != FAT16_ROOT_DIR_CLUSTER)) {
position.sector = 0;
// Move onto the next cluster, making sure there is another cluster to go to
tempCluster = _FAT_fat_nextCluster(partition, position.cluster);
if (tempCluster == CLUSTER_EOF) {
if (extendDirectory) {
tempCluster = _FAT_fat_linkFreeCluster (partition, position.cluster);
if (tempCluster == CLUSTER_FREE) {
return false; // This will only happen if the disc is full
}
} else {
return false; // Got to the end of the directory, not extending it
}
}
position.cluster = tempCluster;
} else if ((position.cluster == FAT16_ROOT_DIR_CLUSTER) && (position.sector == (partition->dataStart - partition->rootDirStart))) {
return false; // Got to end of root directory, can't extend it
}
}
*entryPosition = position;
return true;
}
bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) {
DIR_ENTRY_POSITION entryStart;
DIR_ENTRY_POSITION entryEnd;
u8 entryData[0x20];
bool notFound, found;
u32 maxSectors;
int lfnPos;
u8 lfnChkSum, chkSum;
char* filename;
bool lfnExists;
int i;
lfnChkSum = 0;
entryStart = entry->dataEnd;
// Make sure we are using the correct root directory, in case of FAT32
if (entryStart.cluster == FAT16_ROOT_DIR_CLUSTER) {
entryStart.cluster = partition->rootDirCluster;
}
entryEnd = entryStart;
filename = entry->filename;
// 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;
found = false;
notFound = false;
while (!found && !notFound) {
if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) {
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);
if (entryData[DIR_ENTRY_attributes] == ATTRIB_LFN) {
// It's an LFN
if (entryData[LFN_offset_ordinal] & LFN_DEL) {
lfnExists = false;
} else if (entryData[LFN_offset_ordinal] & LFN_END) {
// Last part of LFN, make sure it isn't deleted using previous if(Thanks MoonLight)
entryStart = entryEnd; // This is the start of a directory entry
lfnExists = true;
filename[(entryData[LFN_offset_ordinal] & ~LFN_END) * 13] = '\0'; // Set end of lfn to null character
lfnChkSum = entryData[LFN_offset_checkSum];
} if (lfnChkSum != entryData[LFN_offset_checkSum]) {
lfnExists = false;
}
if (lfnExists) {
lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13;
for (i = 0; i < 13; i++) {
filename[lfnPos + i] = entryData[LFN_offset_table[i]]; // modify this for unicode support;
}
}
} else if (entryData[DIR_ENTRY_attributes] & ATTRIB_VOL) {
// This is a volume name, don't bother with it
} else if (entryData[0] == DIR_ENTRY_LAST) {
notFound = true;
} else if ((entryData[0] != DIR_ENTRY_FREE) && (entryData[0] > 0x20) && !(entryData[DIR_ENTRY_attributes] & ATTRIB_VOL)) {
if (lfnExists) {
// Calculate file checksum
chkSum = 0;
for (i=0; i < 11; i++) {
// NOTE: The operation is an unsigned char rotate right
chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + entryData[i];
}
if (chkSum != lfnChkSum) {
lfnExists = false;
filename[0] = '\0';
}
}
if (!lfnExists) {
entryStart = entryEnd;
_FAT_directory_entryGetAlias (entryData, filename);
}
found = true;
}
}
// If no file is found, return false
if (notFound) {
return false;
} else {
// Fill in the directory entry struct
entry->dataStart = entryStart;
entry->dataEnd = entryEnd;
memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE);
return true;
}
}
bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster) {
entry->dataStart.cluster = dirCluster;
entry->dataStart.sector = 0;
entry->dataStart.offset = -1; // Start before the beginning of the directory
entry->dataEnd = entry->dataStart;
return _FAT_directory_getNextEntry (partition, entry);
}
bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) {
entry->dataStart.cluster = 0;
entry->dataStart.sector = 0;
entry->dataStart.offset = 0;
entry->dataEnd = entry->dataStart;
memset (entry->filename, '\0', MAX_FILENAME_LENGTH);
entry->filename[0] = '.';
memset (entry->entryData, 0, DIR_ENTRY_DATA_SIZE);
memset (entry->entryData, ' ', 11);
entry->entryData[0] = '.';
entry->entryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
u16_to_u8array (entry->entryData, DIR_ENTRY_cluster, partition->rootDirCluster);
u16_to_u8array (entry->entryData, DIR_ENTRY_clusterHigh, partition->rootDirCluster >> 16);
return true;
}
bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) {
DIR_ENTRY_POSITION entryStart;
DIR_ENTRY_POSITION entryEnd;
entryStart = entry->dataStart;
entryEnd = entry->dataEnd;
bool entryStillValid;
bool finished;
int i;
int lfnPos;
u8 entryData[DIR_ENTRY_DATA_SIZE];
memset (entry->filename, '\0', MAX_FILENAME_LENGTH);
// Create an empty directory entry to overwrite the old ones with
for ( entryStillValid = true, finished = false;
entryStillValid && !finished;
entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false))
{
_FAT_cache_readPartialSector (partition->cache, entryData,
_FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector,
entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
if ((entryStart.cluster == entryEnd.cluster)
&& (entryStart.sector == entryEnd.sector)
&& (entryStart.offset == entryEnd.offset)) {
// Copy the entry data and stop, since this is the last section of the directory entry
memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE);
finished = true;
} else {
// Copy the long file name data
lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13;
for (i = 0; i < 13; i++) {
entry->filename[lfnPos + i] = entryData[LFN_offset_table[i]]; // modify this for unicode support;
}
}
}
if (!entryStillValid) {
return false;
}
if ((entryStart.cluster == entryEnd.cluster)
&& (entryStart.sector == entryEnd.sector)
&& (entryStart.offset == entryEnd.offset)) {
// Since the entry doesn't have a long file name, extract the short filename
if (!_FAT_directory_entryGetAlias (entry->entryData, entry->filename)) {
return false;
}
}
return true;
}
bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd) {
size_t dirnameLength;
const char* pathPosition;
const char* nextPathPosition;
u32 dirCluster;
bool foundFile;
char alias[MAX_ALIAS_LENGTH];
bool found, notFound;
pathPosition = path;
found = false;
notFound = false;
if (pathEnd == NULL) {
// Set pathEnd to the end of the path string
pathEnd = strchr (path, '\0');
}
if (pathPosition[0] == DIR_SEPARATOR) {
// Start at root directory
dirCluster = partition->rootDirCluster;
// Consume separator(s)
while (pathPosition[0] == DIR_SEPARATOR) {
pathPosition++;
}
if (pathPosition >= pathEnd) {
_FAT_directory_getRootEntry (partition, entry);
found = true;
}
} else {
// Start in current working directory
dirCluster = partition->cwdCluster;
}
while (!found && !notFound) {
// Get the name of the next required subdirectory within the path
nextPathPosition = strchr (pathPosition, DIR_SEPARATOR);
if (nextPathPosition != NULL) {
dirnameLength = nextPathPosition - pathPosition;
} else {
dirnameLength = strlen(pathPosition);
}
if (dirnameLength > MAX_FILENAME_LENGTH) {
// The path is too long to bother with
return false;
}
// Look for the directory within the path
foundFile = _FAT_directory_getFirstEntry (partition, entry, dirCluster);
while (foundFile && !found && !notFound) { // It hasn't already found the file
// Check if the filename matches
if ((dirnameLength == strnlen(entry->filename, MAX_FILENAME_LENGTH))
&& (strncasecmp(entry->filename, pathPosition, dirnameLength) == 0)) {
found = true;
}
// Check if the alias matches
_FAT_directory_entryGetAlias (entry->entryData, alias);
if ((dirnameLength == strnlen(alias, MAX_ALIAS_LENGTH))
&& (strncasecmp(alias, pathPosition, dirnameLength) == 0)) {
found = true;
}
if (found && !(entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && (nextPathPosition != NULL)) {
// Make sure that we aren't trying to follow a file instead of a directory in the path
found = false;
}
if (!found) {
foundFile = _FAT_directory_getNextEntry (partition, entry);
}
}
if (!foundFile) {
// Check that the search didn't get to the end of the directory
notFound = true;
found = false;
} else if ((nextPathPosition == NULL) || (nextPathPosition >= pathEnd)) {
// Check that we reached the end of the path
found = true;
} else if (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) {
dirCluster = _FAT_directory_entryGetCluster (entry->entryData);
pathPosition = nextPathPosition;
// Consume separator(s)
while (pathPosition[0] == DIR_SEPARATOR) {
pathPosition++;
}
// The requested directory was found
if (pathPosition >= pathEnd) {
found = true;
} else {
found = false;
}
}
}
if (found && !notFound) {
return true;
} else {
return false;
}
}
bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) {
DIR_ENTRY_POSITION entryStart;
DIR_ENTRY_POSITION entryEnd;
entryStart = entry->dataStart;
entryEnd = entry->dataEnd;
bool entryStillValid;
bool finished;
u8 entryData[DIR_ENTRY_DATA_SIZE];
// Create an empty directory entry to overwrite the old ones with
for ( entryStillValid = true, finished = false;
entryStillValid && !finished;
entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false))
{
_FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
entryData[0] = DIR_ENTRY_FREE;
_FAT_cache_writePartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
if ((entryStart.cluster == entryEnd.cluster) && (entryStart.sector == entryEnd.sector) && (entryStart.offset == entryEnd.offset)) {
finished = true;
}
}
if (!entryStillValid) {
return false;
}
return true;
}
static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster, u32 size) {
DIR_ENTRY_POSITION gapStart;
DIR_ENTRY_POSITION gapEnd;
u8 entryData[DIR_ENTRY_DATA_SIZE];
u32 dirEntryRemain;
bool endOfDirectory, entryStillValid;
// Scan Dir for free entry
gapEnd.offset = 0;
gapEnd.sector = 0;
gapEnd.cluster = dirCluster;
gapStart = gapEnd;
entryStillValid = true;
dirEntryRemain = size;
endOfDirectory = false;
while (entryStillValid && !endOfDirectory && (dirEntryRemain > 0)) {
_FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
if (entryData[0] == DIR_ENTRY_LAST) {
gapStart = gapEnd;
-- dirEntryRemain;
endOfDirectory = true;
} else if (entryData[0] == DIR_ENTRY_FREE) {
if (dirEntryRemain == size) {
gapStart = gapEnd;
}
-- dirEntryRemain;
} else {
dirEntryRemain = size;
}
if (!endOfDirectory && (dirEntryRemain > 0)) {
entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true);
}
}
// Make sure the scanning didn't fail
if (!entryStillValid) {
return false;
}
// Save the start entry, since we know it is valid
entry->dataStart = gapStart;
if (endOfDirectory) {
memset (entryData, DIR_ENTRY_LAST, DIR_ENTRY_DATA_SIZE);
while (((dirEntryRemain + 1) > 0) && entryStillValid) {
entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true);
-- dirEntryRemain;
if (dirEntryRemain > 0) {
entry->dataEnd = gapEnd;
}
_FAT_cache_writePartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
}
if (!entryStillValid) {
return false;
}
} else {
entry->dataEnd = gapEnd;
}
return true;
}
static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, u32 dirCluster) {
DIR_ENTRY tempEntry;
bool foundFile;
char alias[MAX_ALIAS_LENGTH];
u32 dirnameLength;
dirnameLength = strnlen(name, MAX_FILENAME_LENGTH);
if (dirnameLength >= MAX_FILENAME_LENGTH) {
return false;
}
// Make sure the entry doesn't already exist
foundFile = _FAT_directory_getFirstEntry (partition, &tempEntry, dirCluster);
while (foundFile) { // It hasn't already found the file
// Check if the filename matches
if ((dirnameLength == strnlen(tempEntry.filename, MAX_FILENAME_LENGTH))
&& (strcasecmp(tempEntry.filename, name) == 0)) {
return true;
}
// Check if the alias matches
_FAT_directory_entryGetAlias (tempEntry.entryData, alias);
if ((dirnameLength == strnlen(alias, MAX_ALIAS_LENGTH))
&& (strcasecmp(alias, name) == 0)) {
return true;
}
foundFile = _FAT_directory_getNextEntry (partition, &tempEntry);
}
return false;
}
bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster) {
u32 entrySize;
u8 lfnEntry[DIR_ENTRY_DATA_SIZE];
s32 i,j; // Must be signed for use when decrementing in for loop
char *tmpCharPtr;
DIR_ENTRY_POSITION curEntryPos;
bool entryStillValid;
u8 aliasCheckSum = 0;
char alias [MAX_ALIAS_LENGTH];
// Make sure the filename is not 0 length
if (strnlen (entry->filename, MAX_FILENAME_LENGTH) < 1) {
return false;
}
// Make sure the filename is at least a valid LFN
if ( !(_FAT_directory_isValidLfn (entry->filename))) {
return false;
}
// Remove trailing spaces
for (i = strlen (entry->filename) - 1; (i > 0) && (entry->filename[i] == ' '); --i) {
entry->filename[i] = '\0';
}
// Remove leading spaces
for (i = 0; (i < strlen (entry->filename)) && (entry->filename[i] == ' '); ++i) ;
if (i > 0) {
memmove (entry->filename, entry->filename + i, strlen (entry->filename + i));
}
// Remove junk in filename
i = strlen (entry->filename);
memset (entry->filename + i, '\0', MAX_FILENAME_LENGTH - i);
// Make sure the entry doesn't already exist
if (_FAT_directory_entryExists (partition, entry->filename, dirCluster)) {
return false;
}
// Clear out alias, so we can generate a new one
memset (entry->entryData, ' ', 11);
if ( strncmp(entry->filename, ".", MAX_FILENAME_LENGTH) == 0) {
// "." entry
entry->entryData[0] = '.';
entrySize = 1;
} else if ( strncmp(entry->filename, "..", MAX_FILENAME_LENGTH) == 0) {
// ".." entry
entry->entryData[0] = '.';
entry->entryData[1] = '.';
entrySize = 1;
} else if ( _FAT_directory_isValidAlias (entry->filename)) {
// Short filename
strupr (entry->filename);
entrySize = 1;
// Copy into alias
for (i = 0, j = 0; (j < 8) && (entry->filename[i] != '.') && (entry->filename[i] != '\0'); i++, j++) {
entry->entryData[j] = entry->filename[i];
}
while (j < 8) {
entry->entryData[j] = ' ';
++ j;
}
if (entry->filename[i] == '.') {
// Copy extension
++ i;
while ((entry->filename[i] != '\0') && (j < 11)) {
entry->entryData[j] = entry->filename[i];
++ i;
++ j;
}
}
while (j < 11) {
entry->entryData[j] = ' ';
++ j;
}
} else {
// Long filename needed
entrySize = ((strnlen (entry->filename, MAX_FILENAME_LENGTH) + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1;
// Generate alias
tmpCharPtr = strrchr (entry->filename, '.');
if (tmpCharPtr == NULL) {
tmpCharPtr = strrchr (entry->filename, '\0');
}
for (i = 0, j = 0; (j < 6) && (entry->filename + i < tmpCharPtr); i++) {
if ( isalnum(entry->filename[i])) {
alias[j] = entry->filename[i];
++ j;
}
}
while (j < 8) {
alias[j] = '_';
++ j;
}
tmpCharPtr = strrchr (entry->filename, '.');
if (tmpCharPtr != NULL) {
alias[8] = '.';
// Copy extension
while ((tmpCharPtr != '\0') && (j < 12)) {
alias[j] = tmpCharPtr[0];
++ tmpCharPtr;
++ j;
}
alias[j] = '\0';
} else {
for (j = 8; j < MAX_ALIAS_LENGTH; j++) {
alias[j] = '\0';
}
}
// Get a valid tail number
alias[5] = '~';
i = 0;
do {
i++;
alias[6] = '0' + ((i / 10) % 10); // 10's digit
alias[7] = '0' + (i % 10); // 1's digit
} while (_FAT_directory_entryExists (partition, alias, dirCluster) && (i < 100));
if (i == 100) {
// Couldn't get a tail number
return false;
}
// Make it upper case
strupr (alias);
// Now copy it into the directory entry data
memcpy (entry->entryData, alias, 8);
memcpy (entry->entryData + 8, alias + 9, 3);
for (i = 0; i < 10; i++) {
if (entry->entryData[i] < 0x20) {
// Replace null and control characters with spaces
entry->entryData[i] = 0x20;
}
}
// Generate alias checksum
for (i=0; i < 11; i++)
{
// NOTE: The operation is an unsigned char rotate right
aliasCheckSum = ((aliasCheckSum & 1) ? 0x80 : 0) + (aliasCheckSum >> 1) + entry->entryData[i];
}
}
// Find or create space for the entry
if (_FAT_directory_findEntryGap (partition, entry, dirCluster, entrySize) == false) {
return false;
}
// Write out directory entry
curEntryPos = entry->dataStart;
for (entryStillValid = true, i = entrySize; entryStillValid && i > 0;
entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &curEntryPos, false), -- i )
{
if (i > 1) {
// Long filename entry
lfnEntry[LFN_offset_ordinal] = (i - 1) | (i == entrySize ? LFN_END : 0);
for (j = 0; j < 13; j++) {
if (entry->filename [(i - 2) * 13 + j] == '\0') {
if ((j > 1) && (entry->filename [(i - 2) * 13 + (j-1)] == '\0')) {
u16_to_u8array (lfnEntry, LFN_offset_table[j], 0xffff); // Padding
} else {
u16_to_u8array (lfnEntry, LFN_offset_table[j], 0x0000); // Terminating null character
}
} else {
u16_to_u8array (lfnEntry, LFN_offset_table[j], entry->filename [(i - 2) * 13 + j]);
}
}
lfnEntry[LFN_offset_checkSum] = aliasCheckSum;
lfnEntry[LFN_offset_flag] = ATTRIB_LFN;
lfnEntry[LFN_offset_reserved1] = 0;
u16_to_u8array (lfnEntry, LFN_offset_reserved2, 0);
_FAT_cache_writePartialSector (partition->cache, lfnEntry, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
} else {
// Alias & file data
_FAT_cache_writePartialSector (partition->cache, entry->entryData, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
}
}
return true;
}
bool _FAT_directory_chdir (PARTITION* partition, const char* path) {
DIR_ENTRY entry;
if (!_FAT_directory_entryFromPath (partition, &entry, path, NULL)) {
return false;
}
if (!(entry.entryData[DIR_ENTRY_attributes] & ATTRIB_DIR)) {
return false;
}
partition->cwdCluster = _FAT_directory_entryGetCluster (entry.entryData);
return true;
}
void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st) {
// Fill in the stat struct
// Some of the values are faked for the sake of compatibility
st->st_dev = (int)partition; // The device is the partition pointer
st->st_ino = (ino_t)(_FAT_directory_entryGetCluster(entry->entryData)); // The file serial number is the start cluster
st->st_mode = (_FAT_directory_isDirectory(entry) ? S_IFDIR : S_IFREG) |
(S_IRUSR | S_IRGRP | S_IROTH) |
(_FAT_directory_isWritable (entry) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); // Mode bits based on dirEntry ATTRIB byte
st->st_nlink = 1; // Always one hard link on a FAT file
st->st_uid = 1; // Faked for FAT
st->st_gid = 2; // Faked for FAT
st->st_rdev = st->st_dev;
st->st_size = u8array_to_u32 (entry->entryData, DIR_ENTRY_fileSize); // File size
st->st_atime = _FAT_filetime_to_time_t (
0,
u8array_to_u16 (entry->entryData, DIR_ENTRY_aDate)
);
st->st_spare1 = 0;
st->st_mtime = _FAT_filetime_to_time_t (
u8array_to_u16 (entry->entryData, DIR_ENTRY_mTime),
u8array_to_u16 (entry->entryData, DIR_ENTRY_mDate)
);
st->st_spare2 = 0;
st->st_ctime = _FAT_filetime_to_time_t (
u8array_to_u16 (entry->entryData, DIR_ENTRY_cTime),
u8array_to_u16 (entry->entryData, DIR_ENTRY_cDate)
);
st->st_spare3 = 0;
st->st_blksize = BYTES_PER_READ; // Prefered file I/O block size
st->st_blocks = (st->st_size + BYTES_PER_READ - 1) / BYTES_PER_READ; // File size in blocks
st->st_spare4[0] = 0;
st->st_spare4[1] = 0;
}

171
source/directory.h Normal file
View File

@ -0,0 +1,171 @@
/*
directory.h
Reading, writing and manipulation of the directory structure on
a FAT partition
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 _DIRECTORY_H
#define _DIRECTORY_H
#include <sys/stat.h>
#include "common.h"
#include "partition.h"
#define DIR_ENTRY_DATA_SIZE 0x20
#define MAX_FILENAME_LENGTH 256
#define MAX_ALIAS_LENGTH 13
#define LFN_ENTRY_LENGTH 13
#define FAT16_ROOT_DIR_CLUSTER 0
#define DIR_SEPARATOR '/'
// File attributes
#define ATTRIB_ARCH 0x20 // Archive
#define ATTRIB_DIR 0x10 // Directory
#define ATTRIB_LFN 0x0F // Long file name
#define ATTRIB_VOL 0x08 // Volume
#define ATTRIB_SYS 0x04 // System
#define ATTRIB_HID 0x02 // Hidden
#define ATTRIB_RO 0x01 // Read only
typedef enum {FT_DIRECTORY, FT_FILE} FILE_TYPE;
typedef struct {
u32 cluster;
u32 sector;
s32 offset;
} DIR_ENTRY_POSITION;
typedef struct {
u8 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 dataEnd; // Always points to the file/directory's alias entry
char filename[MAX_FILENAME_LENGTH];
} DIR_ENTRY;
// Directory entry offsets
enum DIR_ENTRY_offset {
DIR_ENTRY_name = 0x00,
DIR_ENTRY_extension = 0x08,
DIR_ENTRY_attributes = 0x0B,
DIR_ENTRY_reserved = 0x0C,
DIR_ENTRY_cTime_ms = 0x0D,
DIR_ENTRY_cTime = 0x0E,
DIR_ENTRY_cDate = 0x10,
DIR_ENTRY_aDate = 0x12,
DIR_ENTRY_clusterHigh = 0x14,
DIR_ENTRY_mTime = 0x16,
DIR_ENTRY_mDate = 0x18,
DIR_ENTRY_cluster = 0x1A,
DIR_ENTRY_fileSize = 0x1C
};
/*
Returns true if the file specified by entry is a directory
*/
static inline bool _FAT_directory_isDirectory (DIR_ENTRY* entry) {
return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) != 0);
}
static inline bool _FAT_directory_isWritable (DIR_ENTRY* entry) {
return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_RO) == 0);
}
static inline bool _FAT_directory_isDot (DIR_ENTRY* entry) {
return ((entry->filename[0] == '.') && ((entry->filename[1] == '\0') ||
((entry->filename[1] == '.') && entry->filename[2] == '\0')));
}
/*
Reads the first directory entry from the directory starting at dirCluster
Places result in entry
entry will be destroyed even if no directory entry is found
Returns true on success, false on failure
*/
bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster);
/*
Reads the next directory entry after the one already pointed to by entry
Places result in entry
entry will be destroyed even if no directory entry is found
Returns true on success, false on failure
*/
bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry);
/*
Gets the directory entry corrsponding to the supplied path
entry will be destroyed even if no directory entry is found
pathEnd specifies the end of the path string, for cutting strings short if needed
specify NULL to use the full length of path
pathEnd is only a suggestion, and the path string will be searched up until the next PATH_SEPARATOR
after pathEND.
Returns true on success, false on failure
*/
bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd);
/*
Changes the current directory to the one specified by path
Returns true on success, false on failure
*/
bool _FAT_directory_chdir (PARTITION* partition, const char* path);
/*
Removes the directory entry specified by entry
Assumes that entry is valid
Returns true on success, false on failure
*/
bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry);
/*
Add a directory entry to the directory specified by dirCluster
The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are
updated with the new directory entry position and alias.
Returns true on success, false on failure
*/
bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster);
/*
Get the start cluster of a file from it's entry data
*/
u32 _FAT_directory_entryGetCluster (const u8* entryData);
/*
Fill in the file name and entry data of DIR_ENTRY* entry.
Assumes that the entry's dataStart and dataEnd are correct
Returns true on success, false on failure
*/
bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry);
/*
Fill in a stat struct based on a file entry
*/
void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st);
#endif // _DIRECTORY_H

184
source/disc_io/disc.c Normal file
View File

@ -0,0 +1,184 @@
/*
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
*/
#include "disc.h"
#include "disc_io.h"
#ifdef NDS
#include <nds.h>
#endif
// Include known io-interfaces:
#include "io_mpcf.h"
#include "io_m3cf.h"
#include "io_m3sd.h"
#include "io_sccf.h"
#include "io_scsd.h"
#include "io_fcsr.h"
#include "io_nmmc.h"
#include "io_efa2.h"
#include "io_mmcf.h"
IO_INTERFACE* ioInterfaces[] = {
#ifdef NDS
// Place Slot 1 (DS Card) interfaces here
&_io_nmmc,
#endif
// Place Slot 2 (GBA Cart) interfaces here
&_io_mpcf, &_io_m3cf, &_io_m3sd, &_io_sccf, &_io_fcsr
// Experimental Slot 2 interfaces
, &_io_mmcf, &_io_scsd, &_io_efa2
};
/*
Hardware level disc funtions
*/
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
WAIT_CR &= ~ARM9_OWNS_ROM;
#endif
#ifdef ARM7
WAIT_CR |= ARM9_OWNS_ROM;
#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.
*/
IO_INTERFACE* _FAT_disc_dsSlotFindInterface (void)
{
#ifdef ARM9
WAIT_CR &= ~ARM9_OWNS_CARD;
#endif
#ifdef ARM7
WAIT_CR |= ARM9_OWNS_CARD;
#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
IO_INTERFACE* _FAT_disc_findInterface (void)
{
#ifdef ARM9
WAIT_CR &= ~(ARM9_OWNS_CARD | ARM9_OWNS_ROM);
#endif
#ifdef ARM7
WAIT_CR |= (ARM9_OWNS_CARD | ARM9_OWNS_ROM);
#endif
int i;
for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
if (ioInterfaces[i]->fn_startup()) {
return ioInterfaces[i];
}
}
return NULL;
}
#else
IO_INTERFACE* _FAT_disc_findInterface (void)
{
return _FAT_disc_gbaSlotFindInterface();
}
#endif

126
source/disc_io/disc.h Normal file
View File

@ -0,0 +1,126 @@
/*
disc.h
Interface to the low level disc functions. Used by the higher level
file system code.
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 _DISC_H
#define _DISC_H
#include "../common.h"
#include "disc_io.h"
/*
Search for a block based device in the GBA slot.
Return a pointer to a usable interface if one is found,
NULL if not.
*/
extern IO_INTERFACE* _FAT_disc_gbaSlotFindInterface (void);
/*
Search for a block based device in the DS slot.
Return a pointer to a usable interface if one is found,
NULL if not.
*/
#ifdef NDS
extern IO_INTERFACE* _FAT_disc_dsSlotFindInterface (void);
#endif
/*
Search for a block based device in the both slots.
Return a pointer to a usable interface if one is found,
NULL if not.
*/
extern IO_INTERFACE* _FAT_disc_findInterface (void);
/*
Check if a disc is inserted
Return true if a disc is inserted and ready, false otherwise
*/
static inline bool _FAT_disc_isInserted (IO_INTERFACE* disc) {
return disc->fn_isInserted();
}
/*
Read numSectors sectors from a disc, starting at sector.
numSectors is between 1 and 256
sector is from 0 to 2^28
buffer is a pointer to the memory to fill
*/
static inline bool _FAT_disc_readSectors (IO_INTERFACE* disc, u32 sector, u32 numSectors, void* buffer) {
return disc->fn_readSectors (sector, numSectors, buffer);
}
/*
Write numSectors sectors to a disc, starting at sector.
numSectors is between 1 and 256
sector is from 0 to 2^28
buffer is a pointer to the memory to read from
*/
static inline bool _FAT_disc_writeSectors (IO_INTERFACE* disc, u32 sector, u32 numSectors, const void* buffer) {
return disc->fn_writeSectors (sector, numSectors, buffer);
}
/*
Reset the card back to a ready state
*/
static inline bool _FAT_disc_clearStatus (IO_INTERFACE* disc) {
return disc->fn_clearStatus();
}
/*
Initialise the disc to a state ready for data reading or writing
*/
static inline bool _FAT_disc_startup (IO_INTERFACE* disc) {
return disc->fn_startup();
}
/*
Put the disc in a state ready for power down.
Complete any pending writes and disable the disc if necessary
*/
static inline bool _FAT_disc_shutdown (IO_INTERFACE* disc) {
return disc->fn_shutdown();
}
/*
Return a 32 bit value unique to each type of interface
*/
static inline u32 _FAT_disc_hostType (IO_INTERFACE* disc) {
return disc->ioType;
}
/*
Return a 32 bit value that specifies the capabilities of the disc
*/
static inline u32 _FAT_disc_features (IO_INTERFACE* disc) {
return disc->features;
}
#endif // _DISC_H

77
source/disc_io/disc_io.h Normal file
View File

@ -0,0 +1,77 @@
/*
disc_io.h
Interface template for low level disc functions.
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 _DISC_IO_H
#define _DISC_IO_H
#include "../common.h"
//----------------------------------------------------------------------
// Customisable features
// Use DMA to read the card, remove this line to use normal reads/writes
// #define _CF_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 _CF_ALLOW_UNALIGNED
#if defined _CF_USE_DMA && defined _CF_ALLOW_UNALIGNED
#error You can't use both DMA and unaligned memory
#endif
#define FEATURE_MEDIUM_CANREAD 0x00000001
#define FEATURE_MEDIUM_CANWRITE 0x00000002
#define FEATURE_SLOT_GBA 0x00000010
#define FEATURE_SLOT_NDS 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 ;
#endif // define _DISC_IO_H

View File

@ -0,0 +1,55 @@
/*
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
*/
#ifndef IO_CF_COMMON_H
#define IO_CF_COMMON_H
#include "disc_io.h"
// 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
#endif // define IO_CF_COMMON_H

307
source/disc_io/io_efa2.c Normal file
View File

@ -0,0 +1,307 @@
/*
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 _CF_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 _CF_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
-----------------------------------------------------------------*/
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
};

23
source/disc_io/io_efa2.h Normal file
View File

@ -0,0 +1,23 @@
/*
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_io.h"
// export interface
extern IO_INTERFACE _io_efa2;
#endif // define IO_EFA2_H

334
source/disc_io/io_fcsr.c Normal file
View File

@ -0,0 +1,334 @@
/*
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 _CF_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 _CF_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 // !_CF_USE_DMA
memcpy (buffer, src, readLength);
#endif // _CF_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
-----------------------------------------------------------------*/
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
} ;

44
source/disc_io/io_fcsr.h Normal file
View File

@ -0,0 +1,44 @@
/*
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_io.h"
// export interface
extern IO_INTERFACE _io_fcsr ;
#endif // define IO_FCSR_H

View File

@ -0,0 +1,60 @@
/*
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);
}
}

View File

@ -0,0 +1,48 @@
/*
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_io.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

354
source/disc_io/io_m3cf.c Normal file
View File

@ -0,0 +1,354 @@
/*
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 _CF_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
// 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 M3_DATA ((vu16*)0x08800000) // Pointer to buffer of CF data transered from card
/*-----------------------------------------------------------------
_M3CF_isInserted
Is a compact flash card inserted?
bool return OUT: true if a CF card is inserted
-----------------------------------------------------------------*/
bool _M3CF_isInserted (void)
{
// Change register, then check if value did change
REG_M3CF_STS = CF_STS_INSERTED;
return ((REG_M3CF_STS & 0xff) == CF_STS_INSERTED);
}
/*-----------------------------------------------------------------
_M3CF_clearStatus
Tries to make the CF card go back to idle mode
bool return OUT: true if a CF card is idle
-----------------------------------------------------------------*/
bool _M3CF_clearStatus (void)
{
int i;
// Wait until CF card is finished previous commands
i=0;
while ((REG_M3CF_CMD & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
// Wait until card is ready for commands
i = 0;
while ((!(REG_M3CF_STS & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
return true;
}
/*-----------------------------------------------------------------
_M3CF_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 _M3CF_readSectors (u32 sector, u32 numSectors, void* buffer)
{
int i;
u16 *buff = (u16*)buffer;
#ifdef _CF_ALLOW_UNALIGNED
u8 *buff_u8 = (u8*)buffer;
int temp;
#endif
#if defined _CF_USE_DMA && defined NDS && defined ARM9
DC_FlushRange( buffer, numSectors * BYTES_PER_READ);
#endif
// Wait until CF card is finished previous commands
i=0;
while ((REG_M3CF_CMD & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
// Wait until card is ready for commands
i = 0;
while ((!(REG_M3CF_STS & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Set number of sectors to read
REG_M3CF_SEC = (numSectors < 256 ? numSectors : 0); // Read a maximum of 256 sectors, 0 means 256
// Set read sector
REG_M3CF_LBA1 = sector & 0xFF; // 1st byte of sector number
REG_M3CF_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
REG_M3CF_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
REG_M3CF_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
// Set command to read
REG_M3CF_CMD = CF_CMD_READ;
while (numSectors--)
{
// Wait until card is ready for reading
i = 0;
while (((REG_M3CF_STS & 0xff) != CF_STS_READY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Read data
#ifdef _CF_USE_DMA
#ifdef NDS
DMA3_SRC = (u32)M3_DATA;
DMA3_DEST = (u32)buff;
DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
#else
DMA3COPY ( M3_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
#endif
buff += BYTES_PER_READ / 2;
#elif defined _CF_ALLOW_UNALIGNED
i=256;
if ((u32)buff_u8 & 0x01) {
while(i--)
{
temp = *M3_DATA;
*buff_u8++ = temp & 0xFF;
*buff_u8++ = temp >> 8;
}
} else {
while(i--)
*buff++ = *M3_DATA;
}
#else
i=256;
while(i--)
*buff++ = *M3_DATA;
#endif
}
#if defined _CF_USE_DMA && defined NDS
// Wait for end of transfer before returning
while(DMA3_CR & DMA_BUSY);
#endif
return true;
}
/*-----------------------------------------------------------------
_M3CF_writeSectors
Write 512 byte sector numbered "sector" from "buffer"
u32 sector IN: address of 512 byte sector on CF card to read
u32 numSecs IN: number of 512 byte sectors to read,
1 to 256 sectors can be read, 0 = 256
void* buffer IN: pointer to 512 byte buffer to read data from
bool return OUT: true if successful
-----------------------------------------------------------------*/
bool _M3CF_writeSectors (u32 sector, u32 numSectors, void* buffer)
{
int i;
u16 *buff = (u16*)buffer;
#ifdef _CF_ALLOW_UNALIGNED
u8 *buff_u8 = (u8*)buffer;
int temp;
#endif
#if defined _CF_USE_DMA && defined NDS && defined ARM9
DC_FlushRange( buffer, numSectors * BYTES_PER_READ);
#endif
// Wait until CF card is finished previous commands
i=0;
while ((REG_M3CF_CMD & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
// Wait until card is ready for commands
i = 0;
while ((!(REG_M3CF_STS & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Set number of sectors to write
REG_M3CF_SEC = (numSectors < 256 ? numSectors : 0); // Max of 256, 0 means 256
// Set write sector
REG_M3CF_LBA1 = sector & 0xFF; // 1st byte of sector number
REG_M3CF_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
REG_M3CF_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
REG_M3CF_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
// Set command to write
REG_M3CF_CMD = CF_CMD_WRITE;
while (numSectors--)
{
// Wait until card is ready for writing
i = 0;
while (((REG_M3CF_STS & 0xff) != CF_STS_READY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Write data
#ifdef _CF_USE_DMA
#ifdef NDS
DMA3_SRC = (u32)buff;
DMA3_DEST = (u32)M3_DATA;
DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
#else
DMA3COPY( buff, M3_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
#endif
buff += BYTES_PER_READ / 2;
#elif defined _CF_ALLOW_UNALIGNED
i=256;
if ((u32)buff_u8 & 0x01) {
while(i--)
{
temp = *buff_u8++;
temp |= *buff_u8++ << 8;
*M3_DATA = temp;
}
} else {
while(i--)
*M3_DATA = *buff++;
}
#else
i=256;
while(i--)
*M3_DATA = *buff++;
#endif
}
#if defined _CF_USE_DMA && defined NDS
// Wait for end of transfer before returning
while(DMA3_CR & DMA_BUSY);
#endif
return true;
}
/*-----------------------------------------------------------------
M3_Unlock
Returns true if M3 was unlocked, false if failed
Added by MightyMax
-----------------------------------------------------------------*/
static bool _M3CF_unlock(void)
{
u16 temp;
_M3_changeMode (M3_MODE_MEDIA);
// test that we have register access
temp = REG_M3CF_LBA1;
temp = (~temp & 0xFF);
REG_M3CF_LBA1 = temp;
// did it change?
return (REG_M3CF_LBA1 == temp) ;
}
bool _M3CF_shutdown(void) {
if ( !_M3CF_clearStatus() ) {
return false;
}
_M3_changeMode (M3_MODE_ROM);
return true;
}
bool _M3CF_startUp(void) {
return _M3CF_unlock() ;
}
IO_INTERFACE _io_m3cf = {
DEVICE_TYPE_M3CF,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
(FN_MEDIUM_STARTUP)&_M3CF_startUp,
(FN_MEDIUM_ISINSERTED)&_M3CF_isInserted,
(FN_MEDIUM_READSECTORS)&_M3CF_readSectors,
(FN_MEDIUM_WRITESECTORS)&_M3CF_writeSectors,
(FN_MEDIUM_CLEARSTATUS)&_M3CF_clearStatus,
(FN_MEDIUM_SHUTDOWN)&_M3CF_shutdown
} ;

45
source/disc_io/io_m3cf.h Normal file
View File

@ -0,0 +1,45 @@
/*
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_io.h"
// export interface
extern IO_INTERFACE _io_m3cf ;
#endif // define IO_M3CF_H

527
source/disc_io/io_m3sd.c Normal file
View File

@ -0,0 +1,527 @@
/*
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.
*/
#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 MAX_STARTUP_TRIES 100 // Arbitrary value, check if the card is ready 100 times before giving up
#define NUM_STARTUP_CLOCKS 100 // Number of empty (0xFF when sending) bytes to send/receive to/from the card
#define TRANSMIT_TIMEOUT 0x100 // 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
//---------------------------------------------------------------
// Variables required for tracking SD state
static u32 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();
}
}
static bool _M3SD_initCard (void) {
int i;
u8 responseBuffer[17]; // sizeof 17 to hold the maximum response size possible
// 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
relativeCardAddress = 0;
for (i = 0; i < MAX_STARTUP_TRIES ; i++) {
_M3SD_sendCommand (APP_CMD, 0);
_M3SD_getResponse_R1 (responseBuffer);
_M3SD_sendCommand (SD_APP_OP_COND, 3<<16);
if ((_M3SD_getResponse_R3 (responseBuffer)) && ((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
_M3SD_sendCommand (ALL_SEND_CID, 0);
_M3SD_getResponse_R2 (responseBuffer);
// Get a new address
_M3SD_sendCommand (SEND_RELATIVE_ADDR, 0);
_M3SD_getResponse_R6 (responseBuffer);
relativeCardAddress = (responseBuffer[1] << 24) | (responseBuffer[2] << 16);
// Some cards won't go to higher speeds unless they think you checked their capabilities
_M3SD_sendCommand (SEND_CSD, relativeCardAddress);
_M3SD_getResponse_R2 (responseBuffer);
// Only this card should respond to all future commands
_M3SD_sendCommand (SELECT_CARD, relativeCardAddress);
_M3SD_getResponse_R1 (responseBuffer);
// Set a 4 bit data bus
_M3SD_sendCommand (APP_CMD, relativeCardAddress);
_M3SD_getResponse_R1 (responseBuffer);
_M3SD_sendCommand (SET_BUS_WIDTH, 2);
_M3SD_getResponse_R1 (responseBuffer);
// Use 512 byte blocks
_M3SD_sendCommand (SET_BLOCKLEN, BYTES_PER_READ);
_M3SD_getResponse_R1 (responseBuffer);
// Wait until card is ready for data
i = 0;
do {
if (i >= RESPONSE_TIMEOUT) {
return false;
}
i++;
_M3SD_sendCommand (SEND_STATUS, relativeCardAddress);
} while ((!_M3SD_getResponse_R1 (responseBuffer)) && ((responseBuffer[3] & 0x1f) != ((SD_STATE_TRAN << 1) | READY_FOR_DATA)));
return true;
}
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;
_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;
while (numSectors--) {
_M3SD_sendCommand (WRITE_BLOCK, offset);
if (!_M3SD_getResponse_R1 (responseBuffer)) {
return false;
}
REG_M3SD_DIR = 0x4;
REG_M3SD_STS = 0x0;
_SD_CRC16 ( data, BYTES_PER_READ, crc);
if (! _M3SD_writeData( data, crc)) {
return false;
}
offset += BYTES_PER_READ;
data += BYTES_PER_READ;
}
return true;
}
bool _M3SD_clearStatus (void) {
return _M3SD_initCard ();
}
bool _M3SD_shutdown (void) {
_M3_changeMode (M3_MODE_ROM);
return true;
}
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
} ;

48
source/disc_io/io_m3sd.h Normal file
View File

@ -0,0 +1,48 @@
/*
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_io.h"
// export interface
extern IO_INTERFACE _io_m3sd ;
#endif // define IO_M3SD_H

350
source/disc_io/io_mmcf.c Normal file
View File

@ -0,0 +1,350 @@
/*
io_mmcf.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_mmcf.h"
#include "io_cf_common.h"
//---------------------------------------------------------------
// DMA
#ifdef _CF_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
// Max Media Player CF Addresses
#define REG_MMP_STS (*(vu16*)0x080E0000) // Status of the CF Card / Device control
#define REG_MMP_CMD (*(vu16*)0x080E0000) // Commands sent to control chip and status return
#define REG_MMP_ERR (*(vu16*)0x08020000) // Errors / Features
#define REG_MMP_SEC (*(vu16*)0x08040000) // Number of sector to transfer
#define REG_MMP_LBA1 (*(vu16*)0x08060000) // 1st byte of sector address
#define REG_MMP_LBA2 (*(vu16*)0x08080000) // 2nd byte of sector address
#define REG_MMP_LBA3 (*(vu16*)0x080A0000) // 3rd byte of sector address
#define REG_MMP_LBA4 (*(vu16*)0x080C0000) // last nibble of sector address | 0xE0
#define MMP_DATA ((vu16*)0x09000000) // Pointer to buffer of CF data transered from card
/*-----------------------------------------------------------------
_MMCF_isInserted
Is a compact flash card inserted?
bool return OUT: true if a CF card is inserted
-----------------------------------------------------------------*/
bool _MMCF_isInserted (void) {
// Change register, then check if value did change
REG_MMP_STS = CF_STS_INSERTED;
return ((REG_MMP_STS & 0xff) == CF_STS_INSERTED);
}
/*-----------------------------------------------------------------
_MMCF_clearStatus
Tries to make the CF card go back to idle mode
bool return OUT: true if a CF card is idle
-----------------------------------------------------------------*/
bool _MMCF_clearStatus (void) {
int i;
// Wait until CF card is finished previous commands
i=0;
while ((REG_MMP_CMD & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
// Wait until card is ready for commands
i = 0;
while ((!(REG_MMP_STS & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
return true;
}
/*-----------------------------------------------------------------
_MMCF_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 _MMCF_readSectors (u32 sector, u32 numSectors, void* buffer) {
int i;
u16 *buff = (u16*)buffer;
#ifdef _CF_ALLOW_UNALIGNED
u8 *buff_u8 = (u8*)buffer;
int temp;
#endif
#if (defined _CF_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 ((REG_MMP_CMD & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
// Wait until card is ready for commands
i = 0;
while ((!(REG_MMP_STS & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Set number of sectors to read
REG_MMP_SEC = (numSectors < 256 ? numSectors : 0); // Read a maximum of 256 sectors, 0 means 256
// Set read sector
REG_MMP_LBA1 = sector & 0xFF; // 1st byte of sector number
REG_MMP_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
REG_MMP_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
REG_MMP_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
// Set command to read
REG_MMP_CMD = CF_CMD_READ;
while (numSectors--)
{
// Wait until card is ready for reading
i = 0;
while (((REG_MMP_STS & 0xff)!= CF_STS_READY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Read data
#ifdef _CF_USE_DMA
#ifdef NDS
DMA3_SRC = (u32)MMP_DATA;
DMA3_DEST = (u32)buff;
DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
#else
DMA3COPY ( MMP_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
#endif
buff += BYTES_PER_READ / 2;
#elif defined _CF_ALLOW_UNALIGNED
i=256;
if ((u32)buff_u8 & 0x01) {
while(i--)
{
temp = *MMP_DATA;
*buff_u8++ = temp & 0xFF;
*buff_u8++ = temp >> 8;
}
} else {
while(i--)
*buff++ = *MMP_DATA;
}
#else
i=256;
while(i--)
*buff++ = *MMP_DATA;
#endif
}
#if (defined _CF_USE_DMA) && (defined NDS)
// Wait for end of transfer before returning
while(DMA3_CR & DMA_BUSY);
#endif
return true;
}
/*-----------------------------------------------------------------
_MMCF_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 _MMCF_writeSectors (u32 sector, u32 numSectors, void* buffer) {
int i;
u16 *buff = (u16*)buffer;
#ifdef _CF_ALLOW_UNALIGNED
u8 *buff_u8 = (u8*)buffer;
int temp;
#endif
#if defined _CF_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 ((REG_MMP_CMD & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
// Wait until card is ready for commands
i = 0;
while ((!(REG_MMP_STS & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Set number of sectors to write
REG_MMP_SEC = (numSectors < 256 ? numSectors : 0); // Write a maximum of 256 sectors, 0 means 256
// Set write sector
REG_MMP_LBA1 = sector & 0xFF; // 1st byte of sector number
REG_MMP_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
REG_MMP_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
REG_MMP_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
// Set command to write
REG_MMP_CMD = CF_CMD_WRITE;
while (numSectors--)
{
// Wait until card is ready for writing
i = 0;
while (((REG_MMP_STS & 0xff) != CF_STS_READY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Write data
#ifdef _CF_USE_DMA
#ifdef NDS
DMA3_SRC = (u32)buff;
DMA3_DEST = (u32)MMP_DATA;
DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
#else
DMA3COPY( buff, MMP_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
#endif
buff += BYTES_PER_READ / 2;
#elif defined _CF_ALLOW_UNALIGNED
i=256;
if ((u32)buff_u8 & 0x01) {
while(i--)
{
temp = *buff_u8++;
temp |= *buff_u8++ << 8;
*MMP_DATA = temp;
}
} else {
while(i--)
*MMP_DATA = *buff++;
}
#else
i=256;
while(i--)
*MMP_DATA = *buff++;
#endif
}
#if defined _CF_USE_DMA && defined NDS
// Wait for end of transfer before returning
while(DMA3_CR & DMA_BUSY);
#endif
return true;
}
/*-----------------------------------------------------------------
_MMCF_Shutdown
unload the GBAMP CF interface
-----------------------------------------------------------------*/
bool _MMCF_shutdown(void) {
return _MMCF_clearStatus() ;
}
/*-----------------------------------------------------------------
_MMCF_startUp
initializes the CF interface, returns true if successful,
otherwise returns false
-----------------------------------------------------------------*/
bool _MMCF_startUp(void) {
// See if there is a read/write register
u16 temp = REG_MMP_LBA1;
REG_MMP_LBA1 = (~temp & 0xFF);
temp = (~temp & 0xFF);
if (!(REG_MMP_LBA1 == temp)) {
return false;
}
// Make sure it is 8 bit
REG_MMP_LBA1 = 0xAA55;
if (REG_MMP_LBA1 == 0xAA55) {
return false;
}
return true;
}
/*-----------------------------------------------------------------
the actual interface structure
-----------------------------------------------------------------*/
IO_INTERFACE _io_mmcf = {
DEVICE_TYPE_MMCF,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
(FN_MEDIUM_STARTUP)&_MMCF_startUp,
(FN_MEDIUM_ISINSERTED)&_MMCF_isInserted,
(FN_MEDIUM_READSECTORS)&_MMCF_readSectors,
(FN_MEDIUM_WRITESECTORS)&_MMCF_writeSectors,
(FN_MEDIUM_CLEARSTATUS)&_MMCF_clearStatus,
(FN_MEDIUM_SHUTDOWN)&_MMCF_shutdown
} ;

45
source/disc_io/io_mmcf.h Normal file
View File

@ -0,0 +1,45 @@
/*
io_mmcf.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_MMCF_H
#define IO_MMCF_H
// 'MMCF'
#define DEVICE_TYPE_MMCF 0x46434D4D
#include "disc_io.h"
// export interface
extern IO_INTERFACE _io_mmcf ;
#endif // define IO_MMCF_H

348
source/disc_io/io_mpcf.c Normal file
View File

@ -0,0 +1,348 @@
/*
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 _CF_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
// 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 MP_DATA ((vu16*)0x09000000) // Pointer to buffer of CF data transered from card
/*-----------------------------------------------------------------
_MPCF_isInserted
Is a compact flash card inserted?
bool return OUT: true if a CF card is inserted
-----------------------------------------------------------------*/
bool _MPCF_isInserted (void) {
// Change register, then check if value did change
REG_MPCF_STS = CF_STS_INSERTED;
return ((REG_MPCF_STS & 0xff) == CF_STS_INSERTED);
}
/*-----------------------------------------------------------------
_MPCF_clearStatus
Tries to make the CF card go back to idle mode
bool return OUT: true if a CF card is idle
-----------------------------------------------------------------*/
bool _MPCF_clearStatus (void) {
int i;
// Wait until CF card is finished previous commands
i=0;
while ((REG_MPCF_CMD & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
// Wait until card is ready for commands
i = 0;
while ((!(REG_MPCF_STS & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
return true;
}
/*-----------------------------------------------------------------
_MPCF_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 _MPCF_readSectors (u32 sector, u32 numSectors, void* buffer) {
int i;
u16 *buff = (u16*)buffer;
#ifdef _CF_ALLOW_UNALIGNED
u8 *buff_u8 = (u8*)buffer;
int temp;
#endif
#if (defined _CF_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 ((REG_MPCF_CMD & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT)) {
i++;
}
// Wait until card is ready for commands
i = 0;
while ((!(REG_MPCF_STS & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT)) {
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Set number of sectors to read
REG_MPCF_SEC = (numSectors < 256 ? numSectors : 0); // Read a maximum of 256 sectors, 0 means 256
// Set read sector
REG_MPCF_LBA1 = sector & 0xFF; // 1st byte of sector number
REG_MPCF_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
REG_MPCF_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
REG_MPCF_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
// Set command to read
REG_MPCF_CMD = CF_CMD_READ;
while (numSectors--)
{
// Wait until card is ready for reading
i = 0;
while (((REG_MPCF_STS & 0xff)!= CF_STS_READY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Read data
#ifdef _CF_USE_DMA
#ifdef NDS
DMA3_SRC = (u32)MP_DATA;
DMA3_DEST = (u32)buff;
DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
#else
DMA3COPY ( MP_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
#endif
buff += BYTES_PER_READ / 2;
#elif defined _CF_ALLOW_UNALIGNED
i=256;
if ((u32)buff_u8 & 0x01) {
while(i--)
{
temp = *MP_DATA;
*buff_u8++ = temp & 0xFF;
*buff_u8++ = temp >> 8;
}
} else {
while(i--)
*buff++ = *MP_DATA;
}
#else
i=256;
while(i--)
*buff++ = *MP_DATA;
#endif
}
#if (defined _CF_USE_DMA) && (defined NDS)
// Wait for end of transfer before returning
while(DMA3_CR & DMA_BUSY);
#endif
return true;
}
/*-----------------------------------------------------------------
_MPCF_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 _MPCF_writeSectors (u32 sector, u32 numSectors, void* buffer) {
int i;
u16 *buff = (u16*)buffer;
#ifdef _CF_ALLOW_UNALIGNED
u8 *buff_u8 = (u8*)buffer;
int temp;
#endif
#if defined _CF_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 ((REG_MPCF_CMD & CF_STS_BUSY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
// Wait until card is ready for commands
i = 0;
while ((!(REG_MPCF_STS & CF_STS_INSERTED)) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Set number of sectors to write
REG_MPCF_SEC = (numSectors < 256 ? numSectors : 0); // Write a maximum of 256 sectors, 0 means 256
// Set write sector
REG_MPCF_LBA1 = sector & 0xFF; // 1st byte of sector number
REG_MPCF_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
REG_MPCF_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
REG_MPCF_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
// Set command to write
REG_MPCF_CMD = CF_CMD_WRITE;
while (numSectors--)
{
// Wait until card is ready for writing
i = 0;
while (((REG_MPCF_STS & 0xff) != CF_STS_READY) && (i < CF_CARD_TIMEOUT))
{
i++;
}
if (i >= CF_CARD_TIMEOUT)
return false;
// Write data
#ifdef _CF_USE_DMA
#ifdef NDS
DMA3_SRC = (u32)buff;
DMA3_DEST = (u32)MP_DATA;
DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
#else
DMA3COPY( buff, MP_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
#endif
buff += BYTES_PER_READ / 2;
#elif defined _CF_ALLOW_UNALIGNED
i=256;
if ((u32)buff_u8 & 0x01) {
while(i--)
{
temp = *buff_u8++;
temp |= *buff_u8++ << 8;
*MP_DATA = temp;
}
} else {
while(i--)
*MP_DATA = *buff++;
}
#else
i=256;
while(i--)
*MP_DATA = *buff++;
#endif
}
#if defined _CF_USE_DMA && defined NDS
// Wait for end of transfer before returning
while(DMA3_CR & DMA_BUSY);
#endif
return true;
}
/*-----------------------------------------------------------------
_MPCF_Shutdown
unload the GBAMP CF interface
-----------------------------------------------------------------*/
bool _MPCF_shutdown(void) {
return _MPCF_clearStatus() ;
}
/*-----------------------------------------------------------------
_MPCF_startUp
initializes the CF interface, returns true if successful,
otherwise returns false
-----------------------------------------------------------------*/
bool _MPCF_startUp(void) {
// See if there is a read/write register
u16 temp = REG_MPCF_LBA1;
REG_MPCF_LBA1 = (~temp & 0xFF);
temp = (~temp & 0xFF);
if (!(REG_MPCF_LBA1 == temp)) {
return false;
}
// Make sure it is 8 bit
REG_MPCF_LBA1 = 0xAA55;
if (REG_MPCF_LBA1 == 0xAA55) {
return false;
}
return true;
}
/*-----------------------------------------------------------------
the actual interface structure
-----------------------------------------------------------------*/
IO_INTERFACE _io_mpcf = {
DEVICE_TYPE_MPCF,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
(FN_MEDIUM_STARTUP)&_MPCF_startUp,
(FN_MEDIUM_ISINSERTED)&_MPCF_isInserted,
(FN_MEDIUM_READSECTORS)&_MPCF_readSectors,
(FN_MEDIUM_WRITESECTORS)&_MPCF_writeSectors,
(FN_MEDIUM_CLEARSTATUS)&_MPCF_clearStatus,
(FN_MEDIUM_SHUTDOWN)&_MPCF_shutdown
} ;

45
source/disc_io/io_mpcf.h Normal file
View File

@ -0,0 +1,45 @@
/*
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_io.h"
// export interface
extern IO_INTERFACE _io_mpcf ;
#endif // define IO_MPCF_H

348
source/disc_io/io_nmmc.c Normal file
View File

@ -0,0 +1,348 @@
/*
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 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(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(spi_freq);
if(enable) _Neo_SPI(0x06);
else _Neo_SPI(0x0E);
_Neo_CloseSPI();
}
static void _Neo_WriteMK2Config(u8 config) {
_Neo_EnableEEPROM(true);
_Neo_OpenSPI(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(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 (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) {
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;
}
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

53
source/disc_io/io_nmmc.h Normal file
View File

@ -0,0 +1,53 @@
/*
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_io.h"
#ifdef NDS
// 'NMMC'
#define DEVICE_TYPE_NMMC 0x434D4D4E
// export interface
extern IO_INTERFACE _io_nmmc;
#endif // defined NDS
#endif // define IO_NMMC_H

View File

@ -0,0 +1,47 @@
/*
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 ;
}

View File

@ -0,0 +1,45 @@
/*
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
*/
#ifndef IO_SC_COMMON_H
#define IO_SC_COMMON_H
#include "disc_io.h"
// Values for changing mode
#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

87
source/disc_io/io_sccf.c Normal file
View File

@ -0,0 +1,87 @@
/*
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"
/*-----------------------------------------------------------------
Since all CF addresses and commands are the same for the GBAMP,
simply use it's functions instead.
-----------------------------------------------------------------*/
extern bool _MPCF_isInserted (void);
extern bool _MPCF_clearStatus (void);
extern bool _MPCF_readSectors (u32 sector, u32 numSectors, void* buffer);
extern bool _MPCF_writeSectors (u32 sector, u32 numSectors, void* buffer);
/*-----------------------------------------------------------------
_SCCF_unlock
Returns true if SuperCard was unlocked, false if failed
Added by MightyMax
Modified by Chishm
-----------------------------------------------------------------*/
bool _SCCF_unlock(void) {
#define CF_REG_LBA1 (*(vu16*)0x09060000)
unsigned char temp;
_SC_changeMode (SC_MODE_MEDIA);
// provoke a ready reply
temp = CF_REG_LBA1;
CF_REG_LBA1 = (~temp & 0xFF);
temp = (~temp & 0xFF);
return (CF_REG_LBA1 == temp);
#undef CF_REG_LBA1
}
bool _SCCF_shutdown(void) {
return _MPCF_clearStatus() ;
} ;
bool _SCCF_startUp(void) {
return _SCCF_unlock() ;
} ;
IO_INTERFACE _io_sccf = {
DEVICE_TYPE_SCCF,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
(FN_MEDIUM_STARTUP)&_SCCF_startUp,
(FN_MEDIUM_ISINSERTED)&_MPCF_isInserted,
(FN_MEDIUM_READSECTORS)&_MPCF_readSectors,
(FN_MEDIUM_WRITESECTORS)&_MPCF_writeSectors,
(FN_MEDIUM_CLEARSTATUS)&_MPCF_clearStatus,
(FN_MEDIUM_SHUTDOWN)&_SCCF_shutdown
} ;

45
source/disc_io/io_sccf.h Normal file
View File

@ -0,0 +1,45 @@
/*
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_io.h"
// export interface
extern IO_INTERFACE _io_sccf;
#endif // define IO_SCCF_H

476
source/disc_io/io_scsd.c Normal file
View File

@ -0,0 +1,476 @@
/*
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.
*/
#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_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 MAX_STARTUP_TRIES 100 // Arbitrary value, check if the card is ready 100 times before giving up
#define NUM_STARTUP_CLOCKS 100 // Number of empty (0xFF when sending) bytes to send/receive to/from the card
#define TRANSMIT_TIMEOUT 0x100 // 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 BUSY_WAIT_TIMEOUT 500000
//---------------------------------------------------------------
// Variables required for tracking SD state
static u32 relativeCardAddress = 0; // Preshifted Relative Card Address
//---------------------------------------------------------------
// Internal M3 SD functions
static inline void _SCSD_unlock (void) {
_SC_changeMode (SC_MODE_MEDIA);
}
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;
do {
dataByte = *tempDataPtr++;
for (curBit = 7; curBit >=0; curBit--){
REG_SCSD_CMD = dataByte;
dataByte = dataByte << 1;
}
} while (length--);
return true;
}
static u8 _SCSD_getByte (void) {
// With every 16 bit read to REG_SCSD_CMD, read a single bit.
u32 res = 0;
int i;
for (i = 1; i < 8; i++) {
res = (res << 1) | REG_SCSD_CMD;
}
return (u8)res;
}
// Returns the response from the SD card to a previous command.
static bool _SCSD_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 = _SCSD_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 = _SCSD_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 = _SCSD_getByte();
i = _SCSD_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 _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--);
}
static bool _SCSD_initCard (void) {
//iprintf ("init card\n");
int i;
u8 responseBuffer[17]; // sizeof 17 to hold the maximum response size possible
// Give the card time to stabilise
_SCSD_sendClocks (NUM_STARTUP_CLOCKS);
// Reset the card
if (!_SCSD_sendCommand (GO_IDLE_STATE, 0)) {
//iprintf ("can't idle\n");
return false;
}
_SCSD_sendClocks (NUM_STARTUP_CLOCKS);
// Card is now reset, including it's address
relativeCardAddress = 0;
for (i = 0; i < MAX_STARTUP_TRIES ; i++) {
_SCSD_sendCommand (APP_CMD, 0);
_SCSD_getResponse_R1 (responseBuffer);
_SCSD_sendCommand (SD_APP_OP_COND, 3<<16);
if ((_SCSD_getResponse_R3 (responseBuffer)) && ((responseBuffer[1] & 0x80) != 0)) {
// Card is ready to receive commands now
break;
}
}
if (i >= MAX_STARTUP_TRIES) {
//iprintf ("timeout on OP_COND\n");
return false;
}
// The card's name, as assigned by the manufacturer
_SCSD_sendCommand (ALL_SEND_CID, 0);
_SCSD_getResponse_R2 (responseBuffer);
// Get a new address
_SCSD_sendCommand (SEND_RELATIVE_ADDR, 0);
_SCSD_getResponse_R6 (responseBuffer);
relativeCardAddress = (responseBuffer[1] << 24) | (responseBuffer[2] << 16);
//iprintf ("Relative Address: %08X\n", relativeCardAddress);
// Some cards won't go to higher speeds unless they think you checked their capabilities
_SCSD_sendCommand (SEND_CSD, relativeCardAddress);
_SCSD_getResponse_R2 (responseBuffer);
// Only this card should respond to all future commands
_SCSD_sendCommand (SELECT_CARD, relativeCardAddress);
_SCSD_getResponse_R1 (responseBuffer);
// Set a 4 bit data bus
_SCSD_sendCommand (APP_CMD, relativeCardAddress);
_SCSD_getResponse_R1 (responseBuffer);
_SCSD_sendCommand (SET_BUS_WIDTH, 2);
_SCSD_getResponse_R1 (responseBuffer);
// Use 512 byte blocks
_SCSD_sendCommand (SET_BLOCKLEN, BYTES_PER_READ);
_SCSD_getResponse_R1 (responseBuffer);
// Wait until card is ready for data
i = 0;
do {
if (i >= RESPONSE_TIMEOUT) {
//iprintf ("timeout on SEND_STATUS\n");
return false;
}
i++;
_SCSD_sendCommand (SEND_STATUS, relativeCardAddress);
} while ((!_SCSD_getResponse_R1 (responseBuffer)) && ((responseBuffer[3] & 0x1f) != ((SD_STATE_TRAN << 1) | READY_FOR_DATA)));
return true;
}
static bool _SCSD_readData (void* buffer) {
u8* buff_u8 = (u8*)buffer;
u16* buff = (u16*)buffer;
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++ = (u16)temp;
}
for (i = 0; i < 8; i++) {
temp = REG_SCSD_DATAREAD_32;
}
temp = REG_SCSD_DATAREAD;
return true;
}
static bool _SCSD_writeData (u8* data, u8* crc) {
int pos;
u16 dataHWord;
u16 temp;
while ((REG_SCSD_DATAREAD & SCSD_STS_BUSY) == 0);
temp = REG_SCSD_DATAREAD;
REG_SCSD_DATAWRITE = 0; // start bit;
pos = BYTES_PER_READ / 2;
while (pos--) {
dataHWord = data[0] | (data[1] << 8);
data+=2;
REG_SCSD_DATAWRITE = dataHWord;
REG_SCSD_DATAWRITE = dataHWord << 4;
REG_SCSD_DATAWRITE = dataHWord << 8;
REG_SCSD_DATAWRITE = dataHWord << 12;
}
if (crc != 0) {
pos = 4;
while (pos--) {
dataHWord = *crc++;
REG_SCSD_DATAWRITE = dataHWord;
REG_SCSD_DATAWRITE = dataHWord << 4;
REG_SCSD_DATAWRITE = dataHWord << 8;
REG_SCSD_DATAWRITE = dataHWord << 12;
}
}
REG_SCSD_DATAWRITE = 0xff; // end bit
while ((REG_SCSD_DATAREAD & SCSD_STS_BUSY) == 0);
temp = REG_SCSD_DATAREAD;
temp = REG_SCSD_DATAREAD;
temp = REG_SCSD_DATAREAD;
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 crcBuff[4];
u8* crc = (u8*)crcBuff; // Force crcBuff to be halfword aligned
u8 responseBuffer[6];
u32 offset = sector * BYTES_PER_READ;
u8* data = (u8*) buffer;
while (numSectors--) {
// Send write command and get a response
_SCSD_sendCommand (WRITE_BLOCK, offset);
if (!_SCSD_getResponse_R1 (responseBuffer)) {
return false;
}
// Send the data and CRC
_SD_CRC16 ( data, BYTES_PER_READ, crc);
if (! _SCSD_writeData( data, crc)) {
return false;
}
// Send a few clocks to the SD card
_SCSD_sendClocks(0x10);
offset += BYTES_PER_READ;
data += BYTES_PER_READ;
}
return true;
}
bool _SCSD_clearStatus (void) {
return _SCSD_initCard ();
}
bool _SCSD_shutdown (void) {
_SC_changeMode (SC_MODE_RAM_RO);
return true;
}
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
} ;

45
source/disc_io/io_scsd.h Normal file
View File

@ -0,0 +1,45 @@
/*
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
*/
#ifndef IO_SCSD_H
#define IO_SCSD_H
// 'SCSD'
#define DEVICE_TYPE_SCSD 0x44534353
#include "disc_io.h"
// export interface
extern IO_INTERFACE _io_scsd ;
#endif // define IO_SCSD_H

View File

@ -0,0 +1,124 @@
/*
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.
*/
#include "io_sd_common.h"
/*
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;
}

View File

@ -0,0 +1,92 @@
/*
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
*/
#ifndef IO_SD_COMMON_H
#define IO_SD_COMMON_H
#include "disc_io.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 APP_CMD 55
/* SD App commands */
#define SET_BUS_WIDTH 6
#define SD_APP_OP_COND 41
/* 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);
#endif // define IO_SD_COMMON_H

964
source/fatfile.c Normal file
View File

@ -0,0 +1,964 @@
/*
fatfile.c
Functions used by the newlib disc stubs to interface with
this library
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
*/
#include "fatfile.h"
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include "cache.h"
#include "file_allocation_table.h"
#include "bit_ops.h"
#include "filetime.h"
int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
PARTITION* partition = NULL;
bool fileExists;
DIR_ENTRY dirEntry;
const char* pathEnd;
u32 dirCluster;
FILE_STRUCT* file = (FILE_STRUCT*) fileStruct;
partition = _FAT_partition_getPartitionFromPath (path);
if (partition == NULL) {
r->_errno = ENODEV;
return -1;
}
// Move the path pointer to the start of the actual path
if (strchr (path, ':') != NULL) {
path = strchr (path, ':') + 1;
}
if (strchr (path, ':') != NULL) {
r->_errno = EINVAL;
return -1;
}
// Determine which mode the file is openned for
if ((flags & 0x03) == O_RDONLY) {
// Open the file for read-only access
file->read = true;
file->write = false;
file->append = false;
} else if ((flags & 0x03) == O_WRONLY) {
// Open file for write only access
file->read = false;
file->write = true;
file->append = false;
} else if ((flags & 0x03) == O_RDWR) {
// Open file for read/write access
file->read = true;
file->write = true;
file->append = false;
} else {
r->_errno = EACCES;
return -1;
}
// Make sure we aren't trying to write to a read-only disc
if (file->write && partition->readOnly) {
r->_errno = EROFS;
return -1;
}
// Search for the file on the disc
fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
// The file shouldn't exist if we are trying to create it
if ((flags & O_CREAT) && (flags & O_EXCL) && fileExists) {
r->_errno = EEXIST;
return -1;
}
// It should not be a directory if we're openning a file,
if (fileExists && _FAT_directory_isDirectory(&dirEntry)) {
r->_errno = EISDIR;
return -1;
}
// If the file doesn't exist, create it if we're allowed to
if (!fileExists) {
if (flags & O_CREAT) {
if (partition->readOnly) {
// We can't write to a read-only partition
r->_errno = EROFS;
return -1;
}
// Create the file
// Get the directory it has to go in
pathEnd = strrchr (path, DIR_SEPARATOR);
if (pathEnd == NULL) {
// No path was specified
dirCluster = partition->cwdCluster;
pathEnd = path;
} else {
// Path was specified -- get the right dirCluster
// Recycling dirEntry, since it needs to be recreated anyway
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
!_FAT_directory_isDirectory(&dirEntry)) {
r->_errno = ENOTDIR;
return -1;
}
dirCluster = _FAT_directory_entryGetCluster (dirEntry.entryData);
// Move the pathEnd past the last DIR_SEPARATOR
pathEnd += 1;
}
// Create the entry data
strncpy (dirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1);
memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE);
// Set the creation time and date
dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0;
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC());
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC());
if (!_FAT_directory_addEntry (partition, &dirEntry, dirCluster)) {
r->_errno = ENOSPC;
return -1;
}
} else {
// file doesn't exist, and we aren't creating it
r->_errno = ENOENT;
return -1;
}
}
file->filesize = u8array_to_u32 (dirEntry.entryData, DIR_ENTRY_fileSize);
/* Allow LARGEFILEs with undefined results
// Make sure that the file size can fit in the available space
if (!(flags & O_LARGEFILE) && (file->filesize >= (1<<31))) {
r->_errno = EFBIG;
return -1;
}
*/
// Make sure we aren't trying to write to a read-only file
if (file->write && !_FAT_directory_isWritable(&dirEntry)) {
r->_errno = EROFS;
return -1;
}
// Associate this file with a particular partition
file->partition = partition;
file->startCluster = _FAT_directory_entryGetCluster (dirEntry.entryData);
// Truncate the file if requested
if ((flags & O_TRUNC) && file->write && (file->startCluster != 0)) {
_FAT_fat_clearLinks (partition, file->startCluster);
file->startCluster = 0;
file->filesize = 0;
}
// Get a new cluster for the file if required
if (file->startCluster == 0) {
file->startCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE);
}
// Remember the position of this file's directory entry
file->dirEntryStart = dirEntry.dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
file->dirEntryEnd = dirEntry.dataEnd;
file->currentPosition = 0;
file->rwPosition.cluster = file->startCluster;
file->rwPosition.sector = 0;
file->rwPosition.byte = 0;
file->appendPosition.cluster = _FAT_fat_lastCluster (partition, file->startCluster);
file->appendPosition.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ;
file->appendPosition.byte = file->filesize % BYTES_PER_READ;
// Check if the end of the file is on the end of a cluster
if ( (file->filesize > 0) && ((file->filesize % partition->bytesPerCluster)==0) ){
// Set flag to allocate a new cluster
file->appendPosition.sector = partition->sectorsPerCluster;
file->appendPosition.byte = 0;
}
if (flags & O_APPEND) {
file->append = true;
}
file->inUse = true;
partition->openFileCount += 1;
return (int) file;
}
int _FAT_close_r (struct _reent *r, int fd) {
FILE_STRUCT* file = (FILE_STRUCT*) fd;
u8 dirEntryData[DIR_ENTRY_DATA_SIZE];
if (!file->inUse) {
r->_errno = EBADF;
return -1;
}
if (file->write) {
// Load the old entry
_FAT_cache_readPartialSector (file->partition->cache, dirEntryData,
_FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
// Write new data to the directory entry
// File size
u32_to_u8array (dirEntryData, DIR_ENTRY_fileSize, file->filesize);
// Start cluster
u16_to_u8array (dirEntryData, DIR_ENTRY_cluster, file->startCluster);
u16_to_u8array (dirEntryData, DIR_ENTRY_clusterHigh, file->startCluster >> 16);
// Modification time and date
u16_to_u8array (dirEntryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC());
u16_to_u8array (dirEntryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC());
// Access date
u16_to_u8array (dirEntryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC());
// Write the new entry
_FAT_cache_writePartialSector (file->partition->cache, dirEntryData,
_FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
// Flush any sectors in the disc cache
if (!_FAT_cache_flush(file->partition->cache)) {
r->_errno = EIO;
return -1;
}
}
file->inUse = false;
file->partition->openFileCount -= 1;
return 0;
}
int _FAT_read_r (struct _reent *r, int fd, char *ptr, int len) {
FILE_STRUCT* file = (FILE_STRUCT*) fd;
PARTITION* partition;
CACHE* cache;
FILE_POSITION position;
u32 tempNextCluster;
int tempVar;
u32 remain;
bool flagNoError = true;
// Make sure we can actually read from the file
if ((file == NULL) || !file->inUse || !file->read) {
r->_errno = EBADF;
return 0;
}
// Don't try to read if the read pointer is past the end of file
if (file->currentPosition >= file->filesize) {
return 0;
}
// Don't read past end of file
if (len + file->currentPosition > file->filesize) {
r->_errno = EOVERFLOW;
len = file->filesize - file->currentPosition;
}
remain = len;
position = file->rwPosition;
partition = file->partition;
cache = file->partition->cache;
// Align to sector
tempVar = BYTES_PER_READ - position.byte;
if (tempVar > remain) {
tempVar = remain;
}
if ((tempVar < BYTES_PER_READ) && flagNoError)
{
_FAT_cache_readPartialSector ( cache, ptr, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector,
position.byte, tempVar);
remain -= tempVar;
ptr += tempVar;
position.byte += tempVar;
if (position.byte >= BYTES_PER_READ) {
position.byte = 0;
position.sector++;
}
}
// align to cluster
// tempVar is number of sectors to read
if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) {
tempVar = partition->sectorsPerCluster - position.sector;
} else {
tempVar = remain / BYTES_PER_READ;
}
if ((tempVar > 0) && flagNoError) {
_FAT_disc_readSectors (partition->disc, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector,
tempVar, ptr);
ptr += tempVar * BYTES_PER_READ;
remain -= tempVar * BYTES_PER_READ;
position.sector += tempVar;
}
// Move onto next cluster
// It should get to here without reading anything if a cluster is due to be allocated
if (position.sector >= partition->sectorsPerCluster) {
tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) {
position.sector = partition->sectorsPerCluster;
} else if (tempNextCluster == CLUSTER_FREE) {
r->_errno = EIO;
flagNoError = false;
} else {
position.sector = 0;
position.cluster = tempNextCluster;
}
}
// Read in whole clusters
while ((remain >= partition->bytesPerCluster) && flagNoError) {
_FAT_disc_readSectors (partition->disc, _FAT_fat_clusterToSector (partition, position.cluster), partition->sectorsPerCluster, ptr);
ptr += partition->bytesPerCluster;
remain -= partition->bytesPerCluster;
// Advance to next cluster
tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) {
position.sector = partition->sectorsPerCluster;
} else if (tempNextCluster == CLUSTER_FREE) {
r->_errno = EIO;
flagNoError = false;
} else {
position.sector = 0;
position.cluster = tempNextCluster;
}
}
// Read remaining sectors
tempVar = remain / BYTES_PER_READ; // Number of sectors left
if ((tempVar > 0) && flagNoError) {
_FAT_disc_readSectors (partition->disc, _FAT_fat_clusterToSector (partition, position.cluster),
tempVar, ptr);
ptr += tempVar * BYTES_PER_READ;
remain -= tempVar * BYTES_PER_READ;
position.sector += tempVar;
}
// Last remaining sector
// Check if anything is left
if ((remain > 0) && flagNoError) {
_FAT_cache_readPartialSector ( cache, ptr,
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
position.byte += remain;
remain = 0;
}
// Length read is the wanted length minus the stuff not read
len = len - remain;
// Update file information
file->rwPosition = position;
file->currentPosition += len;
return len;
}
/*
Extend a file so that the size is the same as the rwPosition
*/
static bool file_extend_r (struct _reent *r, FILE_STRUCT* file) {
PARTITION* partition = file->partition;
CACHE* cache = file->partition->cache;
FILE_POSITION position;
u32 remain;
u8 zeroBuffer [BYTES_PER_READ] = {0};
u32 tempNextCluster;
position.byte = file->filesize % BYTES_PER_READ;
position.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ;
position.cluster = _FAT_fat_lastCluster (partition, file->startCluster);
remain = file->currentPosition - file->filesize;
// Only need to clear to the end of the sector
if (remain + position.byte < BYTES_PER_READ) {
_FAT_cache_writePartialSector (cache, zeroBuffer,
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, remain);
position.byte += remain;
} else {
if (position.byte > 0) {
_FAT_cache_writePartialSector (cache, zeroBuffer,
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte,
BYTES_PER_READ - position.byte);
remain -= (BYTES_PER_READ - position.byte);
position.byte = 0;
position.sector ++;
}
while (remain >= BYTES_PER_READ) {
if (position.sector >= partition->sectorsPerCluster) {
position.sector = 0;
tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) {
// Ran out of clusters so get a new one
tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
}
if (tempNextCluster == CLUSTER_FREE) {
// Couldn't get a cluster, so abort
r->_errno = ENOSPC;
return false;
} else {
position.cluster = tempNextCluster;
}
}
_FAT_disc_writeSectors (partition->disc,
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 1, zeroBuffer);
remain -= BYTES_PER_READ;
position.sector ++;
}
if (position.sector >= partition->sectorsPerCluster) {
position.sector = 0;
tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) {
// Ran out of clusters so get a new one
tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
}
if (tempNextCluster == CLUSTER_FREE) {
// Couldn't get a cluster, so abort
r->_errno = ENOSPC;
return false;
} else {
position.cluster = tempNextCluster;
}
}
if (remain > 0) {
_FAT_cache_writePartialSector (cache, zeroBuffer,
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
position.byte = remain;
}
}
file->rwPosition = position;
file->filesize = file->currentPosition;
return true;
}
int _FAT_write_r (struct _reent *r,int fd, const char *ptr, int len) {
FILE_STRUCT* file = (FILE_STRUCT*) fd;
PARTITION* partition;
CACHE* cache;
FILE_POSITION position;
u32 tempNextCluster;
int tempVar;
u32 remain;
bool flagNoError = true;
bool flagAppending = false;
// Make sure we can actually write to the file
if ((file == NULL) || !file->inUse || !file->write) {
r->_errno = EBADF;
return -1;
}
partition = file->partition;
cache = file->partition->cache;
remain = len;
if (file->append) {
position = file->appendPosition;
flagAppending = true;
} else {
// If the write pointer is past the end of the file, extend the file to that size
if (file->currentPosition > file->filesize) {
if (!file_extend_r (r, file)) {
return 0;
}
}
// Write at current read pointer
position = file->rwPosition;
// If it is writing past the current end of file, set appending flag
if (len + file->currentPosition > file->filesize) {
flagAppending = true;
}
}
// Move onto next cluster if needed
if (position.sector >= partition->sectorsPerCluster) {
position.sector = 0;
tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) {
// Ran out of clusters so get a new one
tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
}
if (tempNextCluster == CLUSTER_FREE) {
// Couldn't get a cluster, so abort
r->_errno = ENOSPC;
flagNoError = false;
} else {
position.cluster = tempNextCluster;
}
}
// Align to sector
tempVar = BYTES_PER_READ - position.byte;
if (tempVar > remain) {
tempVar = remain;
}
if ((tempVar < BYTES_PER_READ) && flagNoError) {
// Write partial sector to disk
_FAT_cache_writePartialSector (cache, ptr,
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, tempVar);
remain -= tempVar;
ptr += tempVar;
position.byte += tempVar;
// Move onto next sector
if (position.byte >= BYTES_PER_READ) {
position.byte = 0;
position.sector ++;
}
}
// Align to cluster
// tempVar is number of sectors to write
if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) {
tempVar = partition->sectorsPerCluster - position.sector;
} else {
tempVar = remain / BYTES_PER_READ;
}
if ((tempVar > 0) && flagNoError) {
_FAT_disc_writeSectors (partition->disc,
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, tempVar, ptr);
ptr += tempVar * BYTES_PER_READ;
remain -= tempVar * BYTES_PER_READ;
position.sector += tempVar;
}
if ((position.sector >= partition->sectorsPerCluster) && flagNoError && (remain > 0)) {
position.sector = 0;
tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) {
// Ran out of clusters so get a new one
tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
}
if (tempNextCluster == CLUSTER_FREE) {
// Couldn't get a cluster, so abort
r->_errno = ENOSPC;
flagNoError = false;
} else {
position.cluster = tempNextCluster;
}
}
// Write whole clusters
while ((remain >= partition->bytesPerCluster) && flagNoError) {
_FAT_disc_writeSectors (partition->disc, _FAT_fat_clusterToSector(partition, position.cluster),
partition->sectorsPerCluster, ptr);
ptr += partition->bytesPerCluster;
remain -= partition->bytesPerCluster;
if (remain > 0) {
tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) {
// Ran out of clusters so get a new one
tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
}
if (tempNextCluster == CLUSTER_FREE) {
// Couldn't get a cluster, so abort
r->_errno = ENOSPC;
flagNoError = false;
} else {
position.cluster = tempNextCluster;
}
} else {
// Allocate a new cluster when next writing the file
position.sector = partition->sectorsPerCluster;
}
}
// Write remaining sectors
tempVar = remain / BYTES_PER_READ; // Number of sectors left
if ((tempVar > 0) && flagNoError) {
_FAT_disc_writeSectors (partition->disc, _FAT_fat_clusterToSector (partition, position.cluster),
tempVar, ptr);
ptr += tempVar * BYTES_PER_READ;
remain -= tempVar * BYTES_PER_READ;
position.sector += tempVar;
}
// Last remaining sector
if ((remain > 0) && flagNoError) {
if (flagAppending) {
_FAT_cache_eraseWritePartialSector ( cache, ptr,
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
} else {
_FAT_cache_writePartialSector ( cache, ptr,
_FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
}
position.byte += remain;
remain = 0;
}
// Amount read is the originally requested amount minus stuff remaining
len = len - remain;
// Update file information
if (file->append) {
// Appending doesn't affect the read pointer
file->appendPosition = position;
file->filesize += len;
} else {
// Writing also shifts the read pointer
file->rwPosition = position;
file->currentPosition += len;
if (file->filesize < file->currentPosition) {
file->filesize = file->currentPosition;
}
}
return len;
}
int _FAT_seek_r (struct _reent *r, int fd, int pos, int dir) {
FILE_STRUCT* file = (FILE_STRUCT*) fd;
PARTITION* partition;
u32 cluster, nextCluster;
int clusCount;
int position;
if ((file == NULL) || (file->inUse == false)) {
// invalid file
r->_errno = EBADF;
return -1;
}
partition = file->partition;
switch (dir) {
case SEEK_SET:
position = pos;
break;
case SEEK_CUR:
position = file->currentPosition + pos;
break;
case SEEK_END:
position = file->filesize + pos;
break;
default:
r->_errno = EINVAL;
return -1;
}
if ((pos > 0) && (position < 0)) {
r->_errno = EOVERFLOW;
return -1;
}
if (position < 0) {
r->_errno = EINVAL;
return -1;
}
if (position <= file->filesize) {
// Calculate the sector and byte of the current position,
// and store them
file->rwPosition.sector = (position % partition->bytesPerCluster) / BYTES_PER_READ;
file->rwPosition.byte = position % BYTES_PER_READ;
// Calculate where the correct cluster is
if (position > file->currentPosition) {
clusCount = (position - file->currentPosition
+ (file->rwPosition.sector * partition->bytesPerSector)
+ file->rwPosition.byte) / partition->bytesPerCluster; // Fixed thanks to AgentQ
cluster = file->rwPosition.cluster;
} else {
clusCount = position / partition->bytesPerCluster;
cluster = file->startCluster;
}
// Follow cluster list until desired one is found
if (clusCount > 0) {
// Only look at next cluster if need to
nextCluster = _FAT_fat_nextCluster (partition, cluster);
while ((clusCount--) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) {
cluster = nextCluster;
nextCluster = _FAT_fat_nextCluster (partition, cluster);
}
} else {
nextCluster = cluster;
}
// Check if ran out of clusters, and the file is being written to
if ((clusCount > 0) && (file->write || file->append)) {
// Set flag to allocate a new cluster
file->rwPosition.sector = partition->sectorsPerCluster;
file->rwPosition.byte = 0;
}
file->rwPosition.cluster = cluster;
}
// Save position
file->currentPosition = position;
return position;
}
int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st) {
FILE_STRUCT* file = (FILE_STRUCT*) fd;
PARTITION* partition;
DIR_ENTRY fileEntry;
if ((file == NULL) || (file->inUse == false)) {
// invalid file
r->_errno = EBADF;
return -1;
}
partition = file->partition;
// Get the file's entry data
fileEntry.dataStart = file->dirEntryStart;
fileEntry.dataEnd = file->dirEntryEnd;
if (!_FAT_directory_entryFromPosition (partition, &fileEntry)) {
r->_errno = EIO;
return -1;
}
// Fill in the stat struct
_FAT_directory_entryStat (partition, &fileEntry, st);
// Fix stats that have changed since the file was openned
st->st_ino = (ino_t)(file->startCluster); // The file serial number is the start cluster
st->st_size = file->filesize; // File size
return 0;
}
int _FAT_stat_r (struct _reent *r,const char *path, struct stat *st) {
PARTITION* partition = NULL;
DIR_ENTRY dirEntry;
// Get the partition this file is on
partition = _FAT_partition_getPartitionFromPath (path);
if (partition == NULL) {
r->_errno = ENODEV;
return -1;
}
// Move the path pointer to the start of the actual path
if (strchr (path, ':') != NULL) {
path = strchr (path, ':') + 1;
}
if (strchr (path, ':') != NULL) {
r->_errno = EINVAL;
return -1;
}
// Search for the file on the disc
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
r->_errno = ENOENT;
return -1;
}
// Fill in the stat struct
_FAT_directory_entryStat (partition, &dirEntry, st);
return 0;
}
int _FAT_link_r (struct _reent *r, char *existing, char *newLink) {
r->_errno = ENOTSUP;
return -1;
}
int _FAT_unlink_r (struct _reent *r, char *path) {
PARTITION* partition = NULL;
DIR_ENTRY dirEntry;
DIR_ENTRY dirContents;
u32 cluster;
bool nextEntry;
bool errorOccured = false;
// Get the partition this directory is on
partition = _FAT_partition_getPartitionFromPath (path);
if (partition == NULL) {
r->_errno = ENODEV;
return -1;
}
// Make sure we aren't trying to write to a read-only disc
if (partition->readOnly) {
r->_errno = EROFS;
return -1;
}
// Move the path pointer to the start of the actual path
if (strchr (path, ':') != NULL) {
path = strchr (path, ':') + 1;
}
if (strchr (path, ':') != NULL) {
r->_errno = EINVAL;
return -1;
}
// Search for the file on the disc
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
r->_errno = ENOENT;
return -1;
}
cluster = _FAT_directory_entryGetCluster (dirEntry.entryData);
// If this is a directory, make sure it is empty
if (_FAT_directory_isDirectory (&dirEntry)) {
nextEntry = _FAT_directory_getFirstEntry (partition, &dirContents, cluster);
while (nextEntry) {
if (!_FAT_directory_isDot (&dirContents)) {
// The directory had something in it that isn't a reference to itself or it's parent
r->_errno = EPERM;
return -1;
}
nextEntry = _FAT_directory_getNextEntry (partition, &dirContents);
}
}
if (cluster != CLUSTER_FREE) {
// Remove the cluster chain for this file
if (!_FAT_fat_clearLinks (partition, cluster)) {
r->_errno = EIO;
errorOccured = true;
}
}
// Remove the directory entry for this file
if (!_FAT_directory_removeEntry (partition, &dirEntry)) {
r->_errno = EIO;
errorOccured = true;
}
// Flush any sectors in the disc cache
if (!_FAT_cache_flush(partition->cache)) {
r->_errno = EIO;
errorOccured = true;
}
if (errorOccured) {
return -1;
} else {
return 0;
}
}
int _FAT_chdir_r (struct _reent *r, char *path) {
PARTITION* partition = NULL;
// Get the partition this directory is on
partition = _FAT_partition_getPartitionFromPath (path);
if (partition == NULL) {
r->_errno = ENODEV;
return -1;
}
// Move the path pointer to the start of the actual path
if (strchr (path, ':') != NULL) {
path = strchr (path, ':') + 1;
}
if (strchr (path, ':') != NULL) {
r->_errno = EINVAL;
return -1;
}
// Set the default device to match this one
if (!_FAT_partition_setDefaultPartition (partition)) {
r->_errno = ENOENT;
return -1;
}
// Try changing directory
if (_FAT_directory_chdir (partition, path)) {
// Successful
return 0;
} else {
// Failed
r->_errno = ENOTDIR;
return -1;
}
}

88
source/fatfile.h Normal file
View File

@ -0,0 +1,88 @@
/*
fatfile.h
Functions used by the newlib disc stubs to interface with
this library
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 _FATFILE_H
#define _FATFILE_H
#include <sys/reent.h>
#include <sys/stat.h>
#include "common.h"
#include "partition.h"
#include "directory.h"
typedef struct {
u32 cluster;
u32 sector;
s32 byte;
} FILE_POSITION;
typedef struct {
int pad;
u32 filesize;
u32 startCluster;
u32 currentPosition;
FILE_POSITION rwPosition;
FILE_POSITION appendPosition;
bool read;
bool write;
bool append;
bool inUse;
PARTITION* partition;
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
} FILE_STRUCT;
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_write_r (struct _reent *r,int fd, const char *ptr, int len);
extern int _FAT_read_r (struct _reent *r, int fd, char *ptr, int len);
extern int _FAT_seek_r (struct _reent *r, int fd,int pos, int dir);
extern int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st);
extern int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st);
extern int _FAT_link_r (struct _reent *r, char *existing, char *newLink);
extern int _FAT_unlink_r (struct _reent *r, char *name);
extern int _FAT_chdir_r (struct _reent *r, char *name);
#endif // _FATFILE_H

View File

@ -0,0 +1,280 @@
/*
file_allocation_table.c
Reading, writing and manipulation of the FAT structure on
a FAT partition
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
*/
#include "file_allocation_table.h"
#include "partition.h"
/*
Gets the cluster linked from input cluster
*/
u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster)
{
u32 nextCluster = CLUSTER_FREE;
u32 sector;
int offset;
switch (partition->filesysType)
{
case FS_UNKNOWN:
nextCluster = CLUSTER_FREE;
break;
case FS_FAT12:
sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
offset = ((cluster * 3) / 2) % BYTES_PER_READ;
_FAT_cache_readPartialSector (partition->cache, &nextCluster, sector, offset, sizeof(u8));
offset++;
if (offset >= BYTES_PER_READ) {
offset = 0;
sector++;
}
_FAT_cache_readPartialSector (partition->cache, ((u8*)&nextCluster) + sizeof(u8), sector, offset, sizeof(u8));
if (cluster & 0x01) {
nextCluster = nextCluster >> 4;
} else {
nextCluster &= 0x0FFF;
}
if (nextCluster >= 0x0FF7)
{
nextCluster = CLUSTER_EOF;
}
break;
case FS_FAT16:
sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
_FAT_cache_readPartialSector (partition->cache, &nextCluster, sector, offset, sizeof(u16));
if (nextCluster >= 0xFFF7)
{
nextCluster = CLUSTER_EOF;
}
break;
case FS_FAT32:
sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
_FAT_cache_readPartialSector (partition->cache, &nextCluster, sector, offset, sizeof(u32));
if (nextCluster >= 0x0FFFFFF7)
{
nextCluster = CLUSTER_EOF;
}
break;
default:
nextCluster = CLUSTER_FREE;
break;
}
return nextCluster;
}
/*
writes value into the correct offset within a partition's FAT, based
on the cluster number.
*/
static bool _FAT_fat_writeFatEntry (PARTITION* partition, u32 cluster, u32 value) {
u32 sector;
int offset;
u8 oldValue;
if ((cluster < 0x0002) || (cluster > partition->fat.lastCluster))
{
return false;
}
switch (partition->filesysType)
{
case FS_UNKNOWN:
return false;
break;
case FS_FAT12:
sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
offset = ((cluster * 3) / 2) % BYTES_PER_READ;
if (cluster & 0x01) {
_FAT_cache_readPartialSector (partition->cache, &oldValue, sector, offset, sizeof(u8));
value = (value << 4) | (oldValue & 0x0F);
_FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u8));
offset++;
if (offset >= BYTES_PER_READ) {
offset = 0;
sector++;
}
_FAT_cache_writePartialSector (partition->cache, ((u8*)&value) + sizeof(u8), sector, offset, sizeof(u8));
} else {
_FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u8));
offset++;
if (offset >= BYTES_PER_READ) {
offset = 0;
sector++;
}
_FAT_cache_readPartialSector (partition->cache, &oldValue, sector, offset, sizeof(u8));
value = ((value >> 8) & 0x0F) | (oldValue & 0xF0);
_FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u8));
}
break;
case FS_FAT16:
sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
_FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u16));
break;
case FS_FAT32:
sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
_FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u32));
break;
default:
return false;
break;
}
return true;
}
/*-----------------------------------------------------------------
gets the first available free cluster, sets it
to end of file, links the input cluster to it then returns the
cluster number
-----------------------------------------------------------------*/
u32 _FAT_fat_linkFreeCluster(PARTITION* partition, u32 cluster) {
u32 firstFree;
u32 curLink;
u32 lastCluster;
lastCluster = partition->fat.lastCluster;
if (cluster > lastCluster) {
return CLUSTER_FREE;
}
// Check if the cluster already has a link, and return it if so
curLink = _FAT_fat_nextCluster(partition, cluster);
if ((curLink >= CLUSTER_FIRST) && (curLink < lastCluster)) {
return curLink; // Return the current link - don't allocate a new one
}
// Get a free cluster
firstFree = partition->fat.firstFree;
// Start at first valid cluster
if (firstFree < CLUSTER_FIRST) {
firstFree = CLUSTER_FIRST;
}
// Search until a free cluster is found
while ((_FAT_fat_nextCluster(partition, firstFree) != CLUSTER_FREE) && (firstFree <= lastCluster)) {
firstFree++;
}
if (firstFree > lastCluster) {
// If couldn't get a free cluster then return, saying this fact
partition->fat.firstFree = firstFree;
return CLUSTER_FREE;
}
partition->fat.firstFree = firstFree;
if ((cluster >= CLUSTER_FIRST) && (cluster < lastCluster))
{
// Update the linked from FAT entry
_FAT_fat_writeFatEntry (partition, cluster, firstFree);
}
// Create the linked to FAT entry
_FAT_fat_writeFatEntry (partition, firstFree, CLUSTER_EOF);
return firstFree;
}
/*-----------------------------------------------------------------
_FAT_fat_clearLinks
frees any cluster used by a file
-----------------------------------------------------------------*/
bool _FAT_fat_clearLinks (PARTITION* partition, u32 cluster) {
u32 nextCluster;
if ((cluster < 0x0002) || (cluster > partition->fat.lastCluster))
return false;
while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE)) {
// Store next cluster before erasing the link
nextCluster = _FAT_fat_nextCluster (partition, cluster);
// Erase the link
_FAT_fat_writeFatEntry (partition, cluster, CLUSTER_FREE);
// Move onto next cluster
cluster = nextCluster;
}
return true;
}
/*-----------------------------------------------------------------
_FAT_fat_lastCluster
Trace the cluster links until the last one is found
-----------------------------------------------------------------*/
u32 _FAT_fat_lastCluster (PARTITION* partition, u32 cluster) {
while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) {
cluster = _FAT_fat_nextCluster(partition, cluster);
}
return cluster;
}

View File

@ -0,0 +1,60 @@
/*
file_allocation_table.h
Reading, writing and manipulation of the FAT structure on
a FAT partition
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 _FAT_H
#define _FAT_H
#include "common.h"
#include "partition.h"
#define CLUSTER_EOF_16 0xFFFF
#define CLUSTER_EOF 0x0FFFFFFF
#define CLUSTER_FREE 0x0000
#define CLUSTER_FIRST 0x0002
#define CLUSTERS_PER_FAT12 4085
#define CLUSTERS_PER_FAT16 65525
u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster);
u32 _FAT_fat_linkFreeCluster(PARTITION* partition, u32 cluster);
bool _FAT_fat_clearLinks (PARTITION* partition, u32 cluster);
u32 _FAT_fat_lastCluster (PARTITION* partition, u32 cluster);
static inline u32 _FAT_fat_clusterToSector (PARTITION* partition, u32 cluster) {
return (cluster >= 2) ? ((cluster - 2) * partition->sectorsPerCluster) + partition->dataStart : partition->rootDirStart;
}
#endif // _FAT_H

87
source/filetime.c Normal file
View File

@ -0,0 +1,87 @@
/*
filetime.c
Conversion of file time and date values to various other types
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
*/
#include "filetime.h"
#ifdef NDS
#include <nds/ipc.h>
#endif
u16 _FAT_filetime_getTimeFromRTC (void) {
#ifdef NDS
return (
( ( (IPC->rtc_hours > 11 ? IPC->rtc_hours - 40 : IPC->rtc_hours) & 0x1F) << 11) |
( (IPC->rtc_minutes & 0x3F) << 5) |
( (IPC->rtc_seconds >> 1) & 0x1F) );
#else
return 0;
#endif
}
u16 _FAT_filetime_getDateFromRTC (void) {
#ifdef NDS
return (
( ((IPC->rtc_year + 20) & 0x7F) <<9) |
( (IPC->rtc_month & 0xF) << 5) |
(IPC->rtc_day & 0x1F) );
#else
return 0;
#endif
}
time_t _FAT_filetime_to_time_t (u16 time, u16 date) {
int hour, minute, second;
int day, month, year;
time_t result;
hour = time >> 11;
minute = (time >> 5) & 0x3F;
second = (time & 0x1F) << 1;
day = date & 0x1F;
month = (date >> 5) & 0x0F;
year = date >> 9;
// Second values are averages, so time value won't be 100% accurate,
// but should be within the correct month.
result = second
+ minute * 60
+ hour * 3600
+ day * 86400
+ month * 2629743
+ year * 31556926
;
return result;
}

44
source/filetime.h Normal file
View File

@ -0,0 +1,44 @@
/*
filetime.h
Conversion of file time and date values to various other types
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 _FILETIME_H
#define _FILETIME_H
#include "common.h"
#include <sys/types.h>
u16 _FAT_filetime_getTimeFromRTC (void);
u16 _FAT_filetime_getDateFromRTC (void);
time_t _FAT_filetime_to_time_t (u16 time, u16 date);
#endif // _FILETIME_H

111
source/libfat.c Normal file
View File

@ -0,0 +1,111 @@
/*
libfat.c
Simple functionality for startup, mounting and unmounting of FAT-based devices.
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
*/
#include <sys/iosupport.h>
#include "common.h"
#include "partition.h"
#include "fatfile.h"
const devoptab_t dotab_fat = {
"fat",
sizeof (FILE_STRUCT),
_FAT_open_r,
_FAT_close_r,
_FAT_write_r,
_FAT_read_r,
_FAT_seek_r,
_FAT_fstat_r,
_FAT_stat_r,
_FAT_link_r,
_FAT_unlink_r,
_FAT_chdir_r
};
bool fatInit (u32 cacheSize, bool setAsDefaultDevice) {
#ifdef NDS
bool slot1Device, slot2Device;
// Try mounting both slots
slot1Device = _FAT_partition_mount (PI_SLOT_1, cacheSize);
slot2Device = _FAT_partition_mount (PI_SLOT_2, cacheSize);
// Choose the default device
if (slot1Device) {
_FAT_partition_setDefaultInterface (PI_SLOT_1);
} else if (slot2Device) {
_FAT_partition_setDefaultInterface (PI_SLOT_2);
} else {
return false;
}
#else // not defined NDS
bool cartSlotDevice;
cartSlotDevice = _FAT_partition_mount (PI_CART_SLOT, cacheSize);
if (cartSlotDevice) {
_FAT_partition_setDefaultInterface (PI_CART_SLOT);
} else {
return false;
}
#endif // defined NDS
AddDevice (&dotab_fat);
if (setAsDefaultDevice) {
setDefaultDevice( FindDevice ("fat:") );
}
return true;
}
bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize) {
return _FAT_partition_mount (partitionNumber, cacheSize);
}
bool fatMountCustomInterface (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);
}

47
source/mem_allocate.h Normal file
View File

@ -0,0 +1,47 @@
/*
mem_allocate.h
Memory allocation and destruction calls
Replace these calls with custom allocators if
malloc is unavailable
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 _MEM_ALLOCATE_H
#define _MEM_ALLOCATE_H
#include <malloc.h>
static inline void* _FAT_mem_allocate (size_t size) {
return malloc (size);
}
static inline void _FAT_mem_free (void* mem) {
return free (mem);
}
#endif // _MEM_ALLOCATE_H

455
source/partition.c Normal file
View File

@ -0,0 +1,455 @@
/*
partition.c
Functions for mounting and dismounting partitions
on various block devices.
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
*/
#include "partition.h"
#include "bit_ops.h"
#include "file_allocation_table.h"
#include "directory.h"
#include <string.h>
#include <ctype.h>
#include "mem_allocate.h"
/*
This device name, as known by DevKitPro
*/
const char* DEVICE_NAME = "fat";
/*
Data offsets
*/
// BIOS Parameter Block offsets
enum BPB {
BPB_jmpBoot = 0x00,
BPB_OEMName = 0x03,
// BIOS Parameter Block
BPB_bytesPerSector = 0x0B,
BPB_sectorsPerCluster = 0x0D,
BPB_reservedSectors = 0x0E,
BPB_numFATs = 0x10,
BPB_rootEntries = 0x11,
BPB_numSectorsSmall = 0x13,
BPB_mediaDesc = 0x15,
BPB_sectorsPerFAT = 0x16,
BPB_sectorsPerTrk = 0x18,
BPB_numHeads = 0x1A,
BPB_numHiddenSectors = 0x1C,
BPB_numSectors = 0x20,
// Ext BIOS Parameter Block for FAT16
BPB_FAT16_driveNumber = 0x24,
BPB_FAT16_reserved1 = 0x25,
BPB_FAT16_extBootSig = 0x26,
BPB_FAT16_volumeID = 0x27,
BPB_FAT16_volumeLabel = 0x2B,
BPB_FAT16_fileSysType = 0x36,
// Bootcode
BPB_FAT16_bootCode = 0x3E,
// FAT32 extended block
BPB_FAT32_sectorsPerFAT32 = 0x24,
BPB_FAT32_extFlags = 0x28,
BPB_FAT32_fsVer = 0x2A,
BPB_FAT32_rootClus = 0x2C,
BPB_FAT32_fsInfo = 0x30,
BPB_FAT32_bkBootSec = 0x32,
// Ext BIOS Parameter Block for FAT32
BPB_FAT32_driveNumber = 0x40,
BPB_FAT32_reserved1 = 0x41,
BPB_FAT32_extBootSig = 0x42,
BPB_FAT32_volumeID = 0x43,
BPB_FAT32_volumeLabel = 0x47,
BPB_FAT32_fileSysType = 0x52,
// Bootcode
BPB_FAT32_bootCode = 0x5A,
BPB_bootSig_55 = 0x1FE,
BPB_bootSig_AA = 0x1FF
};
#ifdef NDS
#define MAXIMUM_PARTITIONS 4
PARTITION* _partitions[MAXIMUM_PARTITIONS] = {NULL};
#else // not defined NDS
#define MAXIMUM_PARTITIONS 1
PARTITION* _partitions[MAXIMUM_PARTITIONS] = {NULL};
#endif // defined NDS
// Use a single static buffer for the partitions
static PARTITION* _FAT_partition_constructor ( IO_INTERFACE* disc, u32 cacheSize) {
PARTITION* partition;
int i;
u32 bootSector;
u8 sectorBuffer[BYTES_PER_READ];
// Read first sector of disc
if ( !_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) {
return NULL;
}
// Make sure it is a valid MBR or boot sector
if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) {
return NULL;
}
// Check if there is a FAT string, which indicates this is a boot sector
if ((sectorBuffer[0x36] == 'F') && (sectorBuffer[0x37] == 'A') && (sectorBuffer[0x38] == 'T')) {
bootSector = 0;
} else if ((sectorBuffer[0x52] == 'F') && (sectorBuffer[0x53] == 'A') && (sectorBuffer[0x54] == 'T')) {
// Check for FAT32
bootSector = 0;
} else {
// This is an MBR
// Find first valid partition from MBR
// First check for an active partition
for (i=0x1BE; (i < 0x1FE) && (sectorBuffer[i] != 0x80); i+= 0x10);
// If it didn't find an active partition, search for any valid partition
if (i == 0x1FE) {
for (i=0x1BE; (i < 0x1FE) && (sectorBuffer[i+0x04] == 0x00); i+= 0x10);
}
// Go to first valid partition
if ( i != 0x1FE) {
// Make sure it found a partition
bootSector = u8array_to_u32(sectorBuffer, 0x8 + i);
} else {
bootSector = 0; // No partition found, assume this is a MBR free disk
}
}
// Read in boot sector
if ( !_FAT_disc_readSectors (disc, bootSector, 1, sectorBuffer)) {
return NULL;
}
partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION));
if (partition == NULL) {
return false;
}
// Set partition's disc interface
partition->disc = disc;
// Store required information about the file system
partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT);
if (partition->fat.sectorsPerFat == 0) {
partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32);
}
partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall);
if (partition->numberOfSectors == 0) {
partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors);
}
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->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
partition->fat.fatStart = bootSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
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->totalSize = (partition->numberOfSectors - partition->dataStart) * partition->bytesPerSector;
// Store info about FAT
partition->fat.lastCluster = (partition->numberOfSectors - partition->dataStart) / partition->sectorsPerCluster;
partition->fat.firstFree = CLUSTER_FIRST;
if (partition->fat.lastCluster < CLUSTERS_PER_FAT12) {
partition->filesysType = FS_FAT12; // FAT12 volume
} else if (partition->fat.lastCluster < CLUSTERS_PER_FAT16) {
partition->filesysType = FS_FAT16; // FAT16 volume
} else {
partition->filesysType = FS_FAT32; // FAT32 volume
}
if (partition->filesysType != FS_FAT32) {
partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER;
} else {
// Set up for the FAT32 way
partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus);
// Check if FAT mirroring is enabled
if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) {
// Use the active FAT
partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F));
}
}
// Create a cache to use
partition->cache = _FAT_cache_constructor (cacheSize, partition->disc);
// Set current directory to the root
partition->cwdCluster = partition->rootDirCluster;
// Check if this disc is writable, and set the readOnly property appropriately
partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE);
// There are currently no open files on this partition
partition->openFileCount = 0;
return partition;
}
static void _FAT_partition_destructor (PARTITION* partition) {
_FAT_cache_destructor (partition->cache);
_FAT_disc_shutdown (partition->disc);
_FAT_mem_free (partition);
}
bool _FAT_partition_mount (PARTITION_INTERFACE partitionNumber, u32 cacheSize) {
#ifdef NDS
int i;
IO_INTERFACE* disc = NULL;
if (_partitions[partitionNumber] != NULL) {
return false;
}
switch (partitionNumber) {
case PI_SLOT_1:
// Mount the disc in slot 1
disc = _FAT_disc_dsSlotFindInterface ();
break;
case PI_SLOT_2:
// Mount the disc in slot 2
disc = _FAT_disc_gbaSlotFindInterface ();
break;
case PI_DEFAULT:
case PI_CUSTOM:
default:
// Anything else has to be handled specially
return false;
break;
}
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 ((_partitions[i] != NULL) && (_partitions[i]->disc == disc)) {
_partitions[partitionNumber] = _partitions[i];
return true;
}
}
_partitions[partitionNumber] = _FAT_partition_constructor (disc, cacheSize);
if (_partitions[partitionNumber] == NULL) {
return false;
}
#else // not defined NDS
IO_INTERFACE* disc = NULL;
if (_partitions[partitionNumber] != NULL) {
return false;
}
// Only ever one partition on GBA
disc = _FAT_disc_gbaSlotFindInterface ();
_partitions[partitionNumber] = _FAT_partition_constructor (disc, cacheSize);
#endif // defined NDS
return true;
}
bool _FAT_partition_mountCustomInterface (IO_INTERFACE* device, u32 cacheSize) {
#ifdef NDS
int i;
if (_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 ((_partitions[i] != NULL) && (_partitions[i]->disc == device)) {
_partitions[PI_CUSTOM] = _partitions[i];
return true;
}
}
_partitions[PI_CUSTOM] = _FAT_partition_constructor (device, cacheSize);
if (_partitions[PI_CUSTOM] == NULL) {
return false;
}
#else // not defined NDS
if (_partitions[PI_CART_SLOT] != NULL) {
return false;
}
if (device == NULL) {
return false;
}
// Only ever one partition on GBA
_partitions[PI_CART_SLOT] = _FAT_partition_constructor (device, cacheSize);
#endif // defined NDS
return true;
}
bool _FAT_partition_setDefaultInterface (PARTITION_INTERFACE partitionNumber) {
#ifdef NDS // 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 (_partitions[partitionNumber] == NULL) {
return false;
}
_partitions[PI_DEFAULT] = _partitions[partitionNumber];
#endif
return true;
}
bool _FAT_partition_setDefaultPartition (PARTITION* partition) {
#ifdef NDS // 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 (_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
_partitions[PI_DEFAULT] = partition;
#endif
return true;
}
bool _FAT_partition_unmount (PARTITION_INTERFACE partitionNumber) {
int i;
PARTITION* partition = _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 (_partitions[i] == partition) {
_partitions[i] = NULL;
}
}
_FAT_partition_destructor (partition);
return true;
}
bool _FAT_partition_unsafeUnmount (PARTITION_INTERFACE partitionNumber) {
int i;
PARTITION* partition = _partitions[partitionNumber];
if (partition == NULL) {
return false;
}
// Remove all references to this partition
for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
if (_partitions[i] == partition) {
_partitions[i] = NULL;
}
}
_FAT_cache_invalidate (partition->cache);
_FAT_partition_destructor (partition);
return true;
}
PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
#ifdef NDS
int namelen;
int partitionNumber;
// Device name extraction code taken from DevKitPro
namelen = strlen(DEVICE_NAME);
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 if (strchr (path, ':') == NULL) {
// No device specified
partitionNumber = PI_DEFAULT;
} else {
// Incorrect device name
return NULL;
}
if ((partitionNumber < 0) || (partitionNumber >= MAXIMUM_PARTITIONS)) {
return NULL;
}
return _partitions[partitionNumber];
#else // not defined NDS
// Only one possible partition on GBA
return _partitions[PI_CART_SLOT];
#endif // defined NDS
}

124
source/partition.h Normal file
View File

@ -0,0 +1,124 @@
/*
partition.h
Functions for mounting and dismounting partitions
on various block devices.
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 _PARTITION_H
#define _PARTITION_H
#include "common.h"
#include "disc_io/disc.h"
#include "cache.h"
// Device name
extern const char* DEVICE_NAME;
// Filesystem type
typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
#ifdef NDS
typedef enum {PI_DEFAULT, PI_SLOT_1, PI_SLOT_2, PI_CUSTOM} PARTITION_INTERFACE;
#else
typedef enum {PI_CART_SLOT} PARTITION_INTERFACE;
#endif
typedef struct {
u32 fatStart;
u32 sectorsPerFat;
u32 lastCluster;
u32 firstFree;
} FAT;
typedef struct {
IO_INTERFACE* disc;
CACHE* cache;
// Info about the partition
bool readOnly; // If this is set, then do not try writing to the disc
FS_TYPE filesysType;
u32 totalSize;
u32 rootDirStart;
u32 rootDirCluster;
u32 numberOfSectors;
u32 dataStart;
u32 bytesPerSector;
u32 sectorsPerCluster;
u32 bytesPerCluster;
FAT fat;
// Values that may change after construction
u32 cwdCluster; // Current working directory cluser
u32 openFileCount;
} PARTITION;
/*
Mount the device specified by partitionDevice
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);
/*
Mount a partition on a custom device
*/
bool _FAT_partition_mountCustomInterface (IO_INTERFACE* device, u32 cacheSize);
/*
Unmount the partition specified by partitionNumber
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);
#endif // _PARTITION_H