From 6364488dc26628f4d5346052fdcece8a2690fb5f Mon Sep 17 00:00:00 2001
From: Ivan Olenichev <>
Date: Thu, 21 Oct 2021 18:50:02 +0600
Subject: [PATCH] firmware_update
---
RISC-V/.cproject | 227 ++
RISC-V/.project | 26 +
RISC-V/.settings/language.settings.xml | 25 +
.../org.eclipse.cdt.codan.core.prefs | 109 +
RISC-V/.settings/org.eclipse.cdt.core.prefs | 6 +
.../org.eclipse.ltk.core.refactoring.prefs | 2 +
RISC-V/Source/fattime.c | 11 +
RISC-V/Source/ff.c | 3152 +++++++++++++++++
RISC-V/Source/gd32vf103_it.c | 63 +
RISC-V/Source/gd32vf103_usb_hw.c | 256 ++
RISC-V/Source/main.c | 87 +
RISC-V/Source/system_gd32vf103.c | 998 ++++++
RISC-V/Source/usbh_msc_bbb.c | 454 +++
RISC-V/Source/usbh_msc_core.c | 367 ++
RISC-V/Source/usbh_msc_fatfs.c | 222 ++
RISC-V/Source/usbh_msc_scsi.c | 488 +++
RISC-V/Source/usbh_usr.c | 512 +++
17 files changed, 7005 insertions(+)
create mode 100644 RISC-V/.cproject
create mode 100644 RISC-V/.project
create mode 100644 RISC-V/.settings/language.settings.xml
create mode 100644 RISC-V/.settings/org.eclipse.cdt.codan.core.prefs
create mode 100644 RISC-V/.settings/org.eclipse.cdt.core.prefs
create mode 100644 RISC-V/.settings/org.eclipse.ltk.core.refactoring.prefs
create mode 100644 RISC-V/Source/fattime.c
create mode 100644 RISC-V/Source/ff.c
create mode 100644 RISC-V/Source/gd32vf103_it.c
create mode 100644 RISC-V/Source/gd32vf103_usb_hw.c
create mode 100644 RISC-V/Source/main.c
create mode 100644 RISC-V/Source/system_gd32vf103.c
create mode 100644 RISC-V/Source/usbh_msc_bbb.c
create mode 100644 RISC-V/Source/usbh_msc_core.c
create mode 100644 RISC-V/Source/usbh_msc_fatfs.c
create mode 100644 RISC-V/Source/usbh_msc_scsi.c
create mode 100644 RISC-V/Source/usbh_usr.c
diff --git a/RISC-V/.cproject b/RISC-V/.cproject
new file mode 100644
index 0000000..c4b8085
--- /dev/null
+++ b/RISC-V/.cproject
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RISC-V/.project b/RISC-V/.project
new file mode 100644
index 0000000..d8b2057
--- /dev/null
+++ b/RISC-V/.project
@@ -0,0 +1,26 @@
+
+
+ GD32V_DOWNSTREAM
+
+
+
+
+
+ org.eclipse.cdt.managedbuilder.core.genmakebuilder
+ clean,full,incremental,
+
+
+
+
+ org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder
+ full,incremental,
+
+
+
+
+
+ org.eclipse.cdt.core.cnature
+ org.eclipse.cdt.managedbuilder.core.managedBuildNature
+ org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
+
+
diff --git a/RISC-V/.settings/language.settings.xml b/RISC-V/.settings/language.settings.xml
new file mode 100644
index 0000000..00a5fd9
--- /dev/null
+++ b/RISC-V/.settings/language.settings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RISC-V/.settings/org.eclipse.cdt.codan.core.prefs b/RISC-V/.settings/org.eclipse.cdt.codan.core.prefs
new file mode 100644
index 0000000..da30759
--- /dev/null
+++ b/RISC-V/.settings/org.eclipse.cdt.codan.core.prefs
@@ -0,0 +1,109 @@
+eclipse.preferences.version=1
+org.eclipse.cdt.codan.checkers.errnoreturn=Warning
+org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return\\")",implicit\=>false}
+org.eclipse.cdt.codan.checkers.errreturnvalue=Error
+org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused return value\\")"}
+org.eclipse.cdt.codan.checkers.localvarreturn=-Warning
+org.eclipse.cdt.codan.checkers.localvarreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Returning the address of a local variable\\")"}
+org.eclipse.cdt.codan.checkers.nocommentinside=-Error
+org.eclipse.cdt.codan.checkers.nocommentinside.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Nesting comments\\")"}
+org.eclipse.cdt.codan.checkers.nolinecomment=-Error
+org.eclipse.cdt.codan.checkers.nolinecomment.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Line comments\\")"}
+org.eclipse.cdt.codan.checkers.noreturn=Error
+org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return value\\")",implicit\=>false}
+org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error
+org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Abstract class cannot be instantiated\\")"}
+org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error
+org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Ambiguous problem\\")"}
+org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment in condition\\")"}
+org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error
+org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment to itself\\")"}
+org.eclipse.cdt.codan.internal.checkers.BlacklistProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.BlacklistProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Function or method is blacklisted\\")",blacklist\=>()}
+org.eclipse.cdt.codan.internal.checkers.CStyleCastProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.CStyleCastProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"C-Style cast instead of C++ cast\\")",checkMacro\=>true}
+org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No break at end of case\\")",no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false,enable_fallthrough_quickfix_param\=>false}
+org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning
+org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Catching by reference is recommended\\")",unknown\=>false,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error
+org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Circular inheritance\\")"}
+org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning
+org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class members should be properly initialized\\")",skip\=>true}
+org.eclipse.cdt.codan.internal.checkers.CopyrightProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.CopyrightProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Lack of copyright information\\")",regex\=>".*Copyright.*"}
+org.eclipse.cdt.codan.internal.checkers.DecltypeAutoProblem=Error
+org.eclipse.cdt.codan.internal.checkers.DecltypeAutoProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid 'decltype(auto)' specifier\\")"}
+org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Field cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.FloatCompareProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.FloatCompareProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Direct float comparison\\")"}
+org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Function cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.GotoStatementProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.GotoStatementProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Goto statement used\\")"}
+org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error
+org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid arguments\\")"}
+org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error
+org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid template argument\\")"}
+org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error
+org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Label statement not found\\")"}
+org.eclipse.cdt.codan.internal.checkers.MagicNumberProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MagicNumberProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Avoid magic numbers\\")",checkArray\=>true,checkOperatorParen\=>true,exceptions\=>(1,0,-1,2,1.0,0.0,-1.0)}
+org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error
+org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Member declaration not found\\")"}
+org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Method cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.MissCaseProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MissCaseProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing cases in switch\\")"}
+org.eclipse.cdt.codan.internal.checkers.MissDefaultProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MissDefaultProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing default in switch\\")",defaultWithAllEnums\=>false}
+org.eclipse.cdt.codan.internal.checkers.MissReferenceProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MissReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing reference return value in assignment operator\\")"}
+org.eclipse.cdt.codan.internal.checkers.MissSelfCheckProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MissSelfCheckProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing self check in assignment operator\\")"}
+org.eclipse.cdt.codan.internal.checkers.MultipleDeclarationsProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MultipleDeclarationsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Multiple variable declaration\\")"}
+org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info
+org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Name convention for function\\")",pattern\=>"^[a-z]",macro\=>true,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.NoDiscardProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.NoDiscardProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Return value not evaluated\\")",macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class has a virtual method and non-virtual destructor\\")"}
+org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error
+org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid overload\\")"}
+org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error
+org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redeclaration\\")"}
+org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redefinition\\")"}
+org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Return with parenthesis\\")"}
+org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Format String Vulnerability\\")"}
+org.eclipse.cdt.codan.internal.checkers.ShallowCopyProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ShallowCopyProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Miss copy constructor or assignment operator\\")",onlynew\=>false}
+org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Statement has no effect\\")",macro\=>true,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.StaticVariableInHeaderProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.StaticVariableInHeaderProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Static variable in header file\\")"}
+org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suggested parenthesis around expression\\")",paramNot\=>false}
+org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suspicious semicolon\\")",else\=>false,afterelse\=>false}
+org.eclipse.cdt.codan.internal.checkers.SymbolShadowingProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.SymbolShadowingProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Symbol shadowing\\")",paramFuncParameters\=>true}
+org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Type cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused function declaration\\")",macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused static function\\")",macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused variable declaration in file scope\\")",macro\=>true,exceptions\=>("@(\#)","$Id")}
+org.eclipse.cdt.codan.internal.checkers.UsingInHeaderProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.UsingInHeaderProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Using directive in header\\")"}
+org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Symbol is not resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.VirtualMethodCallProblem=-Error
+org.eclipse.cdt.codan.internal.checkers.VirtualMethodCallProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Virtual method call in constructor/destructor\\")"}
diff --git a/RISC-V/.settings/org.eclipse.cdt.core.prefs b/RISC-V/.settings/org.eclipse.cdt.core.prefs
new file mode 100644
index 0000000..c8ec5df
--- /dev/null
+++ b/RISC-V/.settings/org.eclipse.cdt.core.prefs
@@ -0,0 +1,6 @@
+doxygen/doxygen_new_line_after_brief=true
+doxygen/doxygen_use_brief_tag=false
+doxygen/doxygen_use_javadoc_tags=true
+doxygen/doxygen_use_pre_tag=false
+doxygen/doxygen_use_structural_commands=false
+eclipse.preferences.version=1
diff --git a/RISC-V/.settings/org.eclipse.ltk.core.refactoring.prefs b/RISC-V/.settings/org.eclipse.ltk.core.refactoring.prefs
new file mode 100644
index 0000000..b196c64
--- /dev/null
+++ b/RISC-V/.settings/org.eclipse.ltk.core.refactoring.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
diff --git a/RISC-V/Source/fattime.c b/RISC-V/Source/fattime.c
new file mode 100644
index 0000000..d9f2ebc
--- /dev/null
+++ b/RISC-V/Source/fattime.c
@@ -0,0 +1,11 @@
+
+
+#include "integer.h"
+#include "fattime.h"
+
+
+DWORD get_fattime (void)
+{
+ return 0;
+}
+
diff --git a/RISC-V/Source/ff.c b/RISC-V/Source/ff.c
new file mode 100644
index 0000000..de3bae1
--- /dev/null
+++ b/RISC-V/Source/ff.c
@@ -0,0 +1,3152 @@
+/*----------------------------------------------------------------------------/
+/ FatFs - FAT file system module R0.07e (C)ChaN, 2009
+/-----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following terms.
+/
+/ Copyright (C) 2009, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/-----------------------------------------------------------------------------/
+/ Feb 26,'06 R0.00 Prototype.
+/
+/ Apr 29,'06 R0.01 First stable version.
+/
+/ Jun 01,'06 R0.02 Added FAT12 support.
+/ Removed unbuffered mode.
+/ Fixed a problem on small (<32M) partition.
+/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
+/
+/ Sep 22,'06 R0.03 Added f_rename().
+/ Changed option _FS_MINIMUM to _FS_MINIMIZE.
+/ Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast.
+/ Fixed f_mkdir() creates incorrect directory on FAT32.
+/
+/ Feb 04,'07 R0.04 Supported multiple drive system.
+/ Changed some interfaces for multiple drive system.
+/ Changed f_mountdrv() to f_mount().
+/ Added f_mkfs().
+/ Apr 01,'07 R0.04a Supported multiple partitions on a physical drive.
+/ Added a capability of extending file size to f_lseek().
+/ Added minimization level 3.
+/ Fixed an endian sensitive code in f_mkfs().
+/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
+/ Added FSInfo support.
+/ Fixed DBCS name can result FR_INVALID_NAME.
+/ Fixed short seek (<= csize) collapses the file object.
+/
+/ Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs().
+/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
+/ Fixed f_mkdir() on FAT32 creates incorrect directory.
+/ Feb 03,'08 R0.05a Added f_truncate() and f_utime().
+/ Fixed off by one error at FAT sub-type determination.
+/ Fixed btr in f_read() can be mistruncated.
+/ Fixed cached sector is not flushed when create and close
+/ without write.
+/
+/ Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets().
+/ Improved performance of f_lseek() on moving to the same
+/ or following cluster.
+/
+/ Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option.
+/ Added long file name support.
+/ Added multiple code page support.
+/ Added re-entrancy for multitask operation.
+/ Added auto cluster size selection to f_mkfs().
+/ Added rewind option to f_readdir().
+/ Changed result code of critical errors.
+/ Renamed string functions to avoid name collision.
+/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
+/ Added multiple sector size support.
+/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
+/ Fixed wrong cache control in f_lseek().
+/ Added relative path feature.
+/ Added f_chdir() and f_chdrive().
+/ Added proper case conversion to extended char.
+/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
+/ Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
+/ Fixed name matching error on the 13 char boundary.
+/ Added a configuration option, _LFN_UNICODE.
+/ Changed f_readdir() to return the SFN with always upper
+/ case on non-LFN cfg.
+/---------------------------------------------------------------------------*/
+
+#include "ff.h" /* FatFs configurations and declarations */
+#include "diskio.h" /* Declarations of low level disk I/O functions */
+
+
+/*--------------------------------------------------------------------------
+
+ Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if _FATFS != 0x007E
+#error Wrong include file (ff.h).
+#endif
+
+#if _FS_REENTRANT
+#if _USE_LFN == 1
+#error Static LFN work area must not be used in re-entrant configuration.
+#endif
+#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; }
+#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
+
+#else
+#define ENTER_FF(fs)
+#define LEAVE_FF(fs, res) return res
+
+#endif
+
+#define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* Name status flags */
+#define NS 11 /* Offset of name status byte */
+#define NS_LOSS 0x01 /* Out of 8.3 format */
+#define NS_LFN 0x02 /* Force to create LFN entry */
+#define NS_LAST 0x04 /* Last segment */
+#define NS_BODY 0x08 /* Lower case flag (body) */
+#define NS_EXT 0x10 /* Lower case flag (ext) */
+#define NS_DOT 0x20 /* Dot entry */
+
+
+
+
+/*--------------------------------------------------------------------------
+
+ Private Work Area
+
+---------------------------------------------------------------------------*/
+
+#if _DRIVES < 1 || _DRIVES > 9
+#error Number of drives must be 1-9.
+#endif
+static
+FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */
+
+static
+WORD Fsid; /* File system mount ID */
+
+#if _FS_RPATH
+static
+BYTE Drive; /* Current drive */
+#endif
+
+
+#if _USE_LFN == 1 /* LFN with static LFN working buffer */
+static
+WCHAR LfnBuf[_MAX_LFN + 1];
+#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR *lp = LfnBuf
+#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp
+
+#elif _USE_LFN > 1 /* LFN with dynamic LFN working buffer */
+#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf
+#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp
+
+#else /* No LFN */
+#define NAMEBUF(sp,lp) BYTE sp[12]
+#define INITBUF(dj,sp,lp) dj.fn = sp
+
+#endif
+
+
+
+
+/*--------------------------------------------------------------------------
+
+ Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------*/
+/* String functions */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static
+void mem_cpy (void* dst, const void* src, int cnt) {
+ char *d = (char*)dst;
+ const char *s = (const char *)src;
+ while (cnt--) *d++ = *s++;
+}
+
+/* Fill memory */
+static
+void mem_set (void* dst, int val, int cnt) {
+ char *d = (char*)dst;
+ while (cnt--) *d++ = (char)val;
+}
+
+/* Compare memory to memory */
+static
+int mem_cmp (const void* dst, const void* src, int cnt) {
+ const char *d = (const char *)dst, *s = (const char *)src;
+ int r = 0;
+ while (cnt-- && (r = *d++ - *s++) == 0) ;
+ return r;
+}
+
+/* Check if chr is contained in the string */
+static
+int chk_chr (const char* str, int chr) {
+ while (*str && *str != chr) str++;
+ return *str;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume */
+/*-----------------------------------------------------------------------*/
+#if _FS_REENTRANT
+
+static
+BOOL lock_fs (
+ FATFS *fs /* File system object */
+)
+{
+ return ff_req_grant(fs->sobj);
+}
+
+
+static
+void unlock_fs (
+ FATFS *fs, /* File system object */
+ FRESULT res /* Result code to be returned */
+)
+{
+ if (res != FR_NOT_ENABLED &&
+ res != FR_INVALID_DRIVE &&
+ res != FR_INVALID_OBJECT &&
+ res != FR_TIMEOUT) {
+ ff_rel_grant(fs->sobj);
+ }
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change window offset */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT move_window (
+ FATFS *fs, /* File system object */
+ DWORD sector /* Sector number to make appearance in the fs->win[] */
+) /* Move to zero only writes back dirty window */
+{
+ DWORD wsect;
+
+
+ wsect = fs->winsect;
+ if (wsect != sector) { /* Changed current window */
+#if !_FS_READONLY
+ if (fs->wflag) { /* Write back dirty window if needed */
+ if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK)
+ return FR_DISK_ERR;
+ fs->wflag = 0;
+ if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */
+ BYTE nf;
+ for (nf = fs->n_fats; nf > 1; nf--) { /* Reflect the change to all FAT copies */
+ wsect += fs->sects_fat;
+ disk_write(fs->drive, fs->win, wsect, 1);
+ }
+ }
+ }
+#endif
+ if (sector) {
+ if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK)
+ return FR_DISK_ERR;
+ fs->winsect = sector;
+ }
+ }
+
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Clean-up cached data */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */
+ FATFS *fs /* File system object */
+)
+{
+ FRESULT res;
+
+
+ res = move_window(fs, 0);
+ if (res == FR_OK) {
+ /* Update FSInfo sector if needed */
+ if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
+ fs->winsect = 0;
+ mem_set(fs->win, 0, 512);
+ ST_WORD(fs->win+BS_55AA, 0xAA55);
+ ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
+ ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
+ ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
+ ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
+ disk_write(fs->drive, fs->win, fs->fsi_sector, 1);
+ fs->fsi_flag = 0;
+ }
+ /* Make sure that no pending write process in the physical drive */
+ if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK)
+ res = FR_DISK_ERR;
+ }
+
+ return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */
+ FATFS *fs, /* File system object */
+ DWORD clst /* Cluster# to get the link information */
+)
+{
+ UINT wc, bc;
+ DWORD fsect;
+
+
+ if (clst < 2 || clst >= fs->max_clust) /* Range check */
+ return 1;
+
+ fsect = fs->fatbase;
+ switch (fs->fs_type) {
+ case FS_FAT12 :
+ bc = clst; bc += bc / 2;
+ if (move_window(fs, fsect + (bc / SS(fs)))) break;
+ wc = fs->win[bc & (SS(fs) - 1)]; bc++;
+ if (move_window(fs, fsect + (bc / SS(fs)))) break;
+ wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8;
+ return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
+
+ case FS_FAT16 :
+ if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break;
+ return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]);
+
+ case FS_FAT32 :
+ if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break;
+ return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF;
+ }
+
+ return 0xFFFFFFFF; /* An error occurred at the disk I/O layer */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+
+FRESULT put_fat (
+ FATFS *fs, /* File system object */
+ DWORD clst, /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */
+ DWORD val /* New value to mark the cluster */
+)
+{
+ UINT bc;
+ BYTE *p;
+ DWORD fsect;
+ FRESULT res;
+
+
+ if (clst < 2 || clst >= fs->max_clust) { /* Range check */
+ res = FR_INT_ERR;
+
+ } else {
+ fsect = fs->fatbase;
+ switch (fs->fs_type) {
+ case FS_FAT12 :
+ bc = clst; bc += bc / 2;
+ res = move_window(fs, fsect + (bc / SS(fs)));
+ if (res != FR_OK) break;
+ p = &fs->win[bc & (SS(fs) - 1)];
+ *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
+ bc++;
+ fs->wflag = 1;
+ res = move_window(fs, fsect + (bc / SS(fs)));
+ if (res != FR_OK) break;
+ p = &fs->win[bc & (SS(fs) - 1)];
+ *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
+ break;
+
+ case FS_FAT16 :
+ res = move_window(fs, fsect + (clst / (SS(fs) / 2)));
+ if (res != FR_OK) break;
+ ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val);
+ break;
+
+ case FS_FAT32 :
+ res = move_window(fs, fsect + (clst / (SS(fs) / 4)));
+ if (res != FR_OK) break;
+ ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val);
+ break;
+
+ default :
+ res = FR_INT_ERR;
+ }
+ fs->wflag = 1;
+ }
+
+ return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT remove_chain (
+ FATFS *fs, /* File system object */
+ DWORD clst /* Cluster# to remove a chain from */
+)
+{
+ FRESULT res;
+ DWORD nxt;
+
+
+ if (clst < 2 || clst >= fs->max_clust) { /* Check the range of cluster# */
+ res = FR_INT_ERR;
+
+ } else {
+ res = FR_OK;
+ while (clst < fs->max_clust) { /* Not a last link? */
+ nxt = get_fat(fs, clst); /* Get cluster status */
+ if (nxt == 0) break; /* Empty cluster? */
+ if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */
+ if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */
+ res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */
+ if (res != FR_OK) break;
+ if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */
+ fs->free_clust++;
+ fs->fsi_flag = 1;
+ }
+ clst = nxt; /* Next cluster */
+ }
+ }
+
+ return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch or Create a cluster chain */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+ FATFS *fs, /* File system object */
+ DWORD clst /* Cluster# to stretch. 0 means create a new chain. */
+)
+{
+ DWORD cs, ncl, scl, mcl;
+
+
+ mcl = fs->max_clust;
+ if (clst == 0) { /* Create new chain */
+ scl = fs->last_clust; /* Get suggested start point */
+ if (scl == 0 || scl >= mcl) scl = 1;
+ }
+ else { /* Stretch existing chain */
+ cs = get_fat(fs, clst); /* Check the cluster status */
+ if (cs < 2) return 1; /* It is an invalid cluster */
+ if (cs < mcl) return cs; /* It is already followed by next cluster */
+ scl = clst;
+ }
+
+ ncl = scl; /* Start cluster */
+ for (;;) {
+ ncl++; /* Next cluster */
+ if (ncl >= mcl) { /* Wrap around */
+ ncl = 2;
+ if (ncl > scl) return 0; /* No free cluster */
+ }
+ cs = get_fat(fs, ncl); /* Get the cluster status */
+ if (cs == 0) break; /* Found a free cluster */
+ if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */
+ return cs;
+ if (ncl == scl) return 0; /* No free cluster */
+ }
+
+ if (put_fat(fs, ncl, 0x0FFFFFFF)) /* Mark the new cluster "in use" */
+ return 0xFFFFFFFF;
+ if (clst != 0) { /* Link it to the previous one if needed */
+ if (put_fat(fs, clst, ncl))
+ return 0xFFFFFFFF;
+ }
+
+ fs->last_clust = ncl; /* Update FSINFO */
+ if (fs->free_clust != 0xFFFFFFFF) {
+ fs->free_clust--;
+ fs->fsi_flag = 1;
+ }
+
+ return ncl; /* Return new cluster number */
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get sector# from cluster# */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */
+ FATFS *fs, /* File system object */
+ DWORD clst /* Cluster# to be converted */
+)
+{
+ clst -= 2;
+ if (clst >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */
+ return clst * fs->csize + fs->database;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Seek directory index */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_seek (
+ DIR *dj, /* Pointer to directory object */
+ WORD idx /* Directory index number */
+)
+{
+ DWORD clst;
+ WORD ic;
+
+
+ dj->index = idx;
+ clst = dj->sclust;
+ if (clst == 1 || clst >= dj->fs->max_clust) /* Check start cluster range */
+ return FR_INT_ERR;
+ if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */
+ clst = dj->fs->dirbase;
+
+ if (clst == 0) { /* Static table */
+ dj->clust = clst;
+ if (idx >= dj->fs->n_rootdir) /* Index is out of range */
+ return FR_INT_ERR;
+ dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32); /* Sector# */
+ }
+ else { /* Dynamic table */
+ ic = SS(dj->fs) / 32 * dj->fs->csize; /* Entries per cluster */
+ while (idx >= ic) { /* Follow cluster chain */
+ clst = get_fat(dj->fs, clst); /* Get next cluster */
+ if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
+ if (clst < 2 || clst >= dj->fs->max_clust) /* Reached to end of table or int error */
+ return FR_INT_ERR;
+ idx -= ic;
+ }
+ dj->clust = clst;
+ dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32); /* Sector# */
+ }
+
+ dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32; /* Ptr to the entry in the sector */
+
+ return FR_OK; /* Seek succeeded */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory index next */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */
+ DIR *dj, /* Pointer to directory object */
+ bool streach /* FALSE: Do not stretch table, TRUE: Stretch table if needed */
+)
+{
+ DWORD clst;
+ WORD i;
+
+
+ i = dj->index + 1;
+ if (!i || !dj->sect) /* Report EOT when index has reached 65535 */
+ return FR_NO_FILE;
+
+ if (!(i % (SS(dj->fs) / 32))) { /* Sector changed? */
+ dj->sect++; /* Next sector */
+
+ if (dj->clust == 0) { /* Static table */
+ if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */
+ return FR_NO_FILE;
+ }
+ else { /* Dynamic table */
+ if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */
+ clst = get_fat(dj->fs, dj->clust); /* Get next cluster */
+ if (clst <= 1) return FR_INT_ERR;
+ if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+ if (clst >= dj->fs->max_clust) { /* When it reached end of dynamic table */
+#if !_FS_READONLY
+ BYTE c;
+ if (!streach) return FR_NO_FILE; /* When do not stretch, report EOT */
+ clst = create_chain(dj->fs, dj->clust); /* Stretch cluster chain */
+ if (clst == 0) return FR_DENIED; /* No free cluster */
+ if (clst == 1) return FR_INT_ERR;
+ if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+ /* Clean-up streached table */
+ if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */
+ mem_set(dj->fs->win, 0, SS(dj->fs)); /* Clear window buffer */
+ dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */
+ for (c = 0; c < dj->fs->csize; c++) { /* Fill the new cluster with 0 */
+ dj->fs->wflag = 1;
+ if (move_window(dj->fs, 0)) return FR_DISK_ERR;
+ dj->fs->winsect++;
+ }
+ dj->fs->winsect -= c; /* Rewind window address */
+#else
+ return FR_NO_FILE; /* Report EOT */
+#endif
+ }
+ dj->clust = clst; /* Initialize data for new cluster */
+ dj->sect = clust2sect(dj->fs, clst);
+ }
+ }
+ }
+
+ dj->index = i;
+ dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32;
+
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */
+
+
+static
+BOOL cmp_lfn ( /* TRUE:Matched, FALSE:Not matched */
+ WCHAR *lfnbuf, /* Pointer to the LFN to be compared */
+ BYTE *dir /* Pointer to the directory entry containing a part of LFN */
+)
+{
+ int i, s;
+ WCHAR wc, uc;
+
+
+ i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Get offset in the LFN buffer */
+ s = 0; wc = 1;
+ do {
+ uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */
+ if (wc) { /* Last char has not been processed */
+ wc = ff_wtoupper(uc); /* Convert it to upper case */
+ if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */
+ return FALSE; /* Not matched */
+ } else {
+ if (uc != 0xFFFF) return FALSE; /* Check filler */
+ }
+ } while (++s < 13); /* Repeat until all chars in the entry are checked */
+
+ if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i]) /* Last segment matched but different length */
+ return FALSE;
+
+ return TRUE; /* The part of LFN matched */
+}
+
+
+
+static
+BOOL pick_lfn ( /* TRUE:Succeeded, FALSE:Buffer overflow */
+ WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */
+ BYTE *dir /* Pointer to the directory entry */
+)
+{
+ int i, s;
+ WCHAR wc, uc;
+
+
+ i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */
+
+ s = 0; wc = 1;
+ do {
+ uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */
+ if (wc) { /* Last char has not been processed */
+ if (i >= _MAX_LFN) return FALSE; /* Buffer overflow? */
+ lfnbuf[i++] = wc = uc; /* Store it */
+ } else {
+ if (uc != 0xFFFF) return FALSE; /* Check filler */
+ }
+ } while (++s < 13); /* Read all character in the entry */
+
+ if (dir[LDIR_Ord] & 0x40) { /* Put terminator if it is the last LFN part */
+ if (i >= _MAX_LFN) return FALSE; /* Buffer overflow? */
+ lfnbuf[i] = 0;
+ }
+
+ return TRUE;
+}
+
+
+#if !_FS_READONLY
+static
+void fit_lfn (
+ const WCHAR *lfnbuf, /* Pointer to the LFN buffer */
+ BYTE *dir, /* Pointer to the directory entry */
+ BYTE ord, /* LFN order (1-20) */
+ BYTE sum /* SFN sum */
+)
+{
+ int i, s;
+ WCHAR wc;
+
+
+ dir[LDIR_Chksum] = sum; /* Set check sum */
+ dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */
+ dir[LDIR_Type] = 0;
+ ST_WORD(dir+LDIR_FstClusLO, 0);
+
+ i = (ord - 1) * 13; /* Get offset in the LFN buffer */
+ s = wc = 0;
+ do {
+ if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */
+ ST_WORD(dir+LfnOfs[s], wc); /* Put it */
+ if (!wc) wc = 0xFFFF; /* Padding chars following last char */
+ } while (++s < 13);
+ if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40; /* Bottom LFN part is the start of LFN sequence */
+ dir[LDIR_Ord] = ord; /* Set the LFN order */
+}
+
+#endif
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create numbered name */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+void gen_numname (
+ BYTE *dst, /* Pointer to generated SFN */
+ const BYTE *src, /* Pointer to source SFN to be modified */
+ const WCHAR *lfn, /* Pointer to LFN */
+ WORD num /* Sequence number */
+)
+{
+ char ns[8];
+ int i, j;
+
+
+ mem_cpy(dst, src, 11);
+
+ if (num > 5) { /* On many collisions, generate a hash number instead of sequential number */
+ do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn);
+ }
+
+ /* itoa */
+ i = 7;
+ do {
+ ns[i--] = (num % 10) + '0';
+ num /= 10;
+ } while (num);
+ ns[i] = '~';
+
+ /* Append the number */
+ for (j = 0; j < i && dst[j] != ' '; j++) {
+ if (IsDBCS1(dst[j])) {
+ if (j == i - 1) break;
+ j++;
+ }
+ }
+ do {
+ dst[j++] = (i < 8) ? ns[i++] : ' ';
+ } while (j < 8);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Calculate sum of an SFN */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+BYTE sum_sfn (
+ const BYTE *dir /* Ptr to directory entry */
+)
+{
+ BYTE sum = 0;
+ int n = 11;
+
+ do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
+ return sum;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_find (
+ DIR *dj /* Pointer to the directory object linked to the file name */
+)
+{
+ FRESULT res;
+ BYTE c, *dir;
+#if _USE_LFN
+ BYTE a, ord, sum;
+#endif
+
+ res = dir_seek(dj, 0); /* Rewind directory object */
+ if (res != FR_OK) return res;
+
+#if _USE_LFN
+ ord = sum = 0xFF;
+#endif
+ do {
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ dir = dj->dir; /* Ptr to the directory entry of current index */
+ c = dir[DIR_Name];
+ if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
+#if _USE_LFN /* LFN configuration */
+ a = dir[DIR_Attr] & AM_MASK;
+ if (c == 0xE5 || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */
+ ord = 0xFF;
+ } else {
+ if (a == AM_LFN) { /* An LFN entry is found */
+ if (dj->lfn) {
+ if (c & 0x40) { /* Is it start of LFN sequence? */
+ sum = dir[LDIR_Chksum];
+ c &= 0xBF; ord = c; /* LFN start order */
+ dj->lfn_idx = dj->index;
+ }
+ /* Check validity of the LFN entry and compare it with given name */
+ ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+ }
+ } else { /* An SFN entry is found */
+ if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */
+ ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */
+ if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break; /* SFN matched? */
+ }
+ }
+#else /* Non LFN configuration */
+ if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
+ break;
+#endif
+ res = dir_next(dj, FALSE); /* Next entry */
+ } while (res == FR_OK);
+
+ return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+FRESULT dir_read (
+ DIR *dj /* Pointer to the directory object that pointing the entry to be read */
+)
+{
+ FRESULT res;
+ BYTE c, *dir;
+#if _USE_LFN
+ BYTE a, ord = 0xFF, sum = 0xFF;
+#endif
+
+ res = FR_NO_FILE;
+ while (dj->sect) {
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ dir = dj->dir; /* Ptr to the directory entry of current index */
+ c = dir[DIR_Name];
+ if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
+#if _USE_LFN /* LFN configuration */
+ a = dir[DIR_Attr] & AM_MASK;
+ if (c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */
+ ord = 0xFF;
+ } else {
+ if (a == AM_LFN) { /* An LFN entry is found */
+ if (c & 0x40) { /* Is it start of LFN sequence? */
+ sum = dir[LDIR_Chksum];
+ c &= 0xBF; ord = c;
+ dj->lfn_idx = dj->index;
+ }
+ /* Check LFN validity and capture it */
+ ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+ } else { /* An SFN entry is found */
+ if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */
+ dj->lfn_idx = 0xFFFF; /* It has no LFN. */
+ break;
+ }
+ }
+#else /* Non LFN configuration */
+ if (c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */
+ break;
+#endif
+ res = dir_next(dj, FALSE); /* Next entry */
+ if (res != FR_OK) break;
+ }
+
+ if (res != FR_OK) dj->sect = 0;
+
+ return res;
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
+ DIR *dj /* Target directory with object name to be created */
+)
+{
+ FRESULT res;
+ BYTE c, *dir;
+#if _USE_LFN /* LFN configuration */
+ WORD n, ne, is;
+ BYTE sn[12], *fn, sum;
+ WCHAR *lfn;
+
+
+ fn = dj->fn; lfn = dj->lfn;
+ mem_cpy(sn, fn, 12);
+
+ if (_FS_RPATH && (sn[NS] & NS_DOT)) return FR_INVALID_NAME; /* Cannot create dot entry */
+
+ if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */
+ fn[NS] = 0; dj->lfn = NULL; /* Find only SFN */
+ for (n = 1; n < 100; n++) {
+ gen_numname(fn, sn, lfn, n); /* Generate a numbered name */
+ res = dir_find(dj); /* Check if the name collides with existing SFN */
+ if (res != FR_OK) break;
+ }
+ if (n == 100) return FR_DENIED; /* Abort if too many collisions */
+ if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */
+ fn[NS] = sn[NS]; dj->lfn = lfn;
+ }
+
+ if (sn[NS] & NS_LFN) { /* When LFN is to be created, reserve reserve an SFN + LFN entries. */
+ for (ne = 0; lfn[ne]; ne++) ;
+ ne = (ne + 25) / 13;
+ } else { /* Otherwise reserve only an SFN entry. */
+ ne = 1;
+ }
+
+ /* Reserve contiguous entries */
+ res = dir_seek(dj, 0);
+ if (res != FR_OK) return res;
+ n = is = 0;
+ do {
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ c = *dj->dir; /* Check the entry status */
+ if (c == 0xE5 || c == 0) { /* Is it a blank entry? */
+ if (n == 0) is = dj->index; /* First index of the contiguous entry */
+ if (++n == ne) break; /* A contiguous entry that required count is found */
+ } else {
+ n = 0; /* Not a blank entry. Restart to search */
+ }
+ res = dir_next(dj, TRUE); /* Next entry with table stretch */
+ } while (res == FR_OK);
+
+ if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */
+ res = dir_seek(dj, is);
+ if (res == FR_OK) {
+ sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */
+ ne--;
+ do { /* Store LFN entries in bottom first */
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
+ dj->fs->wflag = 1;
+ res = dir_next(dj, FALSE); /* Next entry */
+ } while (res == FR_OK && --ne);
+ }
+ }
+
+#else /* Non LFN configuration */
+ res = dir_seek(dj, 0);
+ if (res == FR_OK) {
+ do { /* Find a blank entry for the SFN */
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ c = *dj->dir;
+ if (c == 0xE5 || c == 0) break; /* Is it a blank entry? */
+ res = dir_next(dj, TRUE); /* Next entry with table streach */
+ } while (res == FR_OK);
+ }
+#endif
+
+ if (res == FR_OK) { /* Initialize the SFN entry */
+ res = move_window(dj->fs, dj->sect);
+ if (res == FR_OK) {
+ dir = dj->dir;
+ mem_set(dir, 0, 32); /* Clean the entry */
+ mem_cpy(dir, dj->fn, 11); /* Put SFN */
+ dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */
+ dj->fs->wflag = 1;
+ }
+ }
+
+ return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY && !_FS_MINIMIZE
+static
+FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */
+ DIR *dj /* Directory object pointing the entry to be removed */
+)
+{
+ FRESULT res;
+#if _USE_LFN /* LFN configuration */
+ WORD i;
+
+ i = dj->index; /* SFN index */
+ res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); /* Goto the SFN or top of the LFN entries */
+ if (res == FR_OK) {
+ do {
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ *dj->dir = 0xE5; /* Mark the entry "deleted" */
+ dj->fs->wflag = 1;
+ if (dj->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */
+ res = dir_next(dj, FALSE); /* Next entry */
+ } while (res == FR_OK);
+ if (res == FR_NO_FILE) res = FR_INT_ERR;
+ }
+
+#else /* Non LFN configuration */
+ res = dir_seek(dj, dj->index);
+ if (res == FR_OK) {
+ res = move_window(dj->fs, dj->sect);
+ if (res == FR_OK) {
+ *dj->dir = 0xE5; /* Mark the entry "deleted" */
+ dj->fs->wflag = 1;
+ }
+ }
+#endif
+
+ return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a segment and create the object name in directory form */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT create_name (
+ DIR *dj, /* Pointer to the directory object */
+ const XCHAR **path /* Pointer to pointer to the segment in the path string */
+)
+{
+#ifdef _EXCVT
+ static const BYTE cvt[] = _EXCVT;
+#endif
+
+#if _USE_LFN /* LFN configuration */
+ BYTE b, cf;
+ WCHAR w, *lfn;
+ int i, ni, si, di;
+ const XCHAR *p;
+
+ /* Create LFN in Unicode */
+ si = di = 0;
+ p = *path;
+ lfn = dj->lfn;
+ for (;;) {
+ w = p[si++]; /* Get a character */
+ if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */
+ if (di >= _MAX_LFN) /* Reject too long name */
+ return FR_INVALID_NAME;
+#if !_LFN_UNICODE
+ w &= 0xFF;
+ if (IsDBCS1(w)) { /* If it is a DBC 1st byte */
+ b = p[si++]; /* Get 2nd byte */
+ if (!IsDBCS2(b)) /* Reject invalid code for DBC */
+ return FR_INVALID_NAME;
+ //w = (w << 8) + b;
+ else w = (w << 8) + b;
+ }
+ w = ff_convert(w, 1); /* Convert OEM to Unicode */
+ if (!w) return FR_INVALID_NAME; /* Reject invalid code */
+#endif
+ if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */
+ return FR_INVALID_NAME;
+ lfn[di++] = w; /* Store the Unicode char */
+ }
+ *path = &p[si]; /* Return pointer to the next segment */
+ cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */
+#if _FS_RPATH
+ if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */
+ (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) {
+ lfn[di] = 0;
+ for (i = 0; i < 11; i++)
+ dj->fn[i] = (i < di) ? '.' : ' ';
+ dj->fn[i] = cf | NS_DOT; /* This is a dot entry */
+ return FR_OK;
+ }
+#endif
+ while (di) { /* Strip trailing spaces and dots */
+ w = lfn[di - 1];
+ if (w != ' ' && w != '.') break;
+ di--;
+ }
+ if (!di) return FR_INVALID_NAME; /* Reject null string */
+
+ lfn[di] = 0; /* LFN is created */
+
+ /* Create SFN in directory form */
+ mem_set(dj->fn, ' ', 11);
+ for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */
+ if (si) cf |= NS_LOSS | NS_LFN;
+ while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */
+
+ b = i = 0; ni = 8;
+ for (;;) {
+ w = lfn[si++]; /* Get an LFN char */
+ if (!w) break; /* Break on end of the LFN */
+ if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */
+ cf |= NS_LOSS | NS_LFN; continue;
+ }
+
+ if (i >= ni || si == di) { /* Extension or end of SFN */
+ if (ni == 11) { /* Long extension */
+ cf |= NS_LOSS | NS_LFN; break;
+ }
+ if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */
+ if (si > di) break; /* No extension */
+ si = di; i = 8; ni = 11; /* Enter extension section */
+ b <<= 2; continue;
+ }
+
+ if (w >= 0x80) { /* Non ASCII char */
+#ifdef _EXCVT
+ w = ff_convert(w, 0); /* Unicode -> OEM code */
+ if (w) w = cvt[w - 0x80]; /* Convert extended char to upper (SBCS) */
+#else
+ w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */
+#endif
+ cf |= NS_LFN; /* Force create LFN entry */
+ }
+
+ if (_DF1S && w >= 0x100) { /* Double byte char */
+ if (i >= ni - 1) {
+ cf |= NS_LOSS | NS_LFN; i = ni; continue;
+ }
+ dj->fn[i++] = (BYTE)(w >> 8);
+ } else { /* Single byte char */
+ if (!w || chk_chr("+,;[=]", w)) { /* Replace illegal chars for SFN */
+ w = '_'; cf |= NS_LOSS | NS_LFN; /* Lossy conversion */
+ } else {
+ if (IsUpper(w)) { /* ASCII large capital */
+ b |= 2;
+ } else {
+ if (IsLower(w)) { /* ASCII small capital */
+ b |= 1; w -= 0x20;
+ }
+ }
+ }
+ }
+ dj->fn[i++] = (BYTE)w;
+ }
+
+ if (dj->fn[0] == 0xE5) dj->fn[0] = 0x05; /* If the first char collides with deleted mark, replace it with 0x05 */
+
+ if (ni == 8) b <<= 2;
+ if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */
+ cf |= NS_LFN;
+ if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */
+ if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */
+ if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */
+ }
+
+ dj->fn[NS] = cf; /* SFN is created */
+ return FR_OK;
+
+
+#else /* Non-LFN configuration */
+ BYTE b, c, d, *sfn;
+ int ni, si, i;
+ const char *p;
+
+ /* Create file name in directory form */
+ sfn = dj->fn;
+ mem_set(sfn, ' ', 11);
+ si = i = b = 0; ni = 8;
+ p = *path;
+#if _FS_RPATH
+ if (p[si] == '.') { /* Is this a dot entry? */
+ for (;;) {
+ c = p[si++];
+ if (c != '.' || si >= 3) break;
+ sfn[i++] = c;
+ }
+ if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+ *path = &p[si]; /* Return pointer to the next segment */
+ sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */
+ return FR_OK;
+ }
+#endif
+ for (;;) {
+ c = p[si++];
+ if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */
+ if (c == '.' || i >= ni) {
+ if (ni != 8 || c != '.') return FR_INVALID_NAME;
+ i = 8; ni = 11;
+ b <<= 2; continue;
+ }
+ if (c >= 0x80) { /* Extended char */
+#ifdef _EXCVT
+ c = cvt[c - 0x80]; /* Convert extend char (SBCS) */
+#else
+ b |= 3; /* Eliminate NT flag if ext char is exist */
+#if !_DF1S /* ASCII only cfg */
+ return FR_INVALID_NAME;
+#endif
+#endif
+ }
+ if (IsDBCS1(c)) { /* DBC 1st byte? */
+ d = p[si++]; /* Get 2nd byte */
+ if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */
+ return FR_INVALID_NAME;
+ sfn[i++] = c;
+ sfn[i++] = d;
+ } else { /* Single byte code */
+ if (chk_chr(" \"*+,[=]|\x7F", c)) /* Reject illegal chrs for SFN */
+ return FR_INVALID_NAME;
+ if (IsUpper(c)) { /* ASCII large capital? */
+ b |= 2;
+ } else {
+ if (IsLower(c)) { /* ASCII small capital? */
+ b |= 1; c -= 0x20;
+ }
+ }
+ sfn[i++] = c;
+ }
+ }
+ *path = &p[si]; /* Return pointer to the next segment */
+ c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */
+
+ if (!i) return FR_INVALID_NAME; /* Reject null string */
+ if (sfn[0] == 0xE5) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */
+
+ if (ni == 8) b <<= 2;
+ if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Extension has only small capital) */
+ if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Filename has only small capital) */
+
+ sfn[NS] = c; /* Store NT flag, File name is created */
+
+ return FR_OK;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+void get_fileinfo ( /* No return code */
+ DIR *dj, /* Pointer to the directory object */
+ FILINFO *fno /* Pointer to the file information to be filled */
+)
+{
+ int i;
+ BYTE c, nt, *dir;
+ char *p;
+
+
+ p = fno->fname;
+ if (dj->sect) {
+ dir = dj->dir;
+ nt = dir[DIR_NTres]; /* NT flag */
+ for (i = 0; i < 8; i++) { /* Copy name body */
+ c = dir[i];
+ if (c == ' ') break;
+ if (c == 0x05) c = 0xE5;
+ if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20;
+ *p++ = c;
+ }
+ if (dir[8] != ' ') { /* Copy name extension */
+ *p++ = '.';
+ for (i = 8; i < 11; i++) {
+ c = dir[i];
+ if (c == ' ') break;
+ if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20;
+ *p++ = c;
+ }
+ }
+ fno->fattrib = dir[DIR_Attr]; /* Attribute */
+ fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */
+ fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */
+ fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */
+ }
+ *p = 0;
+
+#if _USE_LFN
+ if (fno->lfname) {
+ XCHAR *tp = fno->lfname;
+ WCHAR w, *lfn;
+
+ i = 0;
+ if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
+ lfn = dj->lfn;
+ while ((w = *lfn++) != 0) { /* Get an LFN char */
+#if !_LFN_UNICODE
+ w = ff_convert(w, 0); /* Unicode -> OEM conversion */
+ if (!w) { i = 0; break; } /* Could not convert, no LFN */
+ if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC */
+ tp[i++] = (XCHAR)(w >> 8);
+#endif
+ if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overrun, no LFN */
+ tp[i++] = (XCHAR)w;
+ }
+ }
+ tp[i] = 0; /* Terminator */
+ }
+#endif
+}
+#endif /* _FS_MINIMIZE <= 1 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
+ DIR *dj, /* Directory object to return last directory and found object */
+ const XCHAR *path /* Full-path string to find a file or directory */
+)
+{
+ FRESULT res;
+ BYTE *dir, last;
+
+ while (!_USE_LFN && *path == ' ') path++; /* Skip leading spaces */
+#if _FS_RPATH
+ if (*path == '/' || *path == '\\') { /* There is a heading separator */
+ path++; dj->sclust = 0; /* Strip it and start from the root dir */
+ } else { /* No heading separator */
+ dj->sclust = dj->fs->cdir; /* Start from the current dir */
+ }
+#else
+ if (*path == '/' || *path == '\\') /* Strip heading separator if exist */
+ path++;
+ dj->sclust = 0; /* Start from the root dir */
+#endif
+
+ if ((UINT)*path < ' ') { /* Null path means the start directory itself */
+ res = dir_seek(dj, 0);
+ dj->dir = NULL;
+
+ } else { /* Follow path */
+ for (;;) {
+ res = create_name(dj, &path); /* Get a segment */
+ if (res != FR_OK) break;
+ res = dir_find(dj); /* Find it */
+ last = *(dj->fn+NS) & NS_LAST;
+ if (res != FR_OK) { /* Could not find the object */
+ if (res == FR_NO_FILE && !last)
+ res = FR_NO_PATH;
+ break;
+ }
+ if (last) break; /* Last segment match. Function completed. */
+ dir = dj->dir; /* There is next segment. Follow the sub directory */
+ if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */
+ res = FR_NO_PATH; break;
+ }
+ dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+ }
+ }
+
+ return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load boot record and check if it is an FAT boot record */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
+ FATFS *fs, /* File system object */
+ DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */
+)
+{
+ if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK) /* Load boot record */
+ return 3;
+ if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */
+ return 2;
+
+ if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */
+ return 0;
+ if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)
+ return 0;
+
+ return 1;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Make sure that the file system is valid */
+/*-----------------------------------------------------------------------*/
+
+
+FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occurred */
+ const XCHAR **path, /* Pointer to pointer to the path name (drive number) */
+ FATFS **rfs, /* Pointer to pointer to the found file system object */
+ BYTE chk_wp /* !=0: Check media write protection for write access */
+)
+{
+ BYTE fmt, *tbl;
+ UINT vol;
+ DSTATUS stat;
+ DWORD bsect, fsize, tsect, mclst;
+ const XCHAR *p = *path;
+ FATFS *fs;
+
+ /* Get logical drive number from the path name */
+ vol = p[0] - '0'; /* Is there a drive number? */
+ if (vol <= 9 && p[1] == ':') { /* Found a drive number, get and strip it */
+ p += 2; *path = p; /* Return pointer to the path name */
+ } else { /* No drive number is given */
+#if _FS_RPATH
+ vol = Drive; /* Use current drive */
+#else
+ vol = 0; /* Use drive 0 */
+#endif
+ }
+
+ /* Check if the logical drive is valid or not */
+ if (vol >= _DRIVES) /* Is the drive number valid? */
+ return FR_INVALID_DRIVE;
+ *rfs = fs = FatFs[vol]; /* Return pointer to the corresponding file system object */
+ if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */
+
+ ENTER_FF(fs); /* Lock file system */
+
+ if (fs->fs_type) { /* If the logical drive has been mounted */
+ stat = disk_status(fs->drive);
+ if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized (has not been changed), */
+#if !_FS_READONLY
+ if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */
+ return FR_WRITE_PROTECTED;
+#endif
+ return FR_OK; /* The file system object is valid */
+ }
+ }
+
+ /* The logical drive must be mounted. Following code attempts to mount the volume */
+
+ fs->fs_type = 0; /* Clear the file system object */
+ fs->drive = (BYTE)LD2PD(vol); /* Bind the logical drive and a physical drive */
+ stat = disk_initialize(fs->drive); /* Initialize low level disk I/O layer */
+ if (stat & STA_NOINIT) /* Check if the drive is ready */
+ return FR_NOT_READY;
+#if _MAX_SS != 512 /* Get disk sector size if needed */
+ if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS)
+ return FR_NO_FILESYSTEM;
+#endif
+#if !_FS_READONLY
+ if (chk_wp && (stat & STA_PROTECT)) /* Check disk write protection if needed */
+ return FR_WRITE_PROTECTED;
+#endif
+ /* Search FAT partition on the drive */
+ fmt = check_fs(fs, bsect = 0); /* Check sector 0 as an SFD format */
+ if (fmt == 1) { /* Not an FAT boot record, it may be partitioned */
+ /* Check a partition listed in top of the partition table */
+ tbl = &fs->win[MBR_Table + LD2PT(vol) * 16]; /* Partition table */
+ if (tbl[4]) { /* Is the partition existing? */
+ bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */
+ fmt = check_fs(fs, bsect); /* Check the partition */
+ }
+ }
+ if (fmt == 3) return FR_DISK_ERR;
+ if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* No valid FAT partition is found */
+ return FR_NO_FILESYSTEM;
+
+ /* Initialize the file system object */
+ fsize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */
+ if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32);
+ fs->sects_fat = fsize;
+ fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */
+ fsize *= fs->n_fats; /* (Number of sectors in FAT area) */
+ fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */
+ fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */
+ fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Number of root directory entries */
+ tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */
+ if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
+ fs->max_clust = mclst = (tsect /* Last cluster# + 1 (Number of clusters + 2) */
+ - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32)
+ ) / fs->csize + 2;
+
+ fmt = FS_FAT12; /* Determine the FAT sub type */
+ if (mclst >= 0xFF7) fmt = FS_FAT16; /* Number of clusters >= 0xFF5 */
+ if (mclst >= 0xFFF7) fmt = FS_FAT32; /* Number of clusters >= 0xFFF5 */
+
+ if (fmt == FS_FAT32)
+ fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */
+ else
+ fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */
+ fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32); /* Data start sector (lba) */
+
+#if !_FS_READONLY
+ /* Initialize allocation information */
+ fs->free_clust = 0xFFFFFFFF;
+ fs->wflag = 0;
+ /* Get fsinfo if needed */
+ if (fmt == FS_FAT32) {
+ fs->fsi_flag = 0;
+ fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo);
+ if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK &&
+ LD_WORD(fs->win+BS_55AA) == 0xAA55 &&
+ LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 &&
+ LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) {
+ fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
+ fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
+ }
+ }
+#endif
+ fs->fs_type = fmt; /* FAT sub-type */
+ fs->winsect = 0; /* Invalidate sector cache */
+#if _FS_RPATH
+ fs->cdir = 0; /* Current directory (root dir) */
+#endif
+ fs->id = ++Fsid; /* File system mount ID */
+
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/dir object is valid or not */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */
+ FATFS *fs, /* Pointer to the file system object */
+ WORD id /* Member id of the target object to be checked */
+)
+{
+ if (!fs || !fs->fs_type || fs->id != id)
+ return FR_INVALID_OBJECT;
+
+ ENTER_FF(fs); /* Lock file system */
+
+ if (disk_status(fs->drive) & STA_NOINIT)
+ return FR_NOT_READY;
+
+ return FR_OK;
+}
+
+
+
+
+/*--------------------------------------------------------------------------
+
+ Public Functions
+
+--------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Logical Drive */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+ BYTE vol, /* Logical drive number to be mounted/unmounted */
+ FATFS *fs /* Pointer to new file system object (NULL for unmount)*/
+)
+{
+ FATFS *rfs;
+
+
+ if (vol >= _DRIVES) /* Check if the drive number is valid */
+ return FR_INVALID_DRIVE;
+ rfs = FatFs[vol]; /* Get current fs object */
+
+ if (rfs) {
+#if _FS_REENTRANT /* Discard sync object of the current volume */
+ if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR;
+#endif
+ rfs->fs_type = 0; /* Clear old fs object */
+ }
+
+ if (fs) {
+ fs->fs_type = 0; /* Clear new fs object */
+#if _FS_REENTRANT /* Create sync object for the new volume */
+ if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+ }
+ FatFs[vol] = fs; /* Register new fs object */
+
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+ FIL *fp, /* Pointer to the blank file object */
+ const XCHAR *path, /* Pointer to the file name */
+ BYTE mode /* Access mode and file open mode flags */
+)
+{
+ FRESULT res;
+ DIR dj;
+ NAMEBUF(sfn, lfn);
+ BYTE *dir;
+
+
+ fp->fs = NULL; /* Clear file object */
+#if !_FS_READONLY
+ mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW);
+ res = chk_mounted(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)));
+#else
+ mode &= FA_READ;
+ res = chk_mounted(&path, &dj.fs, 0);
+#endif
+ if (res != FR_OK) LEAVE_FF(dj.fs, res);
+ INITBUF(dj, sfn, lfn);
+ res = follow_path(&dj, path); /* Follow the file path */
+
+#if !_FS_READONLY
+ /* Create or Open a file */
+ if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+ DWORD ps, cl;
+
+ if (res != FR_OK) { /* No file, create new */
+ if (res == FR_NO_FILE) /* There is no file to open, create a new entry */
+ res = dir_register(&dj);
+ if (res != FR_OK) LEAVE_FF(dj.fs, res);
+ mode |= FA_CREATE_ALWAYS;
+ dir = dj.dir; /* Created entry (SFN entry) */
+ }
+ else { /* Any object is already existing */
+ if (mode & FA_CREATE_NEW) /* Cannot create new */
+ LEAVE_FF(dj.fs, FR_EXIST);
+ dir = dj.dir;
+ if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR))) /* Cannot overwrite it (R/O or DIR) */
+ LEAVE_FF(dj.fs, FR_DENIED);
+ if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero on over write mode */
+ cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); /* Get start cluster */
+ ST_WORD(dir+DIR_FstClusHI, 0); /* cluster = 0 */
+ ST_WORD(dir+DIR_FstClusLO, 0);
+ ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */
+ dj.fs->wflag = 1;
+ ps = dj.fs->winsect; /* Remove the cluster chain */
+ if (cl) {
+ res = remove_chain(dj.fs, cl);
+ if (res) LEAVE_FF(dj.fs, res);
+ dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */
+ }
+ res = move_window(dj.fs, ps);
+ if (res != FR_OK) LEAVE_FF(dj.fs, res);
+ }
+ }
+ if (mode & FA_CREATE_ALWAYS) {
+ dir[DIR_Attr] = 0; /* Reset attribute */
+ ps = get_fattime();
+ ST_DWORD(dir+DIR_CrtTime, ps); /* Created time */
+ dj.fs->wflag = 1;
+ mode |= FA__WRITTEN; /* Set file changed flag */
+ }
+ }
+ /* Open an existing file */
+ else {
+#endif /* !_FS_READONLY */
+ if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */
+ dir = dj.dir;
+ if (!dir || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */
+ LEAVE_FF(dj.fs, FR_NO_FILE);
+#if !_FS_READONLY
+ if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
+ LEAVE_FF(dj.fs, FR_DENIED);
+ }
+ fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */
+ fp->dir_ptr = dj.dir;
+#endif
+ fp->flag = mode; /* File access mode */
+ fp->org_clust = /* File start cluster */
+ ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+ fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */
+ fp->fptr = 0; fp->csect = 255; /* File pointer */
+ fp->dsect = 0;
+ fp->fs = dj.fs; fp->id = dj.fs->id; /* Owner file system object of the file */
+
+ LEAVE_FF(dj.fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+ FIL *fp, /* Pointer to the file object */
+ void *buff, /* Pointer to data buffer */
+ UINT btr, /* Number of bytes to read */
+ UINT *br /* Pointer to number of bytes read */
+)
+{
+ FRESULT res;
+ DWORD clst, sect, remain;
+ UINT rcnt, cc;
+ BYTE *rbuff = buff;
+
+
+ *br = 0; /* Initialize bytes read */
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res != FR_OK) LEAVE_FF(fp->fs, res);
+ if (fp->flag & FA__ERROR) /* Check abort flag */
+ LEAVE_FF(fp->fs, FR_INT_ERR);
+ if (!(fp->flag & FA_READ)) /* Check access mode */
+ LEAVE_FF(fp->fs, FR_DENIED);
+ remain = fp->fsize - fp->fptr;
+ if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
+
+ for ( ; btr; /* Repeat until all data transferred */
+ rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
+ if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */
+ if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */
+ clst = (fp->fptr == 0) ? /* On the top of the file? */
+ fp->org_clust : get_fat(fp->fs, fp->curr_clust);
+ if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ fp->curr_clust = clst; /* Update current cluster */
+ fp->csect = 0; /* Reset sector offset in the cluster */
+ }
+ sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */
+ if (!sect) ABORT(fp->fs, FR_INT_ERR);
+ sect += fp->csect;
+ cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */
+ if (cc) { /* Read maximum contiguous sectors directly */
+ if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */
+ cc = fp->fs->csize - fp->csect;
+ if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+#if !_FS_READONLY && _FS_MINIMIZE <= 2
+#if _FS_TINY
+ if (fp->fs->wflag && fp->fs->winsect - sect < cc) /* Replace one of the read sectors with cached data if it contains a dirty sector */
+ mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
+#else
+ if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) /* Replace one of the read sectors with cached data if it contains a dirty sector */
+ mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
+#endif
+#endif
+ fp->csect += (BYTE)cc; /* Next sector address in the cluster */
+ rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */
+ continue;
+ }
+#if !_FS_TINY
+#if !_FS_READONLY
+ if (fp->flag & FA__DIRTY) { /* Write sector I/O buffer if needed */
+ if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ if (fp->dsect != sect) { /* Fill sector buffer with file data */
+ if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ }
+#endif
+ fp->dsect = sect;
+ fp->csect++; /* Next sector address in the cluster */
+ }
+ rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */
+ if (rcnt > btr) rcnt = btr;
+#if _FS_TINY
+ if (move_window(fp->fs, fp->dsect)) /* Move sector window */
+ ABORT(fp->fs, FR_DISK_ERR);
+ mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */
+#else
+ mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */
+#endif
+ }
+
+ LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+ FIL *fp, /* Pointer to the file object */
+ const void *buff, /* Pointer to the data to be written */
+ UINT btw, /* Number of bytes to write */
+ UINT *bw /* Pointer to number of bytes written */
+)
+{
+ FRESULT res;
+ DWORD clst, sect;
+ UINT wcnt, cc;
+ const BYTE *wbuff = buff;
+
+
+ *bw = 0; /* Initialize bytes written */
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res != FR_OK) LEAVE_FF(fp->fs, res);
+ if (fp->flag & FA__ERROR) /* Check abort flag */
+ LEAVE_FF(fp->fs, FR_INT_ERR);
+ if (!(fp->flag & FA_WRITE)) /* Check access mode */
+ LEAVE_FF(fp->fs, FR_DENIED);
+ if (fp->fsize + btw < fp->fsize) btw = 0; /* File size cannot reach 4GB */
+
+ for ( ; btw; /* Repeat until all data transferred */
+ wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
+ if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */
+ if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */
+ if (fp->fptr == 0) { /* On the top of the file? */
+ clst = fp->org_clust; /* Follow from the origin */
+ if (clst == 0) /* When there is no cluster chain, */
+ fp->org_clust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */
+ } else { /* Middle or end of the file */
+ clst = create_chain(fp->fs, fp->curr_clust); /* Follow or stretch cluster chain */
+ }
+ if (clst == 0) break; /* Could not allocate a new cluster (disk full) */
+ if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ fp->curr_clust = clst; /* Update current cluster */
+ fp->csect = 0; /* Reset sector address in the cluster */
+ }
+#if _FS_TINY
+ if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write back data buffer prior to following direct transfer */
+ ABORT(fp->fs, FR_DISK_ERR);
+#else
+ if (fp->flag & FA__DIRTY) { /* Write back data buffer prior to following direct transfer */
+ if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */
+ if (!sect) ABORT(fp->fs, FR_INT_ERR);
+ sect += fp->csect;
+ cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */
+ if (cc) { /* Write maximum contiguous sectors directly */
+ if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */
+ cc = fp->fs->csize - fp->csect;
+ if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+#if _FS_TINY
+ if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */
+ mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
+ fp->fs->wflag = 0;
+ }
+#else
+ if (fp->dsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */
+ mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ fp->csect += (BYTE)cc; /* Next sector address in the cluster */
+ wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */
+ continue;
+ }
+#if _FS_TINY
+ if (fp->fptr >= fp->fsize) { /* Avoid silly buffer filling at growing edge */
+ if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
+ fp->fs->winsect = sect;
+ }
+#else
+ if (fp->dsect != sect) { /* Fill sector buffer with file data */
+ if (fp->fptr < fp->fsize &&
+ disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ }
+#endif
+ fp->dsect = sect;
+ fp->csect++; /* Next sector address in the cluster */
+ }
+ wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Put partial sector into file I/O buffer */
+ if (wcnt > btw) wcnt = btw;
+#if _FS_TINY
+ if (move_window(fp->fs, fp->dsect)) /* Move sector window */
+ ABORT(fp->fs, FR_DISK_ERR);
+ mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */
+ fp->fs->wflag = 1;
+#else
+ mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */
+ fp->flag |= FA__DIRTY;
+#endif
+ }
+
+ if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */
+ fp->flag |= FA__WRITTEN; /* Set file changed flag */
+
+ LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File Object */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+ FIL *fp /* Pointer to the file object */
+)
+{
+ FRESULT res;
+ DWORD tim;
+ BYTE *dir;
+
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res == FR_OK) {
+ if (fp->flag & FA__WRITTEN) { /* Has the file been written? */
+#if !_FS_TINY /* Write-back dirty buffer */
+ if (fp->flag & FA__DIRTY) {
+ if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+ LEAVE_FF(fp->fs, FR_DISK_ERR);
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ /* Update the directory entry */
+ res = move_window(fp->fs, fp->dir_sect);
+ if (res == FR_OK) {
+ dir = fp->dir_ptr;
+ dir[DIR_Attr] |= AM_ARC; /* Set archive bit */
+ ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */
+ ST_WORD(dir+DIR_FstClusLO, fp->org_clust); /* Update start cluster */
+ ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16);
+ tim = get_fattime(); /* Updated time */
+ ST_DWORD(dir+DIR_WrtTime, tim);
+ fp->flag &= ~FA__WRITTEN;
+ fp->fs->wflag = 1;
+ res = sync(fp->fs);
+ }
+ }
+ }
+
+ LEAVE_FF(fp->fs, res);
+}
+
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+ FIL *fp /* Pointer to the file object to be closed */
+)
+{
+ FRESULT res;
+
+
+#if _FS_READONLY
+ res = validate(fp->fs, fp->id);
+ if (res == FR_OK) fp->fs = NULL;
+ LEAVE_FF(fp->fs, res);
+#else
+ res = f_sync(fp);
+ if (res == FR_OK) fp->fs = NULL;
+ return res;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Current Drive/Directory */
+/*-----------------------------------------------------------------------*/
+
+#if _FS_RPATH
+
+FRESULT f_chdrive (
+ BYTE drv /* Drive number */
+)
+{
+ if (drv >= _DRIVES) return FR_INVALID_DRIVE;
+
+ Drive = drv;
+
+ return FR_OK;
+}
+
+
+
+
+FRESULT f_chdir (
+ const XCHAR *path /* Pointer to the directory path */
+)
+{
+ FRESULT res;
+ DIR dj;
+ NAMEBUF(sfn, lfn);
+ BYTE *dir;
+
+
+ res = chk_mounted(&path, &dj.fs, 0);
+ if (res == FR_OK) {
+ INITBUF(dj, sfn, lfn);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (res == FR_OK) { /* Follow completed */
+ dir = dj.dir; /* Pointer to the entry */
+ if (!dir) {
+ dj.fs->cdir = 0; /* No entry (root dir) */
+ } else {
+ if (dir[DIR_Attr] & AM_DIR) /* Reached to the dir */
+ dj.fs->cdir = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+ else
+ res = FR_NO_PATH; /* Could not reach the dir (it is a file) */
+ }
+ }
+ if (res == FR_NO_FILE) res = FR_NO_PATH;
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+#endif
+
+
+
+#if _FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File R/W Pointer */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+ FIL *fp, /* Pointer to the file object */
+ DWORD ofs /* File pointer from top of file */
+)
+{
+ FRESULT res;
+ DWORD clst, bcs, nsect, ifptr;
+
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res != FR_OK) LEAVE_FF(fp->fs, res);
+ if (fp->flag & FA__ERROR) /* Check abort flag */
+ LEAVE_FF(fp->fs, FR_INT_ERR);
+ if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */
+#if !_FS_READONLY
+ && !(fp->flag & FA_WRITE)
+#endif
+ ) ofs = fp->fsize;
+
+ ifptr = fp->fptr;
+ fp->fptr = nsect = 0; fp->csect = 255;
+ if (ofs > 0) {
+ bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */
+ if (ifptr > 0 &&
+ (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
+ fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */
+ ofs -= fp->fptr;
+ clst = fp->curr_clust;
+ } else { /* When seek to back cluster, */
+ clst = fp->org_clust; /* start from the first cluster */
+#if !_FS_READONLY
+ if (clst == 0) { /* If no cluster chain, create a new chain */
+ clst = create_chain(fp->fs, 0);
+ if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ fp->org_clust = clst;
+ }
+#endif
+ fp->curr_clust = clst;
+ }
+ if (clst != 0) {
+ while (ofs > bcs) { /* Cluster following loop */
+#if !_FS_READONLY
+ if (fp->flag & FA_WRITE) { /* Check if in write mode or not */
+ clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */
+ if (clst == 0) { /* When disk gets full, clip file size */
+ ofs = bcs; break;
+ }
+ } else
+#endif
+ clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR);
+ fp->curr_clust = clst;
+ fp->fptr += bcs;
+ ofs -= bcs;
+ }
+ fp->fptr += ofs;
+ fp->csect = (BYTE)(ofs / SS(fp->fs)); /* Sector offset in the cluster */
+ if (ofs % SS(fp->fs)) {
+ nsect = clust2sect(fp->fs, clst); /* Current sector */
+ if (!nsect) ABORT(fp->fs, FR_INT_ERR);
+ nsect += fp->csect;
+ fp->csect++;
+ }
+ }
+ }
+ if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {
+#if !_FS_TINY
+#if !_FS_READONLY
+ if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */
+ if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+#endif
+ fp->dsect = nsect;
+ }
+#if !_FS_READONLY
+ if (fp->fptr > fp->fsize) { /* Set changed flag if the file size is extended */
+ fp->fsize = fp->fptr;
+ fp->flag |= FA__WRITTEN;
+ }
+#endif
+
+ LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+#if _FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directroy Object */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+ DIR *dj, /* Pointer to directory object to create */
+ const XCHAR *path /* Pointer to the directory path */
+)
+{
+ FRESULT res;
+ NAMEBUF(sfn, lfn);
+ BYTE *dir;
+
+ res = chk_mounted(&path, &dj->fs, 0);
+ if (res == FR_OK) {
+ INITBUF((*dj), sfn, lfn);
+ res = follow_path(dj, path); /* Follow the path to the directory */
+ if (res == FR_OK) { /* Follow completed */
+ dir = dj->dir;
+ if (dir) { /* It is not the root dir */
+ if (dir[DIR_Attr] & AM_DIR) { /* The object is a directory */
+ dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+ } else { /* The object is not a directory */
+ res = FR_NO_PATH;
+ }
+ }
+ if (res == FR_OK) {
+ dj->id = dj->fs->id;
+ res = dir_seek(dj, 0); /* Rewind dir */
+ }
+ }
+ if (res == FR_NO_FILE) res = FR_NO_PATH;
+ }
+
+ LEAVE_FF(dj->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entry in Sequense */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+ DIR *dj, /* Pointer to the open directory object */
+ FILINFO *fno /* Pointer to file information to return */
+)
+{
+ FRESULT res;
+ NAMEBUF(sfn, lfn);
+
+
+ res = validate(dj->fs, dj->id); /* Check validity of the object */
+ if (res == FR_OK) {
+ INITBUF((*dj), sfn, lfn);
+ if (!fno) {
+ res = dir_seek(dj, 0);
+ } else {
+ res = dir_read(dj);
+ if (res == FR_NO_FILE) {
+ dj->sect = 0;
+ res = FR_OK;
+ }
+ if (res == FR_OK) { /* A valid entry is found */
+ get_fileinfo(dj, fno); /* Get the object information */
+ res = dir_next(dj, FALSE); /* Increment index for next */
+ if (res == FR_NO_FILE) {
+ dj->sect = 0;
+ res = FR_OK;
+ }
+ }
+ }
+ }
+
+ LEAVE_FF(dj->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+ const XCHAR *path, /* Pointer to the file path */
+ FILINFO *fno /* Pointer to file information to return */
+)
+{
+ FRESULT res;
+ DIR dj;
+ NAMEBUF(sfn, lfn);
+
+
+ res = chk_mounted(&path, &dj.fs, 0);
+ if (res == FR_OK) {
+ INITBUF(dj, sfn, lfn);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (res == FR_OK) { /* Follow completed */
+ if (dj.dir) /* Found an object */
+ get_fileinfo(&dj, fno);
+ else /* It is root dir */
+ res = FR_INVALID_NAME;
+ }
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+ const XCHAR *path, /* Pointer to the logical drive number (root dir) */
+ DWORD *nclst, /* Pointer to the variable to return number of free clusters */
+ FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */
+)
+{
+ FRESULT res;
+ DWORD n, clst, sect, stat;
+ UINT i;
+ BYTE fat, *p;
+
+
+ /* Get drive number */
+ res = chk_mounted(&path, fatfs, 0);
+ if (res != FR_OK) LEAVE_FF(*fatfs, res);
+
+ /* If number of free cluster is valid, return it without cluster scan. */
+ if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) {
+ *nclst = (*fatfs)->free_clust;
+ LEAVE_FF(*fatfs, FR_OK);
+ }
+
+ /* Get number of free clusters */
+ fat = (*fatfs)->fs_type;
+ n = 0;
+ if (fat == FS_FAT12) {
+ clst = 2;
+ do {
+ stat = get_fat(*fatfs, clst);
+ if (stat == 0xFFFFFFFF) LEAVE_FF(*fatfs, FR_DISK_ERR);
+ if (stat == 1) LEAVE_FF(*fatfs, FR_INT_ERR);
+ if (stat == 0) n++;
+ } while (++clst < (*fatfs)->max_clust);
+ } else {
+ clst = (*fatfs)->max_clust;
+ sect = (*fatfs)->fatbase;
+ i = 0; p = 0;
+ do {
+ if (!i) {
+ res = move_window(*fatfs, sect++);
+ if (res != FR_OK)
+ LEAVE_FF(*fatfs, res);
+ p = (*fatfs)->win;
+ i = SS(*fatfs);
+ }
+ if (fat == FS_FAT16) {
+ if (LD_WORD(p) == 0) n++;
+ p += 2; i -= 2;
+ } else {
+ if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
+ p += 4; i -= 4;
+ }
+ } while (--clst);
+ }
+ (*fatfs)->free_clust = n;
+ if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1;
+ *nclst = n;
+
+ LEAVE_FF(*fatfs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+ FIL *fp /* Pointer to the file object */
+)
+{
+ FRESULT res;
+ DWORD ncl;
+
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res != FR_OK) LEAVE_FF(fp->fs, res);
+ if (fp->flag & FA__ERROR) /* Check abort flag */
+ LEAVE_FF(fp->fs, FR_INT_ERR);
+ if (!(fp->flag & FA_WRITE)) /* Check access mode */
+ LEAVE_FF(fp->fs, FR_DENIED);
+
+ if (fp->fsize > fp->fptr) {
+ fp->fsize = fp->fptr; /* Set file size to current R/W point */
+ fp->flag |= FA__WRITTEN;
+ if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */
+ res = remove_chain(fp->fs, fp->org_clust);
+ fp->org_clust = 0;
+ } else { /* When truncate a part of the file, remove remaining clusters */
+ ncl = get_fat(fp->fs, fp->curr_clust);
+ res = FR_OK;
+ if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+ if (ncl == 1) res = FR_INT_ERR;
+ if (res == FR_OK && ncl < fp->fs->max_clust) {
+ res = put_fat(fp->fs, fp->curr_clust, 0x0FFFFFFF);
+ if (res == FR_OK) res = remove_chain(fp->fs, ncl);
+ }
+ }
+ }
+ if (res != FR_OK) fp->flag |= FA__ERROR;
+
+ LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File or Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+ const XCHAR *path /* Pointer to the file or directory path */
+)
+{
+ FRESULT res;
+ DIR dj, sdj;
+ NAMEBUF(sfn, lfn);
+ BYTE *dir;
+ DWORD dclst;
+
+
+ res = chk_mounted(&path, &dj.fs, 1);
+ if (res != FR_OK) LEAVE_FF(dj.fs, res);
+
+ INITBUF(dj, sfn, lfn);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME;
+ if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */
+
+ dir = dj.dir;
+ if (!dir) /* Is it the root directory? */
+ LEAVE_FF(dj.fs, FR_INVALID_NAME);
+ if (dir[DIR_Attr] & AM_RDO) /* Is it a R/O object? */
+ LEAVE_FF(dj.fs, FR_DENIED);
+ dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+
+ if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */
+ if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR);
+ mem_cpy(&sdj, &dj, sizeof(DIR)); /* Check if the sub-dir is empty or not */
+ sdj.sclust = dclst;
+ res = dir_seek(&sdj, 2);
+ if (res != FR_OK) LEAVE_FF(dj.fs, res);
+ res = dir_read(&sdj);
+ if (res == FR_OK) res = FR_DENIED; /* Not empty sub-dir */
+ if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res);
+ }
+
+ res = dir_remove(&dj); /* Remove directory entry */
+ if (res == FR_OK) {
+ if (dclst)
+ res = remove_chain(dj.fs, dclst); /* Remove the cluster chain */
+ if (res == FR_OK) res = sync(dj.fs);
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+ const XCHAR *path /* Pointer to the directory path */
+)
+{
+ FRESULT res;
+ DIR dj;
+ NAMEBUF(sfn, lfn);
+ BYTE *dir, n;
+ DWORD dsect, dclst, pclst, tim;
+
+
+ res = chk_mounted(&path, &dj.fs, 1);
+ if (res != FR_OK) LEAVE_FF(dj.fs, res);
+
+ INITBUF(dj, sfn, lfn);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (res == FR_OK) res = FR_EXIST; /* Any file or directory is already existing */
+ if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME;
+ if (res != FR_NO_FILE) /* Any error occurred */
+ LEAVE_FF(dj.fs, res);
+
+ dclst = create_chain(dj.fs, 0); /* Allocate a new cluster for new directory table */
+ res = FR_OK;
+ if (dclst == 0) res = FR_DENIED;
+ if (dclst == 1) res = FR_INT_ERR;
+ if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR;
+ if (res == FR_OK)
+ res = move_window(dj.fs, 0);
+ if (res != FR_OK) LEAVE_FF(dj.fs, res);
+ dsect = clust2sect(dj.fs, dclst);
+
+ dir = dj.fs->win; /* Initialize the new directory table */
+ mem_set(dir, 0, SS(dj.fs));
+ mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */
+ dir[DIR_Name] = '.';
+ dir[DIR_Attr] = AM_DIR;
+ tim = get_fattime();
+ ST_DWORD(dir+DIR_WrtTime, tim);
+ ST_WORD(dir+DIR_FstClusLO, dclst);
+ ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
+ mem_cpy(dir+32, dir, 32); /* Create ".." entry */
+ dir[33] = '.';
+ pclst = dj.sclust;
+ if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase)
+ pclst = 0;
+ ST_WORD(dir+32+DIR_FstClusLO, pclst);
+ ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16);
+ for (n = 0; n < dj.fs->csize; n++) { /* Write dot entries and clear left sectors */
+ dj.fs->winsect = dsect++;
+ dj.fs->wflag = 1;
+ res = move_window(dj.fs, 0);
+ if (res) LEAVE_FF(dj.fs, res);
+ mem_set(dir, 0, SS(dj.fs));
+ }
+
+ res = dir_register(&dj);
+ if (res != FR_OK) {
+ remove_chain(dj.fs, dclst);
+ } else {
+ dir = dj.dir;
+ dir[DIR_Attr] = AM_DIR; /* Attribute */
+ ST_DWORD(dir+DIR_WrtTime, tim); /* Create time */
+ ST_WORD(dir+DIR_FstClusLO, dclst); /* Table start cluster */
+ ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
+ dj.fs->wflag = 1;
+ res = sync(dj.fs);
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change File Attribute */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+ const XCHAR *path, /* Pointer to the file path */
+ BYTE value, /* Attribute bits */
+ BYTE mask /* Attribute mask to change */
+)
+{
+ FRESULT res;
+ DIR dj;
+ NAMEBUF(sfn, lfn);
+ BYTE *dir;
+
+
+ res = chk_mounted(&path, &dj.fs, 1);
+ if (res == FR_OK) {
+ INITBUF(dj, sfn, lfn);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME;
+ if (res == FR_OK) {
+ dir = dj.dir;
+ if (!dir) { /* Is it a root directory? */
+ res = FR_INVALID_NAME;
+ } else { /* File or sub directory */
+ mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */
+ dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
+ dj.fs->wflag = 1;
+ res = sync(dj.fs);
+ }
+ }
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+ const XCHAR *path, /* Pointer to the file/directory name */
+ const FILINFO *fno /* Pointer to the time-stamp to be set */
+)
+{
+ FRESULT res;
+ DIR dj;
+ NAMEBUF(sfn, lfn);
+ BYTE *dir;
+
+
+ res = chk_mounted(&path, &dj.fs, 1);
+ if (res == FR_OK) {
+ INITBUF(dj, sfn, lfn);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME;
+ if (res == FR_OK) {
+ dir = dj.dir;
+ if (!dir) { /* Root directory */
+ res = FR_INVALID_NAME;
+ } else { /* File or sub-directory */
+ ST_WORD(dir+DIR_WrtTime, fno->ftime);
+ ST_WORD(dir+DIR_WrtDate, fno->fdate);
+ dj.fs->wflag = 1;
+ res = sync(dj.fs);
+ }
+ }
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename File/Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+ const XCHAR *path_old, /* Pointer to the old name */
+ const XCHAR *path_new /* Pointer to the new name */
+)
+{
+ FRESULT res;
+ DIR dj_old, dj_new;
+ NAMEBUF(sfn, lfn);
+ BYTE buf[21], *dir;
+ DWORD dw;
+
+
+ INITBUF(dj_old, sfn, lfn);
+ res = chk_mounted(&path_old, &dj_old.fs, 1);
+ if (res == FR_OK) {
+ dj_new.fs = dj_old.fs;
+ res = follow_path(&dj_old, path_old); /* Check old object */
+ if (_FS_RPATH && res == FR_OK && (dj_old.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME;
+ }
+ if (res != FR_OK) LEAVE_FF(dj_old.fs, res); /* The old object is not found */
+
+ if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE); /* Is root dir? */
+ mem_cpy(buf, dj_old.dir+DIR_Attr, 21); /* Save the object information */
+
+ mem_cpy(&dj_new, &dj_old, sizeof(DIR));
+ res = follow_path(&dj_new, path_new); /* Check new object */
+ if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */
+ if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */
+ res = dir_register(&dj_new); /* Register the new object */
+ if (res == FR_OK) {
+ dir = dj_new.dir; /* Copy object information into new entry */
+ mem_cpy(dir+13, buf+2, 19);
+ dir[DIR_Attr] = buf[0] | AM_ARC;
+ dj_old.fs->wflag = 1;
+ if (dir[DIR_Attr] & AM_DIR) { /* Update .. entry in the directory if needed */
+ dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO));
+ if (!dw) {
+ res = FR_INT_ERR;
+ } else {
+ res = move_window(dj_new.fs, dw);
+ dir = dj_new.fs->win+32;
+ if (res == FR_OK && dir[1] == '.') {
+ dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust;
+ ST_WORD(dir+DIR_FstClusLO, dw);
+ ST_WORD(dir+DIR_FstClusHI, dw >> 16);
+ dj_new.fs->wflag = 1;
+ }
+ }
+ }
+ if (res == FR_OK) {
+ res = dir_remove(&dj_old); /* Remove old entry */
+ if (res == FR_OK)
+ res = sync(dj_old.fs);
+ }
+ }
+ }
+
+ LEAVE_FF(dj_old.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _FS_MINIMIZE == 0 */
+#endif /* _FS_MINIMIZE <= 1 */
+#endif /* _FS_MINIMIZE <= 2 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Forward data to the stream directly (Available on only _FS_TINY cfg) */
+/*-----------------------------------------------------------------------*/
+#if _USE_FORWARD && _FS_TINY
+
+FRESULT f_forward (
+ FIL *fp, /* Pointer to the file object */
+ UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
+ UINT btr, /* Number of bytes to forward */
+ UINT *bf /* Pointer to number of bytes forwarded */
+)
+{
+ FRESULT res;
+ DWORD remain, clst, sect;
+ UINT rcnt;
+
+
+ *bf = 0;
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res != FR_OK) LEAVE_FF(fp->fs, res);
+ if (fp->flag & FA__ERROR) /* Check error flag */
+ LEAVE_FF(fp->fs, FR_INT_ERR);
+ if (!(fp->flag & FA_READ)) /* Check access mode */
+ LEAVE_FF(fp->fs, FR_DENIED);
+
+ remain = fp->fsize - fp->fptr;
+ if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
+
+ for ( ; btr && (*func)(NULL, 0); /* Repeat until all data transferred or stream becomes busy */
+ fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
+ if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */
+ if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */
+ clst = (fp->fptr == 0) ? /* On the top of the file? */
+ fp->org_clust : get_fat(fp->fs, fp->curr_clust);
+ if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ fp->curr_clust = clst; /* Update current cluster */
+ fp->csect = 0; /* Reset sector address in the cluster */
+ }
+ fp->csect++; /* Next sector address in the cluster */
+ }
+ sect = clust2sect(fp->fs, fp->curr_clust); /* Get current data sector */
+ if (!sect) ABORT(fp->fs, FR_INT_ERR);
+ sect += fp->csect - 1;
+ if (move_window(fp->fs, sect)) /* Move sector window */
+ ABORT(fp->fs, FR_DISK_ERR);
+ fp->dsect = sect;
+ rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */
+ if (rcnt > btr) rcnt = btr;
+ rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
+ if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
+ }
+
+ LEAVE_FF(fp->fs, FR_OK);
+}
+#endif /* _USE_FORWARD */
+
+
+
+#if _USE_MKFS && !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create File System on the Drive */
+/*-----------------------------------------------------------------------*/
+#define N_ROOTDIR 512 /* Multiple of 32 and <= 2048 */
+#define N_FATS 1 /* 1 or 2 */
+#define MAX_SECTOR 131072000UL /* Maximum partition size */
+#define MIN_SECTOR 2000UL /* Minimum partition size */
+
+
+FRESULT f_mkfs (
+ BYTE drv, /* Logical drive number */
+ BYTE partition, /* Partitioning rule 0:FDISK, 1:SFD */
+ WORD allocsize /* Allocation unit size [bytes] */
+)
+{
+ static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000, 0 };
+ static const WORD cstbl[] = { 32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512 };
+ BYTE fmt, m, *tbl;
+ DWORD b_part, b_fat, b_dir, b_data; /* Area offset (LBA) */
+ DWORD n_part, n_rsv, n_fat, n_dir; /* Area size */
+ DWORD n_clst, d, n;
+ WORD as;
+ FATFS *fs;
+ DSTATUS stat;
+
+
+ /* Check validity of the parameters */
+ if (drv >= _DRIVES) return FR_INVALID_DRIVE;
+ if (partition >= 2) return FR_MKFS_ABORTED;
+
+ /* Check mounted drive and clear work area */
+ fs = FatFs[drv];
+ if (!fs) return FR_NOT_ENABLED;
+ fs->fs_type = 0;
+ drv = LD2PD(drv);
+
+ /* Get disk statics */
+ stat = disk_initialize(drv);
+ if (stat & STA_NOINIT) return FR_NOT_READY;
+ if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+#if _MAX_SS != 512 /* Get disk sector size */
+ if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
+ || SS(fs) > _MAX_SS)
+ return FR_MKFS_ABORTED;
+#endif
+ if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR)
+ return FR_MKFS_ABORTED;
+ if (n_part > MAX_SECTOR) n_part = MAX_SECTOR;
+ b_part = (!partition) ? 63 : 0; /* Boot sector */
+ n_part -= b_part;
+ for (d = 512; d <= 32768U && d != allocsize; d <<= 1) ; /* Check validity of the allocation unit size */
+ if (d != allocsize) allocsize = 0;
+ if (!allocsize) { /* Auto selection of cluster size */
+ d = n_part;
+ for (as = SS(fs); as > 512U; as >>= 1) d >>= 1;
+ for (n = 0; d < sstbl[n]; n++) ;
+ allocsize = cstbl[n];
+ }
+ if (allocsize < SS(fs)) allocsize = SS(fs);
+
+ allocsize /= SS(fs); /* Number of sectors per cluster */
+
+ /* Pre-compute number of clusters and FAT type */
+ n_clst = n_part / allocsize;
+ fmt = FS_FAT12;
+ if (n_clst >= 0xFF5) fmt = FS_FAT16;
+ if (n_clst >= 0xFFF5) fmt = FS_FAT32;
+
+ /* Determine offset and size of FAT structure */
+ switch (fmt) {
+ case FS_FAT12:
+ n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs);
+ n_rsv = 1 + partition;
+ n_dir = N_ROOTDIR * 32 / SS(fs);
+ break;
+ case FS_FAT16:
+ n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs);
+ n_rsv = 1 + partition;
+ n_dir = N_ROOTDIR * 32 / SS(fs);
+ break;
+ default:
+ n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
+ n_rsv = 33 - partition;
+ n_dir = 0;
+ }
+ b_fat = b_part + n_rsv; /* FATs start sector */
+ b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */
+ b_data = b_dir + n_dir; /* Data start sector */
+
+ /* Align data start sector to erase block boundary (for flash memory media) */
+ if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED;
+ n = (b_data + n - 1) & ~(n - 1);
+ n_fat += (n - b_data) / N_FATS;
+ /* b_dir and b_data are no longer used below */
+
+ /* Determine number of cluster and final check of validity of the FAT type */
+ n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize;
+ if ( (fmt == FS_FAT16 && n_clst < 0xFF5)
+ || (fmt == FS_FAT32 && n_clst < 0xFFF5))
+ return FR_MKFS_ABORTED;
+
+ /* Create partition table if needed */
+ if (!partition) {
+ DWORD n_disk = b_part + n_part;
+
+ mem_set(fs->win, 0, SS(fs));
+ tbl = fs->win+MBR_Table;
+ ST_DWORD(tbl, 0x00010180); /* Partition start in CHS */
+ if (n_disk < 63UL * 255 * 1024) { /* Partition end in CHS */
+ n_disk = n_disk / 63 / 255;
+ tbl[7] = (BYTE)n_disk;
+ tbl[6] = (BYTE)((n_disk >> 2) | 63);
+ } else {
+ ST_WORD(&tbl[6], 0xFFFF);
+ }
+ tbl[5] = 254;
+ if (fmt != FS_FAT32) /* System ID */
+ tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06;
+ else
+ tbl[4] = 0x0c;
+ ST_DWORD(tbl+8, 63); /* Partition start in LBA */
+ ST_DWORD(tbl+12, n_part); /* Partition size in LBA */
+ ST_WORD(tbl+64, 0xAA55); /* Signature */
+ if (disk_write(drv, fs->win, 0, 1) != RES_OK)
+ return FR_DISK_ERR;
+ partition = 0xF8;
+ } else {
+ partition = 0xF0;
+ }
+
+ /* Create boot record */
+ tbl = fs->win; /* Clear buffer */
+ mem_set(tbl, 0, SS(fs));
+ ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB); /* Boot code (jmp $, nop) */
+ ST_WORD(tbl+BPB_BytsPerSec, SS(fs)); /* Sector size */
+ tbl[BPB_SecPerClus] = (BYTE)allocsize; /* Sectors per cluster */
+ ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */
+ tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */
+ ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */
+ if (n_part < 0x10000) { /* Number of total sectors */
+ ST_WORD(tbl+BPB_TotSec16, n_part);
+ } else {
+ ST_DWORD(tbl+BPB_TotSec32, n_part);
+ }
+ tbl[BPB_Media] = partition; /* Media descriptor */
+ ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */
+ ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */
+ ST_DWORD(tbl+BPB_HiddSec, b_part); /* Hidden sectors */
+ n = get_fattime(); /* Use current time as a VSN */
+ if (fmt != FS_FAT32) {
+ ST_DWORD(tbl+BS_VolID, n); /* Volume serial number */
+ ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of sectors per FAT */
+ tbl[BS_DrvNum] = 0x80; /* Drive number */
+ tbl[BS_BootSig] = 0x29; /* Extended boot signature */
+ mem_cpy(tbl+BS_VolLab, "NO NAME FAT ", 19); /* Volume label, FAT signature */
+ } else {
+ ST_DWORD(tbl+BS_VolID32, n); /* Volume serial number */
+ ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of sectors per FAT */
+ ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory cluster (2) */
+ ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (bs+1) */
+ ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (bs+6) */
+ tbl[BS_DrvNum32] = 0x80; /* Drive number */
+ tbl[BS_BootSig32] = 0x29; /* Extended boot signature */
+ mem_cpy(tbl+BS_VolLab32, "NO NAME FAT32 ", 19); /* Volume label, FAT signature */
+ }
+ ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature */
+ if (SS(fs) > 512U) {
+ ST_WORD(tbl+SS(fs)-2, 0xAA55);
+ }
+ if (disk_write(drv, tbl, b_part+0, 1) != RES_OK)
+ return FR_DISK_ERR;
+ if (fmt == FS_FAT32)
+ disk_write(drv, tbl, b_part+6, 1);
+
+ /* Initialize FAT area */
+ for (m = 0; m < N_FATS; m++) {
+ mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */
+ if (fmt != FS_FAT32) {
+ n = (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
+ n |= partition;
+ ST_DWORD(tbl, n); /* Reserve cluster #0-1 (FAT12/16) */
+ } else {
+ ST_DWORD(tbl+0, 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */
+ ST_DWORD(tbl+4, 0xFFFFFFFF);
+ ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */
+ }
+ if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+ return FR_DISK_ERR;
+ mem_set(tbl, 0, SS(fs)); /* Following FAT entries are filled by zero */
+ for (n = 1; n < n_fat; n++) {
+ if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+ return FR_DISK_ERR;
+ }
+ }
+
+ /* Initialize Root directory */
+ m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir);
+ do {
+ if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+ return FR_DISK_ERR;
+ } while (--m);
+
+ /* Create FSInfo record if needed */
+ if (fmt == FS_FAT32) {
+ ST_WORD(tbl+BS_55AA, 0xAA55);
+ ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
+ ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
+ ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);
+ ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF);
+ disk_write(drv, tbl, b_part+1, 1);
+ disk_write(drv, tbl, b_part+7, 1);
+ }
+
+ return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR;
+}
+
+#endif /* _USE_MKFS && !_FS_READONLY */
+
+
+
+
+#if _USE_STRFUNC
+/*-----------------------------------------------------------------------*/
+/* Get a string from the file */
+/*-----------------------------------------------------------------------*/
+char* f_gets (
+ char* buff, /* Pointer to the string buffer to read */
+ int len, /* Size of string buffer */
+ FIL* fil /* Pointer to the file object */
+)
+{
+ int i = 0;
+ char *p = buff;
+ UINT rc;
+
+
+ while (i < len - 1) { /* Read bytes until buffer gets filled */
+ f_read(fil, p, 1, &rc);
+ if (rc != 1) break; /* Break when no data to read */
+#if _USE_STRFUNC >= 2
+ if (*p == '\r') continue; /* Strip '\r' */
+#endif
+ i++;
+ if (*p++ == '\n') break; /* Break when reached end of line */
+ }
+ *p = 0;
+ return i ? buff : NULL; /* When no data read (eof or error), return with error. */
+}
+
+
+
+#if !_FS_READONLY
+#include
+/*-----------------------------------------------------------------------*/
+/* Put a character to the file */
+/*-----------------------------------------------------------------------*/
+int f_putc (
+ int chr, /* A character to be output */
+ FIL* fil /* Pointer to the file object */
+)
+{
+ UINT bw;
+ char c;
+
+
+#if _USE_STRFUNC >= 2
+ if (chr == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */
+#endif
+ if (!fil) { /* Special value may be used to switch the destination to any other device */
+ /* put_console(chr); */
+ return chr;
+ }
+ c = (char)chr;
+ f_write(fil, &c, 1, &bw); /* Write a byte to the file */
+ return bw ? chr : EOF; /* Return the result */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a string to the file */
+/*-----------------------------------------------------------------------*/
+int f_puts (
+ const char* str, /* Pointer to the string to be output */
+ FIL* fil /* Pointer to the file object */
+)
+{
+ int n;
+
+
+ for (n = 0; *str; str++, n++) {
+ if (f_putc(*str, fil) == EOF) return EOF;
+ }
+ return n;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a formatted string to the file */
+/*-----------------------------------------------------------------------*/
+int f_printf (
+ FIL* fil, /* Pointer to the file object */
+ const char* str, /* Pointer to the format string */
+ ... /* Optional arguments... */
+)
+{
+ va_list arp;
+ UCHAR c, f, r;
+ ULONG val;
+ char s[16];
+ int i, w, res, cc;
+
+
+ va_start(arp, str);
+
+ for (cc = res = 0; cc != EOF; res += cc) {
+ c = *str++;
+ if (c == 0) break; /* End of string */
+ if (c != '%') { /* Non escape character */
+ cc = f_putc(c, fil);
+ if (cc != EOF) cc = 1;
+ continue;
+ }
+ w = f = 0;
+ c = *str++;
+ if (c == '0') { /* Flag: '0' padding */
+ f = 1; c = *str++;
+ }
+ while (c >= '0' && c <= '9') { /* Precision */
+ w = w * 10 + (c - '0');
+ c = *str++;
+ }
+ if (c == 'l') { /* Prefix: Size is long int */
+ f |= 2; c = *str++;
+ }
+ if (c == 's') { /* Type is string */
+ cc = f_puts(va_arg(arp, char*), fil);
+ continue;
+ }
+ if (c == 'c') { /* Type is character */
+ cc = f_putc(va_arg(arp, int), fil);
+ if (cc != EOF) cc = 1;
+ continue;
+ }
+ r = 0;
+ if (c == 'd') r = 10; /* Type is signed decimal */
+ if (c == 'u') r = 10; /* Type is unsigned decimal */
+ if (c == 'X') r = 16; /* Type is unsigned hexdecimal */
+ if (r == 0) break; /* Unknown type */
+ if (f & 2) { /* Get the value */
+ val = (ULONG)va_arg(arp, long);
+ } else {
+ val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int);
+ }
+ /* Put numeral string */
+ if (c == 'd') {
+ if (val & 0x80000000) {
+ val = 0 - val;
+ f |= 4;
+ }
+ }
+ i = sizeof(s) - 1; s[i] = 0;
+ do {
+ c = (UCHAR)(val % r + '0');
+ if (c > '9') c += 7;
+ s[--i] = c;
+ val /= r;
+ } while (i && val);
+ if (i && (f & 4)) s[--i] = '-';
+ w = sizeof(s) - 1 - w;
+ while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';
+ cc = f_puts(&s[i], fil);
+ }
+
+ va_end(arp);
+ return (cc == EOF) ? cc : res;
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_STRFUNC */
diff --git a/RISC-V/Source/gd32vf103_it.c b/RISC-V/Source/gd32vf103_it.c
new file mode 100644
index 0000000..87cf4a1
--- /dev/null
+++ b/RISC-V/Source/gd32vf103_it.c
@@ -0,0 +1,63 @@
+/*!
+ \file gd32vf103_it.c
+ \brief main interrupt service routines
+
+ \version 2019-06-05, V1.0.0, firmware for GD32VF103
+*/
+
+/*
+ Copyright (c) 2019, GigaDevice Semiconductor Inc.
+
+ 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. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "drv_usbh_int.h"
+#include "drv_usb_hw.h"
+#include "gd32vf103_it.h"
+
+extern usb_core_driver usbh_msc_core;
+
+extern void usb_timer_irq (void);
+
+/*!
+ \brief this function handles USBD interrupt
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void USBFS_IRQHandler (void)
+{
+ usbh_isr (&usbh_msc_core);
+}
+
+/*!
+ \brief this function handles timer2 updata interrupt request.
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void TIMER2_IRQHandler(void)
+{
+ usb_timer_irq();
+}
diff --git a/RISC-V/Source/gd32vf103_usb_hw.c b/RISC-V/Source/gd32vf103_usb_hw.c
new file mode 100644
index 0000000..4447273
--- /dev/null
+++ b/RISC-V/Source/gd32vf103_usb_hw.c
@@ -0,0 +1,256 @@
+/*!
+ \file gd32vf103_usb_hw.c
+ \brief this file implements the board support package for the USB host library
+
+ \version 2019-06-05, V1.0.0, firmware for GD32VF103
+*/
+
+/*
+ Copyright (c) 2019, GigaDevice Semiconductor Inc.
+
+ 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. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "drv_usb_hw.h"
+#include
+#include
+#include
+
+#define TIM_MSEC_DELAY 0x01
+#define TIM_USEC_DELAY 0x02
+
+#define HOST_POWERSW_PORT_RCC RCU_GPIOD
+#define HOST_POWERSW_PORT GPIOD
+#define HOST_POWERSW_VBUS GPIO_PIN_13
+
+__IO uint32_t delay_time = 0;
+__IO uint32_t usbfs_prescaler = 0;
+__IO uint32_t timer_prescaler = 5;
+
+static void hwp_time_set (uint8_t unit);
+static void hwp_delay (uint32_t ntime, uint8_t unit);
+
+/*!
+ \brief configure USB clock
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usb_rcu_config (void)
+{
+ uint32_t system_clock = rcu_clock_freq_get(CK_SYS);
+
+ if (system_clock == 48000000) {
+ usbfs_prescaler = RCU_CKUSB_CKPLL_DIV1;
+ timer_prescaler = 3;
+ } else if (system_clock == 72000000) {
+ usbfs_prescaler = RCU_CKUSB_CKPLL_DIV1_5;
+ timer_prescaler = 5;
+ } else if (system_clock == 96000000) {
+ usbfs_prescaler = RCU_CKUSB_CKPLL_DIV2;
+ timer_prescaler = 7;
+ } else {
+ /* reserved */
+ }
+
+ rcu_usb_clock_config(usbfs_prescaler);
+ rcu_periph_clock_enable(RCU_USBFS);
+}
+
+/*!
+ \brief configure USB global interrupt
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usb_intr_config (void)
+{
+ eclic_irq_enable((uint8_t)USBFS_IRQn, 1, 0);
+
+#ifdef USB_OTG_FS_LOW_PWR_MGMT_SUPPORT
+
+ /* enable the power module clock */
+ rcu_periph_clock_enable(RCU_PMU);
+
+ /* USB wakeup EXTI line configuration */
+ exti_interrupt_flag_clear(EXTI_18);
+ exti_init(EXTI_18, EXTI_INTERRUPT, EXTI_TRIG_RISING);
+ exti_interrupt_enable(EXTI_18);
+
+ eclic_irq_enable((uint8_t)USBFS_WKUP_IRQn, 3U, 0U);
+
+#endif /* USBHS_LOW_PWR_MGMT_SUPPORT */
+}
+
+/*!
+ \brief drives the VBUS signal through gpio
+ \param[in] state: VBUS states
+ \param[out] none
+ \retval none
+*/
+void usb_vbus_drive (uint8_t state)
+{
+ if (0 == state)
+ {
+ /* DISABLE is needed on output of the Power Switch */
+ gpio_bit_reset(HOST_POWERSW_PORT, HOST_POWERSW_VBUS);
+ }
+ else
+ {
+ /*ENABLE the Power Switch by driving the Enable LOW */
+ gpio_bit_set(HOST_POWERSW_PORT, HOST_POWERSW_VBUS);
+ }
+}
+
+/*!
+ \brief configures the GPIO for the VBUS
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usb_vbus_config (void)
+{
+ rcu_periph_clock_enable(HOST_POWERSW_PORT_RCC);
+
+ gpio_init(HOST_POWERSW_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, HOST_POWERSW_VBUS);
+
+ /* by default, disable is needed on output of the power switch */
+ gpio_bit_set(HOST_POWERSW_PORT, HOST_POWERSW_VBUS);
+
+ /* Delay is need for stabilising the Vbus Low in Reset Condition,
+ * when Vbus=1 and Reset-button is pressed by user
+ */
+ usb_mdelay (2);
+}
+
+/*!
+ \brief initializes delay unit using Timer2
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usb_timer_init (void)
+{
+ rcu_periph_clock_enable(RCU_TIMER2);
+
+ eclic_irq_enable(TIMER2_IRQn, 2, 0);
+}
+
+/*!
+ \brief delay in micro seconds
+ \param[in] usec: value of delay required in micro seconds
+ \param[out] none
+ \retval none
+*/
+void usb_udelay (const uint32_t usec)
+{
+ hwp_delay(usec, TIM_USEC_DELAY);
+}
+
+/*!
+ \brief delay in milli seconds
+ \param[in] msec: value of delay required in milli seconds
+ \param[out] none
+ \retval none
+*/
+void usb_mdelay (const uint32_t msec)
+{
+ hwp_delay(msec, TIM_MSEC_DELAY);
+}
+
+/*!
+ \brief timer base IRQ
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usb_timer_irq (void)
+{
+ if (timer_interrupt_flag_get(TIMER2, TIMER_INT_UP) != RESET){
+ timer_interrupt_flag_clear(TIMER2, TIMER_INT_UP);
+
+ if (delay_time > 0x00U){
+ delay_time--;
+ } else {
+ timer_disable(TIMER2);
+ }
+ }
+}
+
+/*!
+ \brief delay routine based on TIM2
+ \param[in] ntime: delay Time
+ \param[in] unit: delay Time unit = mili sec / micro sec
+ \param[out] none
+ \retval none
+*/
+static void hwp_delay(uint32_t ntime, uint8_t unit)
+{
+ delay_time = ntime;
+ hwp_time_set(unit);
+
+ while(delay_time != 0);
+
+ timer_disable(TIMER2);
+}
+
+/*!
+ \brief configures TIM2 for delay routine based on TIM2
+ \param[in] unit: msec /usec
+ \param[out] none
+ \retval none
+*/
+static void hwp_time_set(uint8_t unit)
+{
+ timer_parameter_struct timer_basestructure;
+
+ timer_disable(TIMER2);
+ timer_interrupt_disable(TIMER2, TIMER_INT_UP);
+
+ if (unit == TIM_USEC_DELAY) {
+ timer_basestructure.period = 11;
+ } else if(unit == TIM_MSEC_DELAY) {
+ timer_basestructure.period = 11999;
+ } else {
+ /* no operation */
+ }
+
+ timer_basestructure.prescaler = timer_prescaler;
+ timer_basestructure.alignedmode = TIMER_COUNTER_EDGE;
+ timer_basestructure.counterdirection = TIMER_COUNTER_UP;
+ timer_basestructure.clockdivision = TIMER_CKDIV_DIV1;
+ timer_basestructure.repetitioncounter = 0;
+
+ timer_init(TIMER2, &timer_basestructure);
+
+ timer_interrupt_flag_clear(TIMER2, TIMER_INT_UP);
+
+ timer_auto_reload_shadow_enable(TIMER2);
+
+ /* timer2 interrupt enable */
+ timer_interrupt_enable(TIMER2, TIMER_INT_UP);
+
+ /* timer2 enable counter */
+ timer_enable(TIMER2);
+}
diff --git a/RISC-V/Source/main.c b/RISC-V/Source/main.c
new file mode 100644
index 0000000..e604348
--- /dev/null
+++ b/RISC-V/Source/main.c
@@ -0,0 +1,87 @@
+/*!
+ \file main.c
+ \brief this file realizes the MSC host
+
+ \version 2019-06-05, V1.0.0, firmware for GD32VF103
+*/
+
+/*
+ Copyright (c) 2019, GigaDevice Semiconductor Inc.
+
+ 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. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "drv_usb_hw.h"
+#include "drv_usb_core.h"
+#include "usbh_usr.h"
+#include "usbh_msc_core.h"
+#include
+#include
+#include
+
+
+usb_core_driver usbh_msc_core;
+
+usbh_host usb_host = {
+ .class_cb = &usbh_msc_cb,
+ .usr_cb = &user_callback_funs,
+
+};
+
+usbh_host hUsbHostFS;
+
+void Downstream_HostUserCallback(usbh_host *phost, uint8_t id){
+ //dummy
+ return;
+}
+
+/**
+ * @brief Main routine for HID mouse / keyboard class application
+ * @param None
+ * @retval None
+ */
+int main(void)
+{
+
+ eclic_global_interrupt_enable();
+
+ eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL2_PRIO2);
+
+ usb_rcu_config();
+
+ usb_timer_init();
+
+ /* configure GPIO pin used for switching VBUS power and charge pump I/O */
+// usb_vbus_config();
+
+ usbh_init (&usbh_msc_core, USB_CORE_ENUM_FS, &usb_host,Downstream_HostUserCallback);
+
+ /* enable interrupts */
+ usb_intr_config();
+
+ while (1) {
+ /* Host state handler */
+ usbh_core_task (&usbh_msc_core, &usb_host);
+ }
+}
diff --git a/RISC-V/Source/system_gd32vf103.c b/RISC-V/Source/system_gd32vf103.c
new file mode 100644
index 0000000..05e5205
--- /dev/null
+++ b/RISC-V/Source/system_gd32vf103.c
@@ -0,0 +1,998 @@
+/*!
+ \file system_gd32vf103.h
+ \brief RISC-V Device Peripheral Access Layer Source File for
+ GD32VF103 Device Series
+
+*/
+
+/*
+ Copyright (c) 2019, GigaDevice Semiconductor Inc.
+
+ 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. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+*/
+
+/* This file refers the RISC-V standard, some adjustments are made according to GigaDevice chips */
+
+#include "gd32vf103.h"
+
+/* system frequency define */
+#define __IRC8M (IRC8M_VALUE) /* internal 8 MHz RC oscillator frequency */
+#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */
+#define __SYS_OSC_CLK (__IRC8M) /* main oscillator frequency */
+
+/* select a system clock by uncommenting the following line */
+/* use IRC8M */
+//#define __SYSTEM_CLOCK_48M_PLL_IRC8M (uint32_t)(48000000)
+//#define __SYSTEM_CLOCK_72M_PLL_IRC8M (uint32_t)(72000000)
+//#define __SYSTEM_CLOCK_108M_PLL_IRC8M (uint32_t)(108000000)
+
+/********************************************************************/
+//#define __SYSTEM_CLOCK_HXTAL (HXTAL_VALUE)
+//#define __SYSTEM_CLOCK_24M_PLL_HXTAL (uint32_t)(24000000)
+/********************************************************************/
+
+//#define __SYSTEM_CLOCK_36M_PLL_HXTAL (uint32_t)(36000000)
+//#define __SYSTEM_CLOCK_48M_PLL_HXTAL (uint32_t)(48000000)
+//#define __SYSTEM_CLOCK_56M_PLL_HXTAL (uint32_t)(56000000)
+//#define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000)
+#define __SYSTEM_CLOCK_96M_PLL_HXTAL (uint32_t)(96000000)
+//#define __SYSTEM_CLOCK_108M_PLL_HXTAL (uint32_t)(108000000)
+
+#define SEL_IRC8M 0x00U
+#define SEL_HXTAL 0x01U
+#define SEL_PLL 0x02U
+
+/* set the system clock frequency and declare the system clock configuration function */
+#ifdef __SYSTEM_CLOCK_48M_PLL_IRC8M
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_IRC8M;
+static void system_clock_48m_irc8m(void);
+#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_IRC8M;
+static void system_clock_72m_irc8m(void);
+#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_IRC8M;
+static void system_clock_108m_irc8m(void);
+
+#elif defined (__SYSTEM_CLOCK_HXTAL)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_HXTAL;
+static void system_clock_hxtal(void);
+#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_24M_PLL_HXTAL;
+static void system_clock_24m_hxtal(void);
+#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_36M_PLL_HXTAL;
+static void system_clock_36m_hxtal(void);
+#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_HXTAL;
+static void system_clock_48m_hxtal(void);
+#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_56M_PLL_HXTAL;
+static void system_clock_56m_hxtal(void);
+#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL;
+static void system_clock_72m_hxtal(void);
+#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_96M_PLL_HXTAL;
+static void system_clock_96m_hxtal(void);
+#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL)
+uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_HXTAL;
+static void system_clock_108m_hxtal(void);
+#else
+uint32_t SystemCoreClock = IRC8M_VALUE;
+#endif /* __SYSTEM_CLOCK_48M_PLL_IRC8M */
+
+/* configure the system clock */
+static void system_clock_config(void);
+
+/*!
+ \brief configure the system clock
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_config(void)
+{
+#ifdef __SYSTEM_CLOCK_HXTAL
+ system_clock_hxtal();
+#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL)
+ system_clock_24m_hxtal();
+#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL)
+ system_clock_36m_hxtal();
+#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL)
+ system_clock_48m_hxtal();
+#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL)
+ system_clock_56m_hxtal();
+#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
+ system_clock_72m_hxtal();
+#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL)
+ system_clock_96m_hxtal();
+#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL)
+ system_clock_108m_hxtal();
+
+#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M)
+ system_clock_48m_irc8m();
+#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M)
+ system_clock_72m_irc8m();
+#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M)
+ system_clock_108m_irc8m();
+#endif /* __SYSTEM_CLOCK_HXTAL */
+}
+
+/*!
+ \brief setup the microcontroller system, initialize the system
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void SystemInit(void)
+{
+ /* reset the RCC clock configuration to the default reset state */
+ /* enable IRC8M */
+ RCU_CTL |= RCU_CTL_IRC8MEN;
+
+ /* reset SCS, AHBPSC, APB1PSC, APB2PSC, ADCPSC, CKOUT0SEL bits */
+ RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC |
+ RCU_CFG0_ADCPSC | RCU_CFG0_ADCPSC_2 | RCU_CFG0_CKOUT0SEL);
+
+ /* reset HXTALEN, CKMEN, PLLEN bits */
+ RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN);
+
+ /* Reset HXTALBPS bit */
+ RCU_CTL &= ~(RCU_CTL_HXTALBPS);
+
+ /* reset PLLSEL, PREDV0_LSB, PLLMF, USBFSPSC bits */
+
+ RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF |
+ RCU_CFG0_USBFSPSC | RCU_CFG0_PLLMF_4);
+ RCU_CFG1 = 0x00000000U;
+
+ /* Reset HXTALEN, CKMEN, PLLEN, PLL1EN and PLL2EN bits */
+ RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);
+ /* disable all interrupts */
+ RCU_INT = 0x00FF0000U;
+
+ /* Configure the System clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */
+ system_clock_config();
+}
+
+/*!
+ \brief update the SystemCoreClock with current core clock retrieved from cpu registers
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void SystemCoreClockUpdate(void)
+{
+ uint32_t scss;
+ uint32_t pllsel, predv0sel, pllmf, ck_src;
+ uint32_t predv0, predv1, pll1mf;
+
+ scss = GET_BITS(RCU_CFG0, 2, 3);
+
+ switch (scss)
+ {
+ /* IRC8M is selected as CK_SYS */
+ case SEL_IRC8M:
+ SystemCoreClock = IRC8M_VALUE;
+ break;
+
+ /* HXTAL is selected as CK_SYS */
+ case SEL_HXTAL:
+ SystemCoreClock = HXTAL_VALUE;
+ break;
+
+ /* PLL is selected as CK_SYS */
+ case SEL_PLL:
+ /* PLL clock source selection, HXTAL or IRC8M/2 */
+ pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL);
+
+
+ if(RCU_PLLSRC_IRC8M_DIV2 == pllsel){
+ /* PLL clock source is IRC8M/2 */
+ ck_src = IRC8M_VALUE / 2U;
+ }else{
+ /* PLL clock source is HXTAL */
+ ck_src = HXTAL_VALUE;
+
+ predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL);
+
+ /* source clock use PLL1 */
+ if(RCU_PREDV0SRC_CKPLL1 == predv0sel){
+ predv1 = ((RCU_CFG1 & RCU_CFG1_PREDV1) >> 4) + 1U;
+ pll1mf = ((RCU_CFG1 & RCU_CFG1_PLL1MF) >> 8) + 2U;
+ if(17U == pll1mf){
+ pll1mf = 20U;
+ }
+ ck_src = (ck_src / predv1) * pll1mf;
+ }
+ predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U;
+ ck_src /= predv0;
+ }
+
+ /* PLL multiplication factor */
+ pllmf = GET_BITS(RCU_CFG0, 18, 21);
+
+ if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){
+ pllmf |= 0x10U;
+ }
+
+ if(pllmf >= 15U){
+ pllmf += 1U;
+ }else{
+ pllmf += 2U;
+ }
+
+ SystemCoreClock = ck_src * pllmf;
+
+ if(15U == pllmf){
+ /* PLL source clock multiply by 6.5 */
+ SystemCoreClock = ck_src * 6U + ck_src / 2U;
+ }
+
+ break;
+
+ /* IRC8M is selected as CK_SYS */
+ default:
+ SystemCoreClock = IRC8M_VALUE;
+ break;
+ }
+}
+
+#ifdef __SYSTEM_CLOCK_HXTAL
+/*!
+ \brief configure the system clock to HXTAL
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_hxtal(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable HXTAL */
+ RCU_CTL |= RCU_CTL_HXTALEN;
+
+ /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
+ }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
+ while(1){
+ }
+ }
+
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* select HXTAL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_HXTAL;
+
+ /* wait until HXTAL is selected as system clock */
+ while(0 == (RCU_CFG0 & RCU_SCSS_HXTAL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL)
+/*!
+ \brief configure the system clock to 24M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_24m_hxtal(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable HXTAL */
+ RCU_CTL |= RCU_CTL_HXTALEN;
+
+ /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
+ }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
+ while(1){
+ }
+ }
+
+ /* HXTAL is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* CK_PLL = (CK_PREDIV0) * 6 = 24 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL6);
+
+ if(HXTAL_VALUE==25000000){
+ /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
+
+ /* enable PLL1 */
+ RCU_CTL |= RCU_CTL_PLL1EN;
+ /* wait till PLL1 is ready */
+ while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
+ }
+
+ }else if(HXTAL_VALUE==8000000){
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 );
+ }
+
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL)
+/*!
+ \brief configure the system clock to 36M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_36m_hxtal(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable HXTAL */
+ RCU_CTL |= RCU_CTL_HXTALEN;
+
+ /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
+ }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
+ while(1){
+ }
+ }
+
+ /* HXTAL is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* CK_PLL = (CK_PREDIV0) * 9 = 36 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL9);
+
+ if(HXTAL_VALUE==25000000){
+ /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
+
+ /* enable PLL1 */
+ RCU_CTL |= RCU_CTL_PLL1EN;
+ /* wait till PLL1 is ready */
+ while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
+ }
+
+ }else if(HXTAL_VALUE==8000000){
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 );
+ }
+
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL)
+/*!
+ \brief configure the system clock to 48M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_48m_hxtal(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable HXTAL */
+ RCU_CTL |= RCU_CTL_HXTALEN;
+
+ /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
+ }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
+ while(1){
+ }
+ }
+
+ /* HXTAL is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* CK_PLL = (CK_PREDIV0) * 12 = 48 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL12);
+
+ if(HXTAL_VALUE==25000000){
+
+ /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
+
+ /* enable PLL1 */
+ RCU_CTL |= RCU_CTL_PLL1EN;
+ /* wait till PLL1 is ready */
+ while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
+ }
+
+ }else if(HXTAL_VALUE==8000000){
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 );
+ }
+
+
+
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL)
+/*!
+ \brief configure the system clock to 56M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_56m_hxtal(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable HXTAL */
+ RCU_CTL |= RCU_CTL_HXTALEN;
+
+ /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
+ }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
+ while(1){
+ }
+ }
+
+ /* HXTAL is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* CK_PLL = (CK_PREDIV0) * 14 = 56 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL14);
+
+ if(HXTAL_VALUE==25000000){
+
+ /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
+
+ /* enable PLL1 */
+ RCU_CTL |= RCU_CTL_PLL1EN;
+ /* wait till PLL1 is ready */
+ while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
+ }
+
+ }else if(HXTAL_VALUE==8000000){
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 );
+ }
+
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
+/*!
+ \brief configure the system clock to 72M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_72m_hxtal(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable HXTAL */
+ RCU_CTL |= RCU_CTL_HXTALEN;
+
+ /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
+ }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
+ while(1){
+ }
+ }
+
+ /* HXTAL is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* CK_PLL = (CK_PREDIV0) * 18 = 72 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL18);
+
+
+ if(HXTAL_VALUE==25000000){
+
+ /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
+
+ /* enable PLL1 */
+ RCU_CTL |= RCU_CTL_PLL1EN;
+ /* wait till PLL1 is ready */
+ while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
+ }
+
+ }else if(HXTAL_VALUE==8000000){
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 );
+ }
+
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL)
+/*!
+ \brief configure the system clock to 96M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_96m_hxtal(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable HXTAL */
+ RCU_CTL |= RCU_CTL_HXTALEN;
+
+ /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
+ }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
+ while(1){
+ }
+ }
+
+ /* HXTAL is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ if(HXTAL_VALUE==25000000){
+
+ /* CK_PLL = (CK_PREDIV0) * 24 = 96 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL24);
+
+ /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
+ /* enable PLL1 */
+ RCU_CTL |= RCU_CTL_PLL1EN;
+ /* wait till PLL1 is ready */
+ while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
+ }
+
+ }else if(HXTAL_VALUE==8000000){
+ /* CK_PLL = (CK_PREDIV0) * 24 = 96 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL24);
+
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 );
+ }
+
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL)
+/*!
+ \brief configure the system clock to 108M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+
+static void system_clock_108m_hxtal(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable HXTAL */
+ RCU_CTL |= RCU_CTL_HXTALEN;
+
+ /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
+ }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
+ while(1){
+ }
+ }
+
+ /* HXTAL is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* CK_PLL = (CK_PREDIV0) * 27 = 108 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL27);
+
+ if(HXTAL_VALUE==25000000){
+ /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PREDV1_DIV5 | RCU_PLL1_MUL8 | RCU_PREDV0_DIV10);
+
+ /* enable PLL1 */
+ RCU_CTL |= RCU_CTL_PLL1EN;
+ /* wait till PLL1 is ready */
+ while(0U == (RCU_CTL & RCU_CTL_PLL1STB)){
+ }
+
+ /* enable PLL1 */
+ RCU_CTL |= RCU_CTL_PLL2EN;
+ /* wait till PLL1 is ready */
+ while(0U == (RCU_CTL & RCU_CTL_PLL2STB)){
+ }
+ }else if(HXTAL_VALUE==8000000){
+ RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
+ RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 | RCU_PREDV1_DIV2 | RCU_PLL1_MUL20 | RCU_PLL2_MUL20);
+
+ /* enable PLL1 */
+ RCU_CTL |= RCU_CTL_PLL1EN;
+ /* wait till PLL1 is ready */
+ while(0U == (RCU_CTL & RCU_CTL_PLL1STB)){
+ }
+
+ /* enable PLL2 */
+ RCU_CTL |= RCU_CTL_PLL2EN;
+ /* wait till PLL1 is ready */
+ while(0U == (RCU_CTL & RCU_CTL_PLL2STB)){
+ }
+
+ }
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M)
+/*!
+ \brief configure the system clock to 48M by PLL which selects IRC8M as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_48m_irc8m(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable IRC8M */
+ RCU_CTL |= RCU_CTL_IRC8MEN;
+
+ /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB);
+ }
+ while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){
+ while(1){
+ }
+ }
+
+ /* IRC8M is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* CK_PLL = (CK_IRC8M/2) * 12 = 48 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= RCU_PLL_MUL12;
+
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M)
+/*!
+ \brief configure the system clock to 72M by PLL which selects IRC8M as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_72m_irc8m(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable IRC8M */
+ RCU_CTL |= RCU_CTL_IRC8MEN;
+
+ /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB);
+ }
+ while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){
+ while(1){
+ }
+ }
+
+ /* IRC8M is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* CK_PLL = (CK_IRC8M/2) * 18 = 72 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= RCU_PLL_MUL18;
+
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M)
+/*!
+ \brief configure the system clock to 108M by PLL which selects IRC8M as its clock source
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+static void system_clock_108m_irc8m(void)
+{
+ uint32_t timeout = 0U;
+ uint32_t stab_flag = 0U;
+
+ /* enable IRC8M */
+ RCU_CTL |= RCU_CTL_IRC8MEN;
+
+ /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */
+ do{
+ timeout++;
+ stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB);
+ }
+ while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout));
+
+ /* if fail */
+ if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){
+ while(1){
+ }
+ }
+
+ /* IRC8M is stable */
+ /* AHB = SYSCLK */
+ RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
+ /* APB2 = AHB/1 */
+ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
+ /* APB1 = AHB/2 */
+ RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
+
+ /* CK_PLL = (CK_IRC8M/2) * 27 = 108 MHz */
+ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
+ RCU_CFG0 |= RCU_PLL_MUL27;
+
+ /* enable PLL */
+ RCU_CTL |= RCU_CTL_PLLEN;
+
+ /* wait until PLL is stable */
+ while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
+ }
+
+ /* select PLL as system clock */
+ RCU_CFG0 &= ~RCU_CFG0_SCS;
+ RCU_CFG0 |= RCU_CKSYSSRC_PLL;
+
+ /* wait until PLL is selected as system clock */
+ while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
+ }
+}
+
+#endif
diff --git a/RISC-V/Source/usbh_msc_bbb.c b/RISC-V/Source/usbh_msc_bbb.c
new file mode 100644
index 0000000..eadf765
--- /dev/null
+++ b/RISC-V/Source/usbh_msc_bbb.c
@@ -0,0 +1,454 @@
+/*!
+ \file usbh_msc_bbb.c
+ \brief USB MSC BBB protocol related functions
+
+ \version 2019-06-05, V1.0.0, firmware for GD32VF103
+*/
+
+/*
+ Copyright (c) 2019, GigaDevice Semiconductor Inc.
+
+ 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. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "usbh_transc.h"
+#include "drv_usbh_int.h"
+#include "usbh_msc_core.h"
+#include "usbh_msc_scsi.h"
+#include "usbh_msc_bbb.h"
+
+usbh_cbw_pkt msc_cbw_data;
+usbh_csw_pkt msc_csw_data;
+
+static uint32_t bot_stall_error_count; /* keeps count of stall error cases */
+usbh_botxfer msc_botxfer_param;
+
+/*!
+ \brief initialize the mass storage parameters
+ \param[in] pudev: pointer to usb core instance
+ \param[out] none
+ \retval none
+*/
+void usbh_msc_init (usb_core_driver *pudev)
+{
+ if (pudev->host.connect_status) {
+ msc_cbw_data.field.dCBWSignature = BBB_CBW_SIGNATURE;
+ msc_cbw_data.field.dCBWTag = USBH_MSC_BOT_CBW_TAG;
+ msc_cbw_data.field.bCBWLUN = 0; /* only one lun is supported */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ }
+
+ bot_stall_error_count = 0;
+
+ msc_error_count = 0;
+}
+
+/*!
+ \brief manage the different states of BOT transfer and updates the status to upper layer
+ \param[in] pudev: pointer to usb core instance
+ \param[in] puhost: pointer to usb host
+ \param[out] none
+ \retval none
+*/
+void usbh_msc_botxfer (usb_core_driver *pudev, usbh_host *puhost)
+{
+ uint8_t xfer_dir, index;
+ static uint32_t remain_len;
+ static uint8_t *data_pointer, *data_pointer_prev;
+ static uint8_t error_dir;
+
+ usbh_status status;
+
+ usb_urb_state URB_Status = URB_IDLE;
+
+ if (pudev->host.connect_status) {
+ switch (msc_botxfer_param.bot_state) {
+ case USBH_MSC_SEND_CBW:
+ /* send CBW */
+ usbh_data_send (pudev,
+ &msc_cbw_data.CBWArray[0],
+ msc_machine.hc_num_out,
+ BBB_CBW_LENGTH);
+
+ msc_botxfer_param.bot_state_bkp = USBH_MSC_SEND_CBW;
+ msc_botxfer_param.bot_state = USBH_MSC_SENT_CBW;
+ break;
+
+ case USBH_MSC_SENT_CBW:
+ URB_Status = usbh_urbstate_get(pudev, msc_machine.hc_num_out);
+
+ if (URB_Status == URB_DONE) {
+ bot_stall_error_count = 0;
+ msc_botxfer_param.bot_state_bkp = USBH_MSC_SENT_CBW;
+
+ /* if the CBW packet is sent successful, then change the state */
+ xfer_dir = (msc_cbw_data.field.bmCBWFlags & USB_TRX_MASK);
+
+ if (msc_cbw_data.field.dCBWDataTransferLength != 0) {
+ remain_len = msc_cbw_data.field.dCBWDataTransferLength;
+ data_pointer = msc_botxfer_param.xfer_buf;
+ data_pointer_prev = data_pointer;
+
+ /* if there is data transfer stage */
+ if (xfer_dir == USB_TRX_IN) {
+ /* data direction is IN */
+ msc_botxfer_param.bot_state = USBH_MSC_BOT_DATAIN_STATE;
+ } else {
+ /* data direction is OUT */
+ msc_botxfer_param.bot_state = USBH_MSC_BOT_DATAOUT_STATE;
+ }
+ } else {
+ /* If there is NO Data Transfer Stage */
+ msc_botxfer_param.bot_state = USBH_MSC_RECEIVE_CSW_STATE;
+ }
+ } else if (URB_Status == URB_NOTREADY) {
+ msc_botxfer_param.bot_state = msc_botxfer_param.bot_state_bkp;
+ } else if (URB_Status == URB_STALL) {
+ error_dir = USBH_MSC_DIR_OUT;
+ msc_botxfer_param.bot_state = USBH_MSC_BOT_ERROR_OUT;
+ }
+ break;
+
+ case USBH_MSC_BOT_DATAIN_STATE:
+ URB_Status = usbh_urbstate_get(pudev, msc_machine.hc_num_in);
+
+ /* BOT DATA IN stage */
+ if ((URB_Status == URB_DONE) ||
+ (msc_botxfer_param.bot_state_bkp != USBH_MSC_BOT_DATAIN_STATE)) {
+ bot_stall_error_count = 0;
+ msc_botxfer_param.bot_state_bkp = USBH_MSC_BOT_DATAIN_STATE;
+
+ if (remain_len > msc_machine.msc_bulk_epinsize) {
+ usbh_data_recev (pudev,
+ data_pointer,
+ msc_machine.hc_num_in,
+ msc_machine.msc_bulk_epinsize);
+
+ remain_len -= msc_machine.msc_bulk_epinsize;
+ data_pointer = data_pointer + msc_machine.msc_bulk_epinsize;
+ } else if (remain_len == 0) {
+ /* if value was 0, and successful transfer, then change the state */
+ msc_botxfer_param.bot_state = USBH_MSC_RECEIVE_CSW_STATE;
+ } else {
+ usbh_data_recev (pudev,
+ data_pointer,
+ msc_machine.hc_num_in,
+ remain_len);
+
+ remain_len = 0; /* reset this value and keep in same state */
+ }
+ } else if(URB_Status == URB_STALL) {
+ /* this is data stage stall condition */
+
+ error_dir = USBH_MSC_DIR_IN;
+ msc_botxfer_param.bot_state = USBH_MSC_BOT_ERROR_IN;
+
+ /* Refer to USB Mass-Storage Class : BOT (www.usb.org)
+ 6.7.2 Host expects to receive data from the device
+ 3. On a STALL condition receiving data, then:
+ The host shall accept the data received.
+ The host shall clear the Bulk-In pipe.
+ 4. The host shall attempt to receive a CSW.
+
+ msc_botxfer_param.bot_state_bkp is used to switch to the Original
+ state after the ClearFeature Command is issued.
+ */
+ msc_botxfer_param.bot_state_bkp = USBH_MSC_RECEIVE_CSW_STATE;
+ }
+ break;
+
+ case USBH_MSC_BOT_DATAOUT_STATE:
+ /* BOT DATA OUT stage */
+ URB_Status = usbh_urbstate_get(pudev, msc_machine.hc_num_out);
+ if (URB_Status == URB_DONE) {
+ bot_stall_error_count = 0;
+ msc_botxfer_param.bot_state_bkp = USBH_MSC_BOT_DATAOUT_STATE;
+
+ if (remain_len > msc_machine.msc_bulk_epoutsize) {
+ usbh_data_send (pudev,
+ data_pointer,
+ msc_machine.hc_num_out,
+ msc_machine.msc_bulk_epoutsize);
+
+ data_pointer_prev = data_pointer;
+ data_pointer = data_pointer + msc_machine.msc_bulk_epoutsize;
+
+ remain_len = remain_len - msc_machine.msc_bulk_epoutsize;
+ } else if (remain_len == 0) {
+ /* if value was 0, and successful transfer, then change the state */
+ msc_botxfer_param.bot_state = USBH_MSC_RECEIVE_CSW_STATE;
+ } else {
+ usbh_data_send (pudev,
+ data_pointer,
+ msc_machine.hc_num_out,
+ remain_len);
+
+ remain_len = 0; /* reset this value and keep in same state */
+ }
+ } else if (URB_Status == URB_NOTREADY) {
+ if (data_pointer != data_pointer_prev) {
+ usbh_data_send (pudev,
+ (data_pointer - msc_machine.msc_bulk_epoutsize),
+ msc_machine.hc_num_out,
+ msc_machine.msc_bulk_epoutsize);
+ } else {
+ usbh_data_send (pudev,
+ data_pointer,
+ msc_machine.hc_num_out,
+ msc_machine.msc_bulk_epoutsize);
+ }
+ } else if (URB_Status == URB_STALL) {
+ error_dir = USBH_MSC_DIR_OUT;
+ msc_botxfer_param.bot_state = USBH_MSC_BOT_ERROR_OUT;
+
+ /* Refer to USB Mass-Storage Class : BOT (www.usb.org)
+ 6.7.3 Ho - Host expects to send data to the device
+ 3. On a STALL condition sending data, then:
+ " The host shall clear the Bulk-Out pipe.
+ 4. The host shall attempt to receive a CSW.
+
+ The Above statement will do the clear the Bulk-Out pipe.
+ The Below statement will help in Getting the CSW.
+
+ msc_botxfer_param.bot_state_bkp is used to switch to the Original
+ state after the ClearFeature Command is issued.
+ */
+
+ msc_botxfer_param.bot_state_bkp = USBH_MSC_RECEIVE_CSW_STATE;
+ }
+ break;
+
+ case USBH_MSC_RECEIVE_CSW_STATE:
+ /* BOT CSW stage */
+ /* NOTE: We cannot reset the BOTStallErrorCount here as it may come from the clearFeature from previous command */
+ msc_botxfer_param.bot_state_bkp = USBH_MSC_RECEIVE_CSW_STATE;
+ msc_botxfer_param.xfer_buf = msc_csw_data.CSWArray;
+ msc_botxfer_param.data_len = USBH_MSC_CSW_MAX_LENGTH;
+
+ for (index = BBB_CSW_LENGTH; index != 0; index--) {
+ msc_csw_data.CSWArray[index] = 0;
+ }
+
+ msc_csw_data.CSWArray[0] = 0;
+
+ usbh_data_recev (pudev,
+ msc_botxfer_param.xfer_buf,
+ msc_machine.hc_num_in,
+ USBH_MSC_CSW_MAX_LENGTH);
+
+ msc_botxfer_param.bot_state = USBH_MSC_DECODE_CSW;
+ break;
+
+ case USBH_MSC_DECODE_CSW:
+ URB_Status = usbh_urbstate_get(pudev, msc_machine.hc_num_in);
+
+ /* decode CSW */
+ if (URB_Status == URB_DONE) {
+ bot_stall_error_count = 0;
+ msc_botxfer_param.bot_state_bkp = USBH_MSC_RECEIVE_CSW_STATE;
+ msc_botxfer_param.msc_state = msc_botxfer_param.msc_state_current ;
+ msc_botxfer_param.bot_xfer_status = usbh_msc_csw_decode (pudev , puhost);
+ } else if (URB_Status == URB_STALL) {
+ error_dir = USBH_MSC_DIR_IN;
+ msc_botxfer_param.bot_state = USBH_MSC_BOT_ERROR_IN;
+ }
+ break;
+
+ case USBH_MSC_BOT_ERROR_IN:
+ status = usbh_msc_bot_abort(pudev, puhost, USBH_MSC_DIR_IN);
+
+ if (status == USBH_OK) {
+ /* check if the error was due in both the directions */
+ if (error_dir == USBH_MSC_BOTH_DIR) {
+ /* if both directions are needed, switch to OUT direction */
+ msc_botxfer_param.bot_state = USBH_MSC_BOT_ERROR_OUT;
+ } else {
+ /* switch back to the original state, in many cases this will be USBH_MSC_RECEIVE_CSW_STATE state */
+ msc_botxfer_param.bot_state = msc_botxfer_param.bot_state_bkp;
+ }
+ } else if (status == USBH_UNRECOVERED_ERROR) {
+ /* this means that there is a stall error limit, do reset recovery */
+ msc_botxfer_param.bot_xfer_status = USBH_MSC_PHASE_ERROR;
+ }
+ break;
+
+ case USBH_MSC_BOT_ERROR_OUT:
+ status = usbh_msc_bot_abort (pudev, puhost, USBH_MSC_DIR_OUT);
+
+ if (status == USBH_OK) {
+ /* switch back to the original state */
+ msc_botxfer_param.bot_state = msc_botxfer_param.bot_state_bkp;
+ } else if (status == USBH_UNRECOVERED_ERROR) {
+ /* this means that there is a stall error limit, do reset recovery */
+ msc_botxfer_param.bot_xfer_status = USBH_MSC_PHASE_ERROR;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/*!
+ \brief manages the different error handling for stall
+ \param[in] pudev: pointer to usb core instance
+ \param[in] puhost: pointer to usb host
+ \param[in] direction: data IN or OUT
+ \param[out] none
+ \retval none
+*/
+usbh_status usbh_msc_bot_abort (usb_core_driver *pudev, usbh_host *puhost, uint8_t direction)
+{
+ usbh_status status = USBH_BUSY;
+
+ switch (direction) {
+ case USBH_MSC_DIR_IN :
+ /* send clrfeture command on bulk IN endpoint */
+ status = usbh_clrfeature(pudev,
+ puhost,
+ msc_machine.msc_bulk_epin,
+ msc_machine.hc_num_in);
+ break;
+
+ case USBH_MSC_DIR_OUT :
+ /*send clrfeature command on bulk OUT endpoint */
+ status = usbh_clrfeature(pudev,
+ puhost,
+ msc_machine.msc_bulk_epout,
+ msc_machine.hc_num_out);
+ break;
+
+ default:
+ break;
+ }
+
+ bot_stall_error_count++; /* check continous number of times, stall has occured */
+
+ if (bot_stall_error_count > MAX_BULK_STALL_COUNT_LIMIT) {
+ status = USBH_UNRECOVERED_ERROR;
+ }
+
+ return status;
+}
+
+/*!
+ \brief decode the CSW received by the device and updates the same to upper layer
+ \param[in] pudev: pointer to usb core instance
+ \param[in] puhost: pointer to usb host
+ \param[out] none
+ \retval on success USBH_MSC_OK, on failure USBH_MSC_FAIL
+ \notes
+ Refer to USB Mass-Storage Class: BOT (www.usb.org)
+ 6.3.1 Valid CSW Conditions :
+ The host shall consider the CSW valid when:
+ 1. dCSWSignature is equal to 53425355h
+ 2. the CSW is 13 (Dh) bytes in length,
+ 3. dCSWTag matches the dCBWTag from the corresponding CBW.
+*/
+uint8_t usbh_msc_csw_decode (usb_core_driver *pudev, usbh_host *puhost)
+{
+ uint8_t status;
+ uint32_t data_xfercount = 0;
+
+ status = USBH_MSC_FAIL;
+
+ if (pudev->host.connect_status) {
+ /* checking if the transfer length is diffrent than 13 */
+ data_xfercount = usbh_xfercount_get (pudev, msc_machine.hc_num_in);
+
+ if (data_xfercount != BBB_CSW_LENGTH) {
+ /* (4) Hi > Dn (Host expects to receive data from the device,
+ Device intends to transfer no data)
+ (5) Hi > Di (Host expects to receive data from the device,
+ Device intends to send data to the host)
+ (9) Ho > Dn (Host expects to send data to the device,
+ Device intends to transfer no data)
+ (11) Ho > Do (Host expects to send data to the device,
+ Device intends to receive data from the host)
+ */
+
+ status = USBH_MSC_PHASE_ERROR;
+ } else {
+ /* CSW length is correct */
+
+ /* check validity of the CSW Signature and CSWStatus */
+ if (msc_csw_data.field.dCSWSignature == BBB_CSW_SIGNATURE) {
+ /* check condition 1. dCSWSignature is equal to 53425355h */
+ if (msc_csw_data.field.dCSWTag == msc_cbw_data.field.dCBWTag) {
+ /* check condition 3. dCSWTag matches the dCBWTag from the corresponding CBW */
+ if (msc_csw_data.field.bCSWStatus == USBH_MSC_OK) {
+ /* refer to USB Mass-Storage Class : BOT (www.usb.org)
+ Hn Host expects no data transfers
+ Hi Host expects to receive data from the device
+ Ho Host expects to send data to the device
+
+ Dn Device intends to transfer no data
+ Di Device intends to send data to the host
+ Do Device intends to receive data from the host
+
+ Section 6.7
+ (1) Hn = Dn (Host expects no data transfers,
+ Device intends to transfer no data)
+ (6) Hi = Di (Host expects to receive data from the device,
+ Device intends to send data to the host)
+ (12) Ho = Do (Host expects to send data to the device,
+ Device intends to receive data from the host)
+ */
+
+ status = USBH_MSC_OK;
+ } else if (msc_csw_data.field.bCSWStatus == USBH_MSC_FAIL) {
+ status = USBH_MSC_FAIL;
+ } else if(msc_csw_data.field.bCSWStatus == USBH_MSC_PHASE_ERROR) {
+ /* refer to USB Mass-Storage Class : BOT (www.usb.org)
+ Section 6.7
+ (2) Hn < Di ( Host expects no data transfers,
+ Device intends to send data to the host)
+ (3) Hn < Do ( Host expects no data transfers,
+ Device intends to receive data from the host)
+ (7) Hi < Di ( Host expects to receive data from the device,
+ Device intends to send data to the host)
+ (8) Hi <> Do ( Host expects to receive data from the device,
+ Device intends to receive data from the host)
+ (10) Ho <> Di (Host expects to send data to the device,
+ Di Device intends to send data to the host)
+ (13) Ho < Do (Host expects to send data to the device,
+ Device intends to receive data from the host)
+ */
+
+ status = USBH_MSC_PHASE_ERROR;
+ }
+ } /* CSW tag matching is checked */
+ } /* CSW signature correct checking */else {
+ /* If the CSW signature is not valid, we sall return the phase error to
+ upper layers for reset recovery */
+ status = USBH_MSC_PHASE_ERROR;
+ }
+ } /* CSW length check */
+ }
+
+ msc_botxfer_param.bot_xfer_status = status;
+
+ return status;
+}
diff --git a/RISC-V/Source/usbh_msc_core.c b/RISC-V/Source/usbh_msc_core.c
new file mode 100644
index 0000000..3f6b0c7
--- /dev/null
+++ b/RISC-V/Source/usbh_msc_core.c
@@ -0,0 +1,367 @@
+/*!
+ \file usbh_msc_core.c
+ \brief USB MSC(mass storage device) class driver
+
+ \version 2019-06-05, V1.0.0, firmware for GD32VF103
+*/
+
+/*
+ Copyright (c) 2019, GigaDevice Semiconductor Inc.
+
+ 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. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "usbh_pipe.h"
+#include "usbh_transc.h"
+#include "usbh_msc_core.h"
+#include "usbh_msc_scsi.h"
+#include "usbh_msc_bbb.h"
+
+#define USBH_MSC_ERROR_RETRY_LIMIT 10
+
+uint8_t msc_error_count = 0;
+
+usbh_msc_machine msc_machine;
+
+static void usbh_msc_itf_deinit (usb_core_driver *pudev, void *puhost);
+static usbh_status usbh_msc_itf_init (usb_core_driver *pudev, void *puhost);
+static usbh_status usbh_msc_handle (usb_core_driver *pudev, void *puhost);
+static usbh_status usbh_msc_req (usb_core_driver *pudev, void *puhost);
+static usbh_status usbh_msc_maxlun_get (usb_core_driver *pudev, usbh_host *puhost);
+
+usbh_class_cb usbh_msc_cb =
+{
+ usbh_msc_itf_init,
+ usbh_msc_itf_deinit,
+ usbh_msc_req,
+ usbh_msc_handle,
+};
+
+void usbh_msc_error_handle (uint8_t status);
+
+/*!
+ \brief interface initialization for MSC class
+ \param[in] pudev: pointer to usb core instance
+ \param[in] puhost: pointer to usb host
+ \param[out] none
+ \retval operation status
+*/
+static usbh_status usbh_msc_itf_init (usb_core_driver *pudev, void *puhost)
+{
+ usbh_host *pphost = puhost;
+
+ usb_desc_itf *itf_desc = &pphost->dev_prop.itf_desc[0];
+
+ if ((itf_desc->bInterfaceClass == MSC_CLASS) &&
+ (itf_desc->bInterfaceProtocol == MSC_PROTOCOL)) {
+
+ usb_desc_ep *ep_desc = &pphost->dev_prop.ep_desc[0][0];
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ msc_machine.msc_bulk_epin = ep_desc->bEndpointAddress;
+ msc_machine.msc_bulk_epinsize = ep_desc->wMaxPacketSize;
+ } else {
+ msc_machine.msc_bulk_epout = ep_desc->bEndpointAddress;
+ msc_machine.msc_bulk_epoutsize = ep_desc->wMaxPacketSize;
+ }
+
+ ep_desc = &pphost->dev_prop.ep_desc[0][1];
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ msc_machine.msc_bulk_epin = ep_desc->bEndpointAddress;
+ msc_machine.msc_bulk_epinsize = ep_desc->wMaxPacketSize;
+ } else {
+ msc_machine.msc_bulk_epout = ep_desc->bEndpointAddress;
+ msc_machine.msc_bulk_epoutsize = ep_desc->wMaxPacketSize;
+ }
+
+ msc_machine.hc_num_out = usbh_pipe_allocate(pudev, msc_machine.msc_bulk_epout);
+
+ msc_machine.hc_num_in = usbh_pipe_allocate(pudev, msc_machine.msc_bulk_epin);
+
+
+ /* open the new channels */
+ usbh_pipe_create (pudev,
+ &pphost->dev_prop,
+ msc_machine.hc_num_out,
+ USB_EPTYPE_BULK,
+ msc_machine.msc_bulk_epoutsize);
+
+ usbh_pipe_create (pudev,
+ &pphost->dev_prop,
+ msc_machine.hc_num_in,
+ USB_EPTYPE_BULK,
+ msc_machine.msc_bulk_epinsize);
+
+ } else {
+ pphost->usr_cb->dev_not_supported();
+ }
+
+ return USBH_OK;
+}
+
+/*!
+ \brief de-initialize interface by freeing host channels allocated to interface
+ \param[in] pudev: pointer to usb core instance
+ \param[in] puhost: pointer to usb host
+ \param[out] none
+ \retval operation status
+*/
+void usbh_msc_itf_deinit (usb_core_driver *pudev, void *puhost)
+{
+ if (msc_machine.hc_num_out) {
+ usb_pipe_halt (pudev, msc_machine.hc_num_out);
+ usbh_pipe_free (pudev, msc_machine.hc_num_out);
+
+ msc_machine.hc_num_out = 0;
+ }
+
+ if (msc_machine.hc_num_in) {
+ usb_pipe_halt (pudev, msc_machine.hc_num_in);
+ usbh_pipe_free (pudev, msc_machine.hc_num_in);
+
+ msc_machine.hc_num_in = 0;
+ }
+}
+
+/*!
+ \brief initialize the MSC state machine
+ \param[in] pudev: pointer to usb core instance
+ \param[in] puhost: pointer to usb host
+ \param[out] none
+ \retval operation status
+*/
+static usbh_status usbh_msc_req (usb_core_driver *pudev, void *puhost)
+{
+ usbh_status status = USBH_OK;
+ msc_botxfer_param.msc_state = USBH_MSC_BOT_INIT_STATE;
+
+ return status;
+}
+
+/*!
+ \brief MSC state machine handler
+ \param[in] pudev: pointer to usb core instance
+ \param[in] puhost: pointer to usb host
+ \param[out] none
+ \retval operation status
+*/
+static usbh_status usbh_msc_handle (usb_core_driver *pudev, void *puhost)
+{
+ usbh_host *pphost = puhost;
+ usbh_status status = USBH_BUSY;
+ uint8_t msc_status = USBH_MSC_BUSY;
+ uint8_t app_status = 0;
+
+ static uint8_t max_lun_exceed = FALSE;
+
+ if (pudev->host.connect_status) {
+ switch (msc_botxfer_param.msc_state) {
+ case USBH_MSC_BOT_INIT_STATE:
+ usbh_msc_init(pudev);
+ msc_botxfer_param.msc_state = USBH_MSC_BOT_RESET;
+ break;
+
+ case USBH_MSC_BOT_RESET:
+ status = USBH_OK;
+
+ msc_botxfer_param.msc_state = USBH_MSC_GET_MAX_LUN;
+ break;
+
+ case USBH_MSC_GET_MAX_LUN:
+ /* issue Get_MaxLun request */
+ status = usbh_msc_maxlun_get (pudev, puhost);
+
+ if (status == USBH_OK) {
+ msc_machine.max_lun = *(msc_machine.buf);
+
+ /* if device has more that one logical unit then it is not supported */
+ if ((msc_machine.max_lun > 0) && (max_lun_exceed == FALSE)) {
+ max_lun_exceed = TRUE;
+ //pphost->usr_cb->dev_not_supported();
+
+ break;
+ }
+
+ msc_botxfer_param.msc_state = USBH_MSC_TEST_UNIT_READY;
+ }
+
+ if (status == USBH_NOT_SUPPORTED) {
+ /* if the command has failed, then we need to move to next state, after
+ STALL condition is cleared by Control-Transfer */
+ msc_botxfer_param.msc_state_bkp = USBH_MSC_TEST_UNIT_READY;
+
+ /* a clear feature should be issued here */
+ msc_botxfer_param.msc_state = USBH_MSC_CTRL_ERROR_STATE;
+ }
+ break;
+
+ case USBH_MSC_CTRL_ERROR_STATE:
+ /* issue clearfeature request */
+ status = usbh_clrfeature(pudev,
+ puhost,
+ 0x00,
+ pphost->control.pipe_out_num);
+
+ if (status == USBH_OK) {
+ /* if GetMaxLun request not support, assume single LUN configuration */
+ msc_machine.max_lun = 0;
+
+ msc_botxfer_param.msc_state = msc_botxfer_param.msc_state_bkp;
+ }
+ break;
+
+ case USBH_MSC_TEST_UNIT_READY:
+ /* issue SCSI command TestUnitReady */
+ msc_status = usbh_msc_test_unitready(pudev);
+
+ if (msc_status == USBH_MSC_OK) {
+ msc_botxfer_param.msc_state = USBH_MSC_READ_CAPACITY10;
+ msc_error_count = 0;
+ status = USBH_OK;
+ } else {
+ usbh_msc_error_handle (msc_status);
+ }
+ break;
+
+ case USBH_MSC_READ_CAPACITY10:
+ /* issue READ_CAPACITY10 SCSI command */
+ msc_status = usbh_msc_read_capacity10(pudev);
+
+ if (msc_status == USBH_MSC_OK) {
+ msc_botxfer_param.msc_state = USBH_MSC_MODE_SENSE6;
+ msc_error_count = 0;
+ status = USBH_OK;
+ } else {
+ usbh_msc_error_handle (msc_status);
+ }
+ break;
+
+ case USBH_MSC_MODE_SENSE6:
+ /* issue ModeSense6 SCSI command for detecting if device is write-protected */
+ msc_status = usbh_msc_mode_sense6 (pudev);
+
+ if (msc_status == USBH_MSC_OK) {
+ msc_botxfer_param.msc_state = USBH_MSC_DEFAULT_APPLI_STATE;
+ msc_error_count = 0;
+ status = USBH_OK;
+ } else {
+ usbh_msc_error_handle (msc_status);
+ }
+ break;
+
+ case USBH_MSC_REQUEST_SENSE:
+ /* issue RequestSense SCSI command for retreiving error code */
+ msc_status = usbh_msc_request_sense (pudev);
+ if (msc_status == USBH_MSC_OK) {
+ msc_botxfer_param.msc_state = msc_botxfer_param.msc_state_bkp;
+ status = USBH_OK;
+ } else {
+ usbh_msc_error_handle (msc_status);
+ }
+ break;
+
+ case USBH_MSC_BOT_USB_TRANSFERS:
+ /* process the BOT state machine */
+ usbh_msc_botxfer(pudev , puhost);
+ break;
+
+ case USBH_MSC_DEFAULT_APPLI_STATE:
+ /* process application callback for MSC */
+ app_status = pphost->usr_cb->dev_user_app();
+
+ if (app_status == 0) {
+ msc_botxfer_param.msc_state = USBH_MSC_DEFAULT_APPLI_STATE;
+ } else if (app_status == 1) {
+ /* de-init requested from application layer */
+ status = USBH_APPLY_DEINIT;
+ }
+ break;
+
+ case USBH_MSC_UNRECOVERED_STATE:
+ status = USBH_UNRECOVERED_ERROR;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return status;
+}
+
+/*!
+ \brief get max lun of the mass storage device
+ \param[in] pudev: pointer to usb core instance
+ \param[in] puhost: pointer to usb host
+ \param[out] none
+ \retval operation status
+*/
+static usbh_status usbh_msc_maxlun_get (usb_core_driver *pudev, usbh_host *puhost)
+{
+ usbh_status status = USBH_BUSY;
+
+ if (puhost->control.ctl_state == CTL_IDLE) {
+ puhost->control.setup.req = (usb_req) {
+ .bmRequestType = USB_TRX_IN | USB_REQTYPE_CLASS | USB_RECPTYPE_ITF,
+ .bRequest = BBB_GET_MAX_LUN,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = 1
+ };
+
+ usbh_ctlstate_config (puhost, msc_machine.buf, 1);
+ }
+
+ status = usbh_ctl_handler (pudev, puhost);
+
+ return status;
+}
+
+/*!
+ \brief handling errors occuring during the MSC state machine
+ \param[in] status: error status
+ \param[out] none
+ \retval operation status
+*/
+void usbh_msc_error_handle (uint8_t status)
+{
+ if (status == USBH_MSC_FAIL) {
+ msc_error_count++;
+
+ if (msc_error_count < USBH_MSC_ERROR_RETRY_LIMIT) {
+ /* try msc level error recovery, issue the request sense to get drive error reason */
+ msc_botxfer_param.msc_state = USBH_MSC_REQUEST_SENSE;
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ } else {
+ /* error trials exceeded the limit, go to unrecovered state */
+ msc_botxfer_param.msc_state = USBH_MSC_UNRECOVERED_STATE;
+ }
+ } else if (status == USBH_MSC_PHASE_ERROR) {
+ /* phase error, go to unrecoovered state */
+ msc_botxfer_param.msc_state = USBH_MSC_UNRECOVERED_STATE;
+ } else if (status == USBH_MSC_BUSY) {
+ /* no change in state */
+ }
+}
diff --git a/RISC-V/Source/usbh_msc_fatfs.c b/RISC-V/Source/usbh_msc_fatfs.c
new file mode 100644
index 0000000..c6f7ee0
--- /dev/null
+++ b/RISC-V/Source/usbh_msc_fatfs.c
@@ -0,0 +1,222 @@
+/*!
+ \file usbh_msc_fatfs.c
+ \brief USB MSC host FATFS related functions
+
+ \version 2019-06-05, V1.0.0, firmware for GD32VF103
+*/
+
+/*
+ Copyright (c) 2019, GigaDevice Semiconductor Inc.
+
+ 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. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "usb_conf.h"
+#include "diskio.h"
+#include "usbh_msc_core.h"
+
+static volatile DSTATUS state = STA_NOINIT; /* disk status */
+
+extern usb_core_driver usbh_msc_core;
+extern usbh_host usb_host;
+
+/*!
+ \brief initialize the disk drive
+ \param[in] drv: physical drive number (0)
+ \param[out] none
+ \retval operation status
+*/
+DSTATUS disk_initialize (BYTE drv)
+{
+ if (usbh_msc_core.host.connect_status) {
+ state &= ~STA_NOINIT;
+ }
+
+ return state;
+}
+
+/*!
+ \brief get disk status
+ \param[in] drv: physical drive number (0)
+ \param[out] none
+ \retval operation status
+*/
+DSTATUS disk_status (BYTE drv)
+{
+ if (drv) {
+ return STA_NOINIT; /* supports only single drive */
+ }
+
+ return state;
+}
+
+/*!
+ \brief read sectors
+ \param[in] drv: physical drive number (0)
+ \param[in] buff: pointer to the data buffer to store read data
+ \param[in] sector: start sector number (LBA)
+ \param[in] count: sector count (1..255)
+ \param[out] none
+ \retval operation status
+*/
+DRESULT disk_read (BYTE drv,
+ BYTE *buff,
+ DWORD sector,
+ BYTE count)
+{
+ BYTE status = USBH_MSC_OK;
+
+ if (drv || !count) {
+ return RES_PARERR;
+ }
+
+ if (state & STA_NOINIT) {
+ return RES_NOTRDY;
+ }
+
+ if (usbh_msc_core.host.connect_status) {
+ do {
+ status = usbh_msc_read10 (&usbh_msc_core, buff,sector, 512 * count);
+ usbh_msc_botxfer(&usbh_msc_core, &usb_host);
+
+ if (!usbh_msc_core.host.connect_status) {
+ return RES_ERROR;
+ }
+ } while(status == USBH_MSC_BUSY);
+ }
+
+ if (status == USBH_MSC_OK) {
+ return RES_OK;
+ }
+
+ return RES_ERROR;
+}
+
+#if _READONLY == 0
+
+/*!
+ \brief write sectors
+ \param[in] drv: physical drive number (0)
+ \param[in] buff: pointer to the data buffer to store read data
+ \param[in] sector: start sector number (LBA)
+ \param[in] count: sector count (1..255)
+ \param[out] none
+ \retval operation status
+*/
+DRESULT disk_write (BYTE drv,
+ const BYTE *buff,
+ DWORD sector,
+ BYTE count)
+{
+ BYTE status = USBH_MSC_OK;
+
+ if (drv || !count) {
+ return RES_PARERR;
+ }
+
+ if (state & STA_NOINIT) {
+ return RES_NOTRDY;
+ }
+
+ if (state & STA_PROTECT) {
+ return RES_WRPRT;
+ }
+
+ if (usbh_msc_core.host.connect_status) {
+ do {
+ status = usbh_msc_write10 (&usbh_msc_core, (BYTE*)buff,sector, 512 * count);
+ usbh_msc_botxfer(&usbh_msc_core, &usb_host);
+
+ if (!usbh_msc_core.host.connect_status) {
+ return RES_ERROR;
+ }
+ } while(status == USBH_MSC_BUSY);
+ }
+
+ if (status == USBH_MSC_OK) {
+ return RES_OK;
+ }
+
+ return RES_ERROR;
+}
+
+#endif /* _READONLY == 0 */
+
+/*-----------------------------------------------------------------------*/
+/* Miscellaneous Functions */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_IOCTL != 0
+
+/*!
+ \brief I/O control function
+ \param[in] drv: physical drive number (0)
+ \param[in] ctrl: control code
+ \param[in] buff: pointer to the data buffer to store read data
+ \param[out] none
+ \retval operation status
+*/
+DRESULT disk_ioctl (BYTE drv,
+ BYTE ctrl,
+ void *buff)
+{
+ DRESULT res = RES_OK;
+
+ if (drv) {
+ return RES_PARERR;
+ }
+
+ res = RES_ERROR;
+
+ if (state & STA_NOINIT) {
+ return RES_NOTRDY;
+ }
+
+ switch (ctrl) {
+ case CTRL_SYNC: /* make sure that no pending write process */
+ res = RES_OK;
+ break;
+
+ case GET_SECTOR_COUNT: /* get number of sectors on the disk (dword) */
+ *(DWORD*)buff = (DWORD) usbh_msc_param.msc_capacity;
+ res = RES_OK;
+ break;
+
+ case GET_SECTOR_SIZE: /* get r/w sector size (word) */
+ *(WORD*)buff = 512;
+ res = RES_OK;
+ break;
+
+ case GET_BLOCK_SIZE: /* get erase block size in unit of sector (dword) */
+ *(DWORD*)buff = 512;
+ break;
+
+ default:
+ res = RES_PARERR;
+ break;
+ }
+
+ return res;
+}
+#endif /* _USE_IOCTL != 0 */
diff --git a/RISC-V/Source/usbh_msc_scsi.c b/RISC-V/Source/usbh_msc_scsi.c
new file mode 100644
index 0000000..1bd9b48
--- /dev/null
+++ b/RISC-V/Source/usbh_msc_scsi.c
@@ -0,0 +1,488 @@
+/*!
+ \file usbh_msc_scsi.c
+ \brief USB MSC SCSI commands implemention
+
+ \version 2019-06-05, V1.0.0, firmware for GD32VF103
+*/
+
+/*
+ Copyright (c) 2019, GigaDevice Semiconductor Inc.
+
+ 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. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "usbh_msc_core.h"
+#include "usbh_msc_scsi.h"
+#include "usbh_msc_bbb.h"
+
+usbh_msc_parameter usbh_msc_param;
+
+uint8_t usbh_databuf_in[512];
+uint8_t usbh_databuf_out[512];
+
+/*!
+ \brief send 'Test unit ready' command to the device
+ \param[in] pudev: pointer to usb device
+ \param[out] none
+ \retval operation status
+*/
+uint8_t usbh_msc_test_unitready (usb_core_driver *pudev)
+{
+ uint8_t index;
+ usbh_msc_status status = USBH_MSC_BUSY;
+
+ if (pudev->host.connect_status) {
+ switch (msc_botxfer_param.cmd_state_machine) {
+ case CMD_SEND_STATE:
+ /* prepare the CBW and relevent field */
+ msc_cbw_data.field.dCBWDataTransferLength = 0; /* no data transfer */
+ msc_cbw_data.field.bmCBWFlags = USB_TRX_OUT;
+ msc_cbw_data.field.bCBWCBLength = CBW_LENGTH_TEST_UNIT_READY;
+
+ msc_botxfer_param.xfer_buf = msc_csw_data.CSWArray;
+ msc_botxfer_param.data_len = USBH_MSC_CSW_MAX_LENGTH;
+ msc_botxfer_param.msc_state_current = USBH_MSC_TEST_UNIT_READY;
+
+ for (index = CBW_CB_LENGTH; index != 0; index--) {
+ msc_cbw_data.field.CBWCB[index] = 0x00;
+ }
+
+ msc_cbw_data.field.CBWCB[0] = SCSI_TEST_UNIT_READY;
+ msc_botxfer_param.bot_state = USBH_MSC_SEND_CBW;
+
+ /* start the transfer, then let the state machine magage the other transactions */
+ msc_botxfer_param.msc_state = USBH_MSC_BOT_USB_TRANSFERS;
+ msc_botxfer_param.bot_xfer_status = USBH_MSC_BUSY;
+ msc_botxfer_param.cmd_state_machine = CMD_WAIT_STATUS;
+
+ status = USBH_MSC_BUSY;
+ break;
+
+ case CMD_WAIT_STATUS:
+ if (msc_botxfer_param.bot_xfer_status == USBH_MSC_OK) {
+ /* commands successfully sent and response received */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_OK;
+ } else if ( msc_botxfer_param.bot_xfer_status == USBH_MSC_FAIL) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_FAIL;
+ } else if (msc_botxfer_param.bot_xfer_status == USBH_MSC_PHASE_ERROR) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_PHASE_ERROR;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return status;
+}
+
+/*!
+ \brief send the read capacity command to the device
+ \param[in] pudev: pointer to usb device
+ \param[out] none
+ \retval operation status
+*/
+uint8_t usbh_msc_read_capacity10 (usb_core_driver *pudev)
+{
+ uint8_t index;
+ usbh_msc_status status = USBH_MSC_BUSY;
+
+ if (pudev->host.connect_status) {
+ switch (msc_botxfer_param.cmd_state_machine) {
+ case CMD_SEND_STATE:
+ /* prepare the CBW and relevent field */
+ msc_cbw_data.field.dCBWDataTransferLength = READ_CAPACITY10_DATA_LEN;
+ msc_cbw_data.field.bmCBWFlags = USB_TRX_IN;
+ msc_cbw_data.field.bCBWCBLength = CBW_LENGTH;
+
+ msc_botxfer_param.xfer_buf = usbh_databuf_in;
+ msc_botxfer_param.msc_state_current = USBH_MSC_READ_CAPACITY10;
+
+ for (index = CBW_CB_LENGTH; index != 0; index--) {
+ msc_cbw_data.field.CBWCB[index] = 0x00;
+ }
+
+ msc_cbw_data.field.CBWCB[0] = SCSI_READ_CAPACITY10;
+ msc_botxfer_param.bot_state = USBH_MSC_SEND_CBW;
+
+ /* start the transfer, then let the state machine manage the other transactions */
+ msc_botxfer_param.msc_state = USBH_MSC_BOT_USB_TRANSFERS;
+ msc_botxfer_param.bot_xfer_status = USBH_MSC_BUSY;
+ msc_botxfer_param.cmd_state_machine = CMD_WAIT_STATUS;
+
+ status = USBH_MSC_BUSY;
+ break;
+
+ case CMD_WAIT_STATUS:
+ if (msc_botxfer_param.bot_xfer_status == USBH_MSC_OK) {
+ /* assign the capacity */
+ (((uint8_t*)&usbh_msc_param.msc_capacity )[3]) = usbh_databuf_in[0];
+ (((uint8_t*)&usbh_msc_param.msc_capacity )[2]) = usbh_databuf_in[1];
+ (((uint8_t*)&usbh_msc_param.msc_capacity )[1]) = usbh_databuf_in[2];
+ (((uint8_t*)&usbh_msc_param.msc_capacity )[0]) = usbh_databuf_in[3];
+
+ /* assign the page length */
+ (((uint8_t*)&usbh_msc_param.msc_page_len )[1]) = usbh_databuf_in[6];
+ (((uint8_t*)&usbh_msc_param.msc_page_len )[0]) = usbh_databuf_in[7];
+
+ /* commands successfully sent and response received */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_OK;
+ } else if (msc_botxfer_param.bot_xfer_status == USBH_MSC_FAIL) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_FAIL;
+ } else if (msc_botxfer_param.bot_xfer_status == USBH_MSC_PHASE_ERROR) {
+ /* Failure Mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_PHASE_ERROR;
+ } else {
+ /* wait for the commands to get completed */
+ /* no change in state machine */
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return status;
+}
+
+/*!
+ \brief send the mode sense6 command to the device
+ \param[in] pudev: pointer to usb device
+ \param[out] none
+ \retval operation status
+*/
+uint8_t usbh_msc_mode_sense6 (usb_core_driver *pudev)
+{
+ uint8_t index;
+ usbh_msc_status status = USBH_MSC_BUSY;
+
+ if (pudev->host.connect_status) {
+ switch (msc_botxfer_param.cmd_state_machine) {
+ case CMD_SEND_STATE:
+ /* prepare the CBW and relevent field */
+ msc_cbw_data.field.dCBWDataTransferLength = XFER_LEN_MODE_SENSE6;
+ msc_cbw_data.field.bmCBWFlags = USB_TRX_IN;
+ msc_cbw_data.field.bCBWCBLength = CBW_LENGTH;
+
+ msc_botxfer_param.xfer_buf = usbh_databuf_in;
+ msc_botxfer_param.msc_state_current = USBH_MSC_MODE_SENSE6;
+
+ for (index = CBW_CB_LENGTH; index != 0; index--) {
+ msc_cbw_data.field.CBWCB[index] = 0x00;
+ }
+
+ msc_cbw_data.field.CBWCB[0] = SCSI_MODE_SENSE6;
+ msc_cbw_data.field.CBWCB[2] = MODE_SENSE_PAGE_CONTROL_FIELD | MODE_SENSE_PAGE_CODE;
+ msc_cbw_data.field.CBWCB[4] = XFER_LEN_MODE_SENSE6;
+ msc_botxfer_param.bot_state = USBH_MSC_SEND_CBW;
+
+ /* start the transfer, then let the state machine manage the other transactions */
+ msc_botxfer_param.msc_state = USBH_MSC_BOT_USB_TRANSFERS;
+ msc_botxfer_param.bot_xfer_status = USBH_MSC_BUSY;
+ msc_botxfer_param.cmd_state_machine = CMD_WAIT_STATUS;
+
+ status = USBH_MSC_BUSY;
+ break;
+
+ case CMD_WAIT_STATUS:
+ if (msc_botxfer_param.bot_xfer_status == USBH_MSC_OK) {
+ /* assign the write protect status */
+ /* if writeprotect = 0, writing is allowed
+ if writeprotect != 0, disk is write protected */
+ if (usbh_databuf_in[2] & MASK_MODE_SENSE_WRITE_PROTECT) {
+ usbh_msc_param.msc_write_protect = DISK_WRITE_PROTECTED;
+ } else {
+ usbh_msc_param.msc_write_protect = 0;
+ }
+
+ /* commands successfully sent and response received */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_OK;
+ } else if (msc_botxfer_param.bot_xfer_status == USBH_MSC_FAIL) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_FAIL;
+ } else if (msc_botxfer_param.bot_xfer_status == USBH_MSC_PHASE_ERROR) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_PHASE_ERROR;
+ } else {
+ /* wait for the commands to get completed */
+ /* no change in state machine */
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return status;
+}
+
+/*!
+ \brief send the Request Sense command to the device
+ \param[in] pudev: pointer to usb device
+ \param[out] none
+ \retval operation status
+*/
+uint8_t usbh_msc_request_sense (usb_core_driver *pudev)
+{
+ usbh_msc_status status = USBH_MSC_BUSY;
+ uint8_t index;
+
+ if (pudev->host.connect_status) {
+ switch (msc_botxfer_param.cmd_state_machine) {
+ case CMD_SEND_STATE:
+ /* prepare the cbw and relevent field */
+ msc_cbw_data.field.dCBWDataTransferLength = ALLOCATION_LENGTH_REQUEST_SENSE;
+ msc_cbw_data.field.bmCBWFlags = USB_TRX_IN;
+ msc_cbw_data.field.bCBWCBLength = CBW_LENGTH;
+
+ msc_botxfer_param.xfer_buf = usbh_databuf_in;
+ msc_botxfer_param.msc_state_bkp = msc_botxfer_param.msc_state_current;
+ msc_botxfer_param.msc_state_current = USBH_MSC_REQUEST_SENSE;
+
+ for (index = CBW_CB_LENGTH; index != 0; index--) {
+ msc_cbw_data.field.CBWCB[index] = 0x00;
+ }
+
+ msc_cbw_data.field.CBWCB[0] = SCSI_REQUEST_SENSE;
+ msc_cbw_data.field.CBWCB[1] = DESC_REQUEST_SENSE;
+ msc_cbw_data.field.CBWCB[4] = ALLOCATION_LENGTH_REQUEST_SENSE;
+
+ msc_botxfer_param.bot_state = USBH_MSC_SEND_CBW;
+
+ /* start the transfer, then let the state machine magage the other transactions */
+ msc_botxfer_param.msc_state = USBH_MSC_BOT_USB_TRANSFERS;
+ msc_botxfer_param.bot_xfer_status = USBH_MSC_BUSY;
+ msc_botxfer_param.cmd_state_machine = CMD_WAIT_STATUS;
+
+ status = USBH_MSC_BUSY;
+ break;
+
+ case CMD_WAIT_STATUS:
+ if (msc_botxfer_param.bot_xfer_status == USBH_MSC_OK) {
+ /* get sense data */
+ (((uint8_t*)&usbh_msc_param.msc_sense_key )[3]) = usbh_databuf_in[0];
+ (((uint8_t*)&usbh_msc_param.msc_sense_key )[2]) = usbh_databuf_in[1];
+ (((uint8_t*)&usbh_msc_param.msc_sense_key )[1]) = usbh_databuf_in[2];
+ (((uint8_t*)&usbh_msc_param.msc_sense_key )[0]) = usbh_databuf_in[3];
+
+ /* commands successfully sent and response received */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_OK;
+ } else if (msc_botxfer_param.bot_xfer_status == USBH_MSC_FAIL) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_FAIL;
+ } else if ( msc_botxfer_param.bot_xfer_status == USBH_MSC_PHASE_ERROR ) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_PHASE_ERROR;
+ } else {
+ /* wait for the commands to get completed */
+ /* no change in state machine */
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return status;
+}
+
+/*!
+ \brief send the write10 command to the device
+ \param[in] pudev: pointer to usb device
+ \param[in] data_buf: data buffer contains the data to write
+ \param[in] addr: address to which the data will be written
+ \param[in] byte_num: number of bytes to be written
+ \param[out] none
+ \retval operation status
+*/
+uint8_t usbh_msc_write10 (usb_core_driver *pudev, uint8_t *data_buf, uint32_t addr, uint32_t byte_num)
+{
+ uint8_t index;
+ uint16_t nbOfPages;
+ usbh_msc_status status = USBH_MSC_BUSY;
+
+ if (pudev->host.connect_status) {
+ switch (msc_botxfer_param.cmd_state_machine) {
+ case CMD_SEND_STATE:
+ msc_cbw_data.field.dCBWDataTransferLength = byte_num;
+ msc_cbw_data.field.bmCBWFlags = USB_TRX_OUT;
+ msc_cbw_data.field.bCBWCBLength = CBW_LENGTH;
+ msc_botxfer_param.xfer_buf = data_buf;
+
+ for (index = CBW_CB_LENGTH; index != 0; index--) {
+ msc_cbw_data.field.CBWCB[index] = 0x00;
+ }
+
+ msc_cbw_data.field.CBWCB[0] = SCSI_WRITE10;
+
+ /* logical block address */
+ msc_cbw_data.field.CBWCB[2] = (((uint8_t*)&addr)[3]);
+ msc_cbw_data.field.CBWCB[3] = (((uint8_t*)&addr)[2]);
+ msc_cbw_data.field.CBWCB[4] = (((uint8_t*)&addr)[1]);
+ msc_cbw_data.field.CBWCB[5] = (((uint8_t*)&addr)[0]);
+
+ /* USBH_MSC_PAGE_LENGTH = 512*/
+ nbOfPages = byte_num / USBH_MSC_PAGE_LENGTH;
+
+ /* tranfer length */
+ msc_cbw_data.field.CBWCB[7] = (((uint8_t *)&nbOfPages)[1]);
+ msc_cbw_data.field.CBWCB[8] = (((uint8_t *)&nbOfPages)[0]);
+
+ msc_botxfer_param.bot_state = USBH_MSC_SEND_CBW;
+
+ /* start the transfer, then let the state machine magage the other transactions */
+ msc_botxfer_param.msc_state = USBH_MSC_BOT_USB_TRANSFERS;
+ msc_botxfer_param.bot_xfer_status = USBH_MSC_BUSY;
+ msc_botxfer_param.cmd_state_machine = CMD_WAIT_STATUS;
+
+ status = USBH_MSC_BUSY;
+ break;
+
+ case CMD_WAIT_STATUS:
+ if (msc_botxfer_param.bot_xfer_status == USBH_MSC_OK) {
+ /* commands successfully sent and response received */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_OK;
+ } else if (msc_botxfer_param.bot_xfer_status == USBH_MSC_FAIL) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ } else if (msc_botxfer_param.bot_xfer_status == USBH_MSC_PHASE_ERROR) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_PHASE_ERROR;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return status;
+}
+
+/*!
+ \brief send the read10 command to the device
+ \param[in] pudev: pointer to usb device
+ \param[in] data_buf: data buffer contains the data to write
+ \param[in] addr: address to which the data will be written
+ \param[in] byte_num: number of bytes to be written
+ \param[out] none
+ \retval operation status
+*/
+uint8_t usbh_msc_read10 (usb_core_driver *pudev, uint8_t *data_buf, uint32_t addr, uint32_t byte_num)
+{
+ uint8_t index;
+ uint16_t nbOfPages;
+
+ static usbh_msc_status status = USBH_MSC_BUSY;
+
+ status = USBH_MSC_BUSY;
+
+ if (pudev->host.connect_status) {
+ switch (msc_botxfer_param.cmd_state_machine) {
+ case CMD_SEND_STATE:
+ /* prepare the CBW and relevent field */
+ msc_cbw_data.field.dCBWDataTransferLength = byte_num;
+ msc_cbw_data.field.bmCBWFlags = USB_TRX_IN;
+ msc_cbw_data.field.bCBWCBLength = CBW_LENGTH;
+
+ msc_botxfer_param.xfer_buf = data_buf;
+
+ for(index = CBW_CB_LENGTH; index != 0; index--)
+ {
+ msc_cbw_data.field.CBWCB[index] = 0x00;
+ }
+
+ msc_cbw_data.field.CBWCB[0] = SCSI_READ10;
+
+ /* logical block address */
+ msc_cbw_data.field.CBWCB[2] = (((uint8_t*)&addr)[3]);
+ msc_cbw_data.field.CBWCB[3] = (((uint8_t*)&addr)[2]);
+ msc_cbw_data.field.CBWCB[4] = (((uint8_t*)&addr)[1]);
+ msc_cbw_data.field.CBWCB[5] = (((uint8_t*)&addr)[0]);
+
+ /* USBH_MSC_PAGE_LENGTH = 512 */
+ nbOfPages = byte_num / USBH_MSC_PAGE_LENGTH;
+
+ /* tranfer length */
+ msc_cbw_data.field.CBWCB[7] = (((uint8_t *)&nbOfPages)[1]);
+ msc_cbw_data.field.CBWCB[8] = (((uint8_t *)&nbOfPages)[0]);
+
+ msc_botxfer_param.bot_state = USBH_MSC_SEND_CBW;
+
+ /* start the transfer, then let the state machine magage the other transactions */
+ msc_botxfer_param.msc_state = USBH_MSC_BOT_USB_TRANSFERS;
+ msc_botxfer_param.bot_xfer_status = USBH_MSC_BUSY;
+ msc_botxfer_param.cmd_state_machine = CMD_WAIT_STATUS;
+
+ status = USBH_MSC_BUSY;
+ break;
+
+ case CMD_WAIT_STATUS:
+ if ((msc_botxfer_param.bot_xfer_status == USBH_MSC_OK) && \
+ (pudev->host.connect_status)) {
+ /* commands successfully sent and response received */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_OK;
+ } else if ((msc_botxfer_param.bot_xfer_status == USBH_MSC_FAIL) && \
+ (pudev->host.connect_status)) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ } else if (msc_botxfer_param.bot_xfer_status == USBH_MSC_PHASE_ERROR) {
+ /* failure mode */
+ msc_botxfer_param.cmd_state_machine = CMD_SEND_STATE;
+ status = USBH_MSC_PHASE_ERROR;
+ } else {
+ /* wait for the commands to get completed */
+ /* no change in state machine */
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return status;
+}
diff --git a/RISC-V/Source/usbh_usr.c b/RISC-V/Source/usbh_usr.c
new file mode 100644
index 0000000..6ec5750
--- /dev/null
+++ b/RISC-V/Source/usbh_usr.c
@@ -0,0 +1,512 @@
+/*!
+ \file usbh_usr.c
+ \brief user application layer for USBFS host-mode MSC class operation
+
+ \version 2019-06-05, V1.0.0, firmware for GD32VF103
+*/
+
+/*
+ Copyright (c) 2019, GigaDevice Semiconductor Inc.
+
+ 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. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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
+#include "usbh_usr.h"
+#include "drv_usb_hw.h"
+#include "ff.h"
+#include "usbh_msc_core.h"
+#include "usbh_msc_scsi.h"
+#include "usbh_msc_bbb.h"
+
+extern usb_core_driver usbh_msc_core;
+
+FATFS fatfs;
+FIL file;
+
+uint8_t line_idx = 0;
+uint8_t need_clear = 0;
+uint8_t usbh_usr_application_state = USBH_USR_FS_INIT;
+uint8_t WriteTextBuff[] = "GD32 Connectivity line Host Demo application using FAT_FS ";
+
+/* points to the DEVICE_PROP structure of current device */
+usbh_user_cb user_callback_funs =
+{
+ usbh_user_init,
+ usbh_user_deinit,
+ usbh_user_device_connected,
+ usbh_user_device_reset,
+ usbh_user_device_disconnected,
+ usbh_user_over_current_detected,
+ usbh_user_device_speed_detected,
+ usbh_user_device_desc_available,
+ usbh_user_device_address_assigned,
+ usbh_user_configuration_descavailable,
+ usbh_user_manufacturer_string,
+ usbh_user_product_string,
+ usbh_user_serialnum_string,
+ usbh_user_enumeration_finish,
+ usbh_user_userinput,
+ usbh_usr_msc_application,
+ usbh_user_device_not_supported,
+ usbh_user_unrecovered_error
+};
+
+const uint8_t MSG_HOST_INIT[] = "> Host Library Initialized.";
+const uint8_t MSG_DEV_ATTACHED[] = "> Device Attached.";
+const uint8_t MSG_DEV_DISCONNECTED[] = "> Device Disconnected.";
+const uint8_t MSG_DEV_ENUMERATED[] = "> Enumeration completed.";
+const uint8_t MSG_DEV_HIGHSPEED[] = "> High speed device detected.";
+const uint8_t MSG_DEV_FULLSPEED[] = "> Full speed device detected.";
+const uint8_t MSG_DEV_LOWSPEED[] = "> Low speed device detected.";
+const uint8_t MSG_DEV_ERROR[] = "> Device fault.";
+
+const uint8_t MSG_HOST_HEADER[] = "> USBFS MSC Host";
+const uint8_t MSG_HOST_FOOTER[] = "> USB Host Library v2.0.0";
+
+const uint8_t MSG_LIB_START[] = "##### USB Host library started #####";
+const uint8_t MSG_DEV_NOSUP[] = "> Device not supported.";
+const uint8_t MSG_OVERCURRENT[] = "> Overcurrent detected.";
+const uint8_t MSG_RESET_DEV[] = "> Reset the USB device.";
+
+const uint8_t MSG_MSC_CLASS[] = "> Mass storage device connected.";
+const uint8_t MSG_HID_CLASS[] = "> HID device connected.";
+const uint8_t MSG_DISK_SIZE[] = "> Size of the disk in MBytes: ";
+const uint8_t MSG_LUN[] = "> LUN Available in the device:";
+const uint8_t MSG_ROOT_CONT[] = "> Exploring disk flash ...";
+const uint8_t MSG_WR_PROTECT[] = "> The disk is write protected.";
+const uint8_t MSG_UNREC_ERROR[] = "> UNRECOVERED ERROR STATE.";
+const uint8_t MSG_FILE_NOTINIT[] = "> Cannot initialize File System.";
+const uint8_t MSG_FILE_INIT[] = "> File System initialized.";
+const uint8_t MSG_Write_File[] = "> Writing File to disk flash ...";
+const uint8_t MSG_Write_Protect[] = "> Disk flash is write protected ";
+const uint8_t MSG_NOT_WRITE[] = "> GD32.TXT CANNOT be writen.";
+const uint8_t MSG_CREATE_FILE[] = "> GD32.TXT created in the disk.";
+
+static uint8_t explore_disk (char* path, uint8_t recu_level);
+
+/*!
+ \brief user operation for host-mode initialization
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_init(void)
+{
+ static uint8_t startup = 0U;
+
+ if (0U == startup) {
+ startup = 1U;
+
+ //lcd_log_print((uint8_t *)MSG_LIB_START, sizeof(MSG_LIB_START) - 1, LCD_COLOR_WHITE);
+
+ //lcd_log_footer_set((uint8_t *)MSG_HOST_FOOTER, 70);
+ }
+}
+
+/*!
+ \brief user operation for device attached
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_device_connected(void)
+{
+ //lcd_log_print((uint8_t *)MSG_DEV_ATTACHED, sizeof(MSG_DEV_ATTACHED) - 1, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief user operation when unrecoveredError happens
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_unrecovered_error (void)
+{
+ //lcd_log_print((uint8_t *)MSG_UNREC_ERROR, sizeof(MSG_UNREC_ERROR) - 1, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief user operation for device disconnect event
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_device_disconnected (void)
+{
+ //lcd_log_print((uint8_t *)MSG_DEV_DISCONNECTED, sizeof(MSG_DEV_DISCONNECTED) - 1, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief user operation for reset USB Device
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_device_reset(void)
+{
+ //lcd_log_print((uint8_t *)MSG_RESET_DEV, sizeof(MSG_RESET_DEV) - 1, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief user operation for detectting device speed
+ \param[in] device_speed: device speed
+ \param[out] none
+ \retval none
+*/
+void usbh_user_device_speed_detected(uint32_t device_speed)
+{
+ if (PORT_SPEED_HIGH == device_speed) {
+ //lcd_log_print((uint8_t *)MSG_DEV_HIGHSPEED, sizeof(MSG_DEV_HIGHSPEED) - 1, LCD_COLOR_WHITE);
+ } else if(PORT_SPEED_FULL == device_speed) {
+ //lcd_log_print((uint8_t *)MSG_DEV_FULLSPEED, sizeof(MSG_DEV_FULLSPEED) - 1, LCD_COLOR_WHITE);
+ } else if(PORT_SPEED_LOW == device_speed) {
+ //lcd_log_print((uint8_t *)MSG_DEV_LOWSPEED, sizeof(MSG_DEV_LOWSPEED) - 1, LCD_COLOR_WHITE);
+ } else {
+ //lcd_log_print((uint8_t *)MSG_DEV_ERROR, sizeof(MSG_DEV_ERROR) - 1, LCD_COLOR_WHITE);
+ }
+}
+
+/*!
+ \brief user operation when device descriptor is available
+ \param[in] device_desc: device descriptor
+ \param[out] none
+ \retval none
+*/
+void usbh_user_device_desc_available(void *device_desc)
+{
+ uint8_t TempStr[64], str_len = 0;
+ usb_desc_dev *pDevStr = device_desc;
+
+ sprintf((char *)TempStr, "VID: %04Xh", (uint32_t)pDevStr->idVendor);
+ str_len = strlen((const char *)TempStr);
+ //lcd_log_print((uint8_t *)TempStr, str_len, LCD_COLOR_WHITE);
+
+ sprintf((char *)TempStr, "PID: %04Xh", (uint32_t)pDevStr->idProduct);
+ str_len = strlen((const char *)TempStr);
+ //lcd_log_print((uint8_t *)TempStr, str_len, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief usb device is successfully assigned the Address
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_device_address_assigned(void)
+{
+}
+
+/*!
+ \brief user operation when configuration descriptor is available
+ \param[in] cfg_desc: pointer to configuration descriptor
+ \param[in] itf_desc: pointer to interface descriptor
+ \param[in] ep_desc: pointer to endpoint descriptor
+ \param[out] none
+ \retval none
+*/
+void usbh_user_configuration_descavailable(usb_desc_config *cfg_desc,
+ usb_desc_itf *itf_desc,
+ usb_desc_ep *ep_desc)
+{
+ usb_desc_itf *id = itf_desc;
+
+ if (0x08U == (*id).bInterfaceClass) {
+ //lcd_log_print((uint8_t *)MSG_MSC_CLASS, sizeof(MSG_MSC_CLASS) - 1, LCD_COLOR_WHITE);
+ } else if (0x03U == (*id).bInterfaceClass) {
+ //lcd_log_print((uint8_t *)MSG_HID_CLASS, sizeof(MSG_HID_CLASS) - 1, LCD_COLOR_WHITE);
+ }
+}
+
+/*!
+ \brief user operation when manufacturer string exists
+ \param[in] manufacturer_string: manufacturer string of usb device
+ \param[out] none
+ \retval none
+*/
+void usbh_user_manufacturer_string(void *manufacturer_string)
+{
+ uint8_t TempStr[64], str_len = 0;
+
+ sprintf((char *)TempStr, "Manufacturer: %s", (char *)manufacturer_string);
+ str_len = strlen((const char *)TempStr);
+ //lcd_log_print((uint8_t *)TempStr, str_len, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief user operation when product string exists
+ \param[in] product_string: product string of usb device
+ \param[out] none
+ \retval none
+*/
+void usbh_user_product_string(void *product_string)
+{
+ uint8_t TempStr[64], str_len = 0;
+
+ sprintf((char *)TempStr, "Product: %s", (char *)product_string);
+ str_len = strlen((const char *)TempStr);
+ //lcd_log_print((uint8_t *)TempStr, str_len, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief user operatin when serialNum string exists
+ \param[in] serial_num_string: serialNum string of usb device
+ \param[out] none
+ \retval none
+*/
+void usbh_user_serialnum_string(void *serial_num_string)
+{
+ uint8_t TempStr[64], str_len = 0;
+
+ sprintf((char *)TempStr, "Serial Number: %s", (char *)serial_num_string);
+ str_len = strlen((const char *)TempStr);
+ //lcd_log_print((uint8_t *)TempStr, str_len, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief user response request is displayed to ask for application jump to class
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_enumeration_finish(void)
+{
+ uint8_t Str1[] = "To see the disk information: ";
+ uint8_t Str2[] = "Press CET Key...";
+
+ //lcd_log_print((uint8_t *)MSG_DEV_ENUMERATED, sizeof(MSG_DEV_ENUMERATED) - 1, LCD_COLOR_WHITE);
+ //lcd_log_print((uint8_t *)Str1, sizeof(Str1) - 1, LCD_COLOR_GREEN);
+ //lcd_log_print((uint8_t *)Str2, sizeof(Str2) - 1, LCD_COLOR_GREEN);
+}
+
+/*!
+ \brief user operation when device is not supported
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_device_not_supported(void)
+{
+ //lcd_log_print((uint8_t *)MSG_DEV_NOSUP, sizeof(MSG_DEV_NOSUP) - 1, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief user action for application state entry
+ \param[in] none
+ \param[out] none
+ \retval user response for user key
+*/
+usbh_user_status usbh_user_userinput(void)
+{
+ usbh_user_status usbh_usr_status = USBH_USER_NO_RESP;
+
+ return usbh_usr_status;
+}
+
+/*!
+ \brief user operation for device overcurrent detection event
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_over_current_detected (void)
+{
+ //lcd_log_print((uint8_t *)MSG_OVERCURRENT, sizeof(MSG_OVERCURRENT) - 1, LCD_COLOR_WHITE);
+}
+
+/*!
+ \brief demo application for mass storage
+ \param[in] pudev: pointer to device
+ \param[in] id: no use here
+ \param[out] none
+ \retval status
+*/
+int usbh_usr_msc_application(void)
+{
+ FRESULT res;
+ uint16_t bytesWritten, bytesToWrite;
+
+ uint8_t Str1[] = "> To see the root content of the disk ";
+ uint8_t Str2[] = "> Press C Key to write file";
+ uint8_t Str3[] = "> The MSC host demo is end.";
+ uint8_t Str4[] = "> Press CET key...";
+ uint8_t TempStr[64], str_len = 0;
+
+ switch(usbh_usr_application_state)
+ {
+ case USBH_USR_FS_INIT:
+ /* initialises the file system*/
+ if (FR_OK != f_mount(0, &fatfs)) {
+ //lcd_log_print((uint8_t *)MSG_FILE_NOTINIT, sizeof(MSG_FILE_NOTINIT) - 1, LCD_COLOR_WHITE);
+
+ return(-1);
+ }
+
+ //lcd_log_print((uint8_t *)MSG_FILE_INIT, sizeof(MSG_FILE_INIT) - 1, LCD_COLOR_WHITE);
+
+ sprintf((char *)TempStr, "> Disk capacity: %ud Bytes.", \
+ usbh_msc_param.msc_capacity * usbh_msc_param.msc_page_len);
+
+ str_len = strlen((const char *)TempStr);
+
+ //lcd_log_print((uint8_t *)TempStr, str_len, LCD_COLOR_WHITE);
+
+ if (DISK_WRITE_PROTECTED == usbh_msc_param.msc_write_protect) {
+ //lcd_log_print((uint8_t *)MSG_WR_PROTECT, sizeof(MSG_WR_PROTECT) - 1, LCD_COLOR_WHITE);
+ }
+
+ usbh_usr_application_state = USBH_USR_FS_READLIST;
+ break;
+
+ case USBH_USR_FS_READLIST:
+ //lcd_log_print((uint8_t *)MSG_ROOT_CONT, sizeof(MSG_ROOT_CONT) - 1, LCD_COLOR_WHITE);
+ //lcd_log_print((uint8_t *)Str1, sizeof(Str1) - 1, LCD_COLOR_GREEN);
+ //lcd_log_print((uint8_t *)Str4, sizeof(Str4) - 1, LCD_COLOR_GREEN);
+
+ //explore_disk("0:/", 1);
+ line_idx = 0;
+ usbh_usr_application_state = USBH_USR_FS_WRITEFILE;
+ break;
+
+ case USBH_USR_FS_WRITEFILE:
+ usb_mdelay(100U);
+
+ //lcd_log_print((uint8_t *)Str2, sizeof(Str2) - 1, LCD_COLOR_GREEN);
+
+ //lcd_log_print((uint8_t *)MSG_Write_File, sizeof(MSG_Write_File) - 1, LCD_COLOR_WHITE);
+
+ if (DISK_WRITE_PROTECTED == usbh_msc_param.msc_write_protect) {
+ //lcd_log_print((uint8_t *)MSG_Write_Protect, sizeof(MSG_Write_Protect) - 1, LCD_COLOR_WHITE);
+
+ usbh_usr_application_state = USBH_USR_FS_DEMOEND;
+ break;
+ }
+
+ /* register work area for logical drives */
+ f_mount(0, &fatfs);
+
+ if (FR_OK == f_open(&file, "0:GD32.TXT", FA_CREATE_ALWAYS | FA_WRITE)) {
+ /* write buffer to file */
+ bytesToWrite = sizeof(WriteTextBuff);
+ res = f_write (&file, WriteTextBuff, bytesToWrite, (void *)&bytesWritten);
+ /* EOF or error */
+ if ((0U == bytesWritten) || (FR_OK != res)) {
+ //lcd_log_print((uint8_t *)MSG_NOT_WRITE, sizeof(MSG_NOT_WRITE) - 1, LCD_COLOR_WHITE);
+ } else {
+ //lcd_log_print((uint8_t *)MSG_CREATE_FILE, sizeof(MSG_CREATE_FILE) - 1, LCD_COLOR_WHITE);
+ }
+
+ /* close file and filesystem */
+ f_close(&file);
+ f_mount(0, NULL);
+ } else {
+ //lcd_log_print((uint8_t *)MSG_CREATE_FILE, sizeof(MSG_CREATE_FILE) - 1, LCD_COLOR_WHITE);
+ }
+
+ usbh_usr_application_state = USBH_USR_FS_DEMOEND;
+ //lcd_log_print((uint8_t *)Str3, sizeof(Str3) - 1, LCD_COLOR_GREEN);
+ break;
+
+ case USBH_USR_FS_DEMOEND:
+ break;
+
+ default:
+ break;
+ }
+
+ return(0);
+}
+
+/*!
+ \brief displays disk content
+ \param[in] path: pointer to root path
+ \param[in] recu_level: recursive level
+ \param[out] none
+ \retval status
+*/
+static uint8_t explore_disk (char* path, uint8_t recu_level)
+{
+ FRESULT res;
+ FILINFO fno;
+ DIR dir;
+ char *fn;
+
+ uint8_t Str2[] = "Press B Key to continue";
+
+ res = f_opendir(&dir, path);
+
+ if (res == FR_OK) {
+ while (usbh_msc_core.host.connect_status) {
+ res = f_readdir(&dir, &fno);
+ if (FR_OK != res || 0U == fno.fname[0]) {
+ break;
+ }
+
+ if ('.' == fno.fname[0]) {
+ continue;
+ }
+
+ fn = fno.fname;
+
+ line_idx++;
+
+ if (line_idx > 4) {
+ line_idx = 0;
+
+ //lcd_log_print((uint8_t *)Str2, sizeof(Str2) - 1, LCD_COLOR_GREEN);
+ }
+
+ if (1U == recu_level) {
+ uint8_t temp[] = " |__";
+ //lcd_log_print((uint8_t *)temp, strlen((const char *)temp), LCD_COLOR_WHITE);
+ } else if(2U == recu_level) {
+ uint8_t temp[] = " | |__";
+ //lcd_log_print((uint8_t *)temp, strlen((const char *)temp), LCD_COLOR_WHITE);
+ }
+
+ if (AM_DIR == (fno.fattrib & AM_MASK)) {
+ //lcd_log_print((uint8_t *)fno.fname, strlen(fno.fname), LCD_COLOR_RED);
+ } else {
+ //lcd_log_print((uint8_t *)fno.fname, strlen(fno.fname), LCD_COLOR_WHITE);
+ }
+
+ if ((AM_DIR == (fno.fattrib & AM_MASK)) && (1U == recu_level)) {
+ explore_disk(fn, 2);
+ }
+ }
+ }
+
+ return res;
+}
+
+/*!
+ \brief deinit user state and associated variables
+ \param[in] none
+ \param[out] none
+ \retval none
+*/
+void usbh_user_deinit(void)
+{
+ usbh_usr_application_state = USBH_USR_FS_INIT;
+}