mirror of
https://github.com/wiiu-env/libfat.git
synced 2024-11-26 03:44:15 +01:00
Initial import into devkitpro CVS.
This commit is contained in:
commit
cc64edfab8
45
Makefile
Normal file
45
Makefile
Normal 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
159
gba/Makefile
Normal 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
91
gba/include/fat.h
Normal 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
106
include/fat.h
Normal 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
160
nds/Makefile
Normal 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
96
nds/include/fat.h
Normal 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
58
source/bit_ops.h
Normal 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
237
source/cache.c
Normal 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
118
source/cache.h
Normal 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
54
source/common.h
Normal 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
877
source/directory.c
Normal 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
171
source/directory.h
Normal 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
184
source/disc_io/disc.c
Normal 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
126
source/disc_io/disc.h
Normal 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
77
source/disc_io/disc_io.h
Normal 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
|
55
source/disc_io/io_cf_common.h
Normal file
55
source/disc_io/io_cf_common.h
Normal 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
307
source/disc_io/io_efa2.c
Normal 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
23
source/disc_io/io_efa2.h
Normal 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
334
source/disc_io/io_fcsr.c
Normal 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
44
source/disc_io/io_fcsr.h
Normal 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
|
60
source/disc_io/io_m3_common.c
Normal file
60
source/disc_io/io_m3_common.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
48
source/disc_io/io_m3_common.h
Normal file
48
source/disc_io/io_m3_common.h
Normal 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
354
source/disc_io/io_m3cf.c
Normal 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
45
source/disc_io/io_m3cf.h
Normal 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
527
source/disc_io/io_m3sd.c
Normal 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
48
source/disc_io/io_m3sd.h
Normal 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
350
source/disc_io/io_mmcf.c
Normal 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
45
source/disc_io/io_mmcf.h
Normal 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
348
source/disc_io/io_mpcf.c
Normal 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
45
source/disc_io/io_mpcf.h
Normal 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
348
source/disc_io/io_nmmc.c
Normal 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
53
source/disc_io/io_nmmc.h
Normal 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
|
47
source/disc_io/io_sc_common.c
Normal file
47
source/disc_io/io_sc_common.c
Normal 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 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
45
source/disc_io/io_sc_common.h
Normal file
45
source/disc_io/io_sc_common.h
Normal 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
87
source/disc_io/io_sccf.c
Normal 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
45
source/disc_io/io_sccf.h
Normal 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
476
source/disc_io/io_scsd.c
Normal 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
45
source/disc_io/io_scsd.h
Normal 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
|
124
source/disc_io/io_sd_common.c
Normal file
124
source/disc_io/io_sd_common.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
92
source/disc_io/io_sd_common.h
Normal file
92
source/disc_io/io_sd_common.h
Normal 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
964
source/fatfile.c
Normal 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
88
source/fatfile.h
Normal 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
|
280
source/file_allocation_table.c
Normal file
280
source/file_allocation_table.c
Normal 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;
|
||||||
|
}
|
60
source/file_allocation_table.h
Normal file
60
source/file_allocation_table.h
Normal 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
87
source/filetime.c
Normal 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
44
source/filetime.h
Normal 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
111
source/libfat.c
Normal 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
47
source/mem_allocate.h
Normal 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
455
source/partition.c
Normal 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
124
source/partition.h
Normal 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
|
Loading…
Reference in New Issue
Block a user