mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-01-19 04:52:12 +01:00
Merge remote-tracking branch 'origin/rel-15.01'
This commit is contained in:
commit
4ce2b63644
95
Makefile
95
Makefile
@ -47,6 +47,7 @@ export DL_DIR := $(if $(OPENPILOT_DL_DIR),$(call slashfix,$(OPENPILOT_DL_DI
|
||||
export TOOLS_DIR := $(if $(OPENPILOT_TOOLS_DIR),$(call slashfix,$(OPENPILOT_TOOLS_DIR)),$(ROOT_DIR)/tools)
|
||||
export BUILD_DIR := $(ROOT_DIR)/build
|
||||
export PACKAGE_DIR := $(ROOT_DIR)/build/package
|
||||
export DIST_DIR := $(ROOT_DIR)/build/dist
|
||||
|
||||
# Set up default build configurations (debug | release)
|
||||
GCS_BUILD_CONF := release
|
||||
@ -65,18 +66,24 @@ $(if $(filter-out undefined,$(origin $(1))),
|
||||
)
|
||||
endef
|
||||
|
||||
# These specific variables can influence gcc in unexpected (and undesirable) ways
|
||||
# These specific variables can influence compilation in unexpected (and undesirable) ways
|
||||
# gcc flags
|
||||
SANITIZE_GCC_VARS := TMPDIR GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH
|
||||
SANITIZE_GCC_VARS += CFLAGS CPATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH OBJC_INCLUDE_PATH DEPENDENCIES_OUTPUT
|
||||
# preprocessor flags
|
||||
SANITIZE_GCC_VARS += CPATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH OBJC_INCLUDE_PATH DEPENDENCIES_OUTPUT
|
||||
# make flags
|
||||
SANITIZE_GCC_VARS += CFLAGS CXXFLAGS CPPFLAGS LDFLAGS LDLIBS
|
||||
$(foreach var, $(SANITIZE_GCC_VARS), $(eval $(call SANITIZE_VAR,$(var),disallowed)))
|
||||
|
||||
# These specific variables used to be valid but now they make no sense
|
||||
SANITIZE_DEPRECATED_VARS := USE_BOOTLOADER CLEAN_BUILD
|
||||
$(foreach var, $(SANITIZE_DEPRECATED_VARS), $(eval $(call SANITIZE_VAR,$(var),deprecated)))
|
||||
|
||||
# Make sure this isn't being run as root (no whoami on Windows, but that is ok here)
|
||||
# Make sure this isn't being run as root unless installing (no whoami on Windows, but that is ok here)
|
||||
ifeq ($(shell whoami 2>/dev/null),root)
|
||||
$(error You should not be running this as root)
|
||||
ifeq ($(filter install all_clean,$(MAKECMDGOALS)),)
|
||||
$(error You should not be running this as root)
|
||||
endif
|
||||
endif
|
||||
|
||||
# Decide on a verbosity level based on the V= parameter
|
||||
@ -776,9 +783,6 @@ ifneq ($(strip $(filter package clean_package,$(MAKECMDGOALS))),)
|
||||
export PACKAGE_NAME := OpenPilot
|
||||
export PACKAGE_SEP := -
|
||||
|
||||
# Copy the Qt libraries regardless whether the building machine needs them to run GCS
|
||||
export FORCE_COPY_QT := true
|
||||
|
||||
# We can only package release builds
|
||||
ifneq ($(GCS_BUILD_CONF),release)
|
||||
$(error Packaging is currently supported for release builds only)
|
||||
@ -801,24 +805,11 @@ ifneq ($(strip $(filter package clean_package,$(MAKECMDGOALS))),)
|
||||
endif
|
||||
endif
|
||||
|
||||
# Copy file template. Empty line before the endef is required, do not remove
|
||||
# $(1) = copy file name without extension
|
||||
# $(2) = source file extension
|
||||
# $(3) = destination file extension
|
||||
define COPY_FW_FILES
|
||||
$(V1) $(CP) "$(BUILD_DIR)/$(1)/$(1)$(2)" "$(PACKAGE_DIR)/firmware/$(1)$(PACKAGE_SEP)$(PACKAGE_LBL)$(3)"
|
||||
|
||||
endef
|
||||
|
||||
# Build and copy package files into the package directory
|
||||
# and call platform-specific packaging script
|
||||
.PHONY: package
|
||||
package: all_fw all_ground uavobjects_matlab
|
||||
@$(ECHO) "Packaging for $(UNAME) $(ARCH) into $(call toprel, $(PACKAGE_DIR)) directory"
|
||||
$(V1) [ ! -d "$(PACKAGE_DIR)" ] || $(RM) -rf "$(PACKAGE_DIR)"
|
||||
$(V1) $(MKDIR) -p "$(PACKAGE_DIR)/firmware"
|
||||
$(foreach fw_targ, $(PACKAGE_FW_TARGETS), $(call COPY_FW_FILES,$(fw_targ),.opfw,.opfw))
|
||||
$(foreach fw_targ, $(PACKAGE_ELF_TARGETS), $(call COPY_FW_FILES,$(fw_targ),.elf,.elf))
|
||||
$(V1) $(MKDIR) -p "$(PACKAGE_DIR)"
|
||||
$(MAKE) --no-print-directory -C $(ROOT_DIR)/package --file=$(UNAME).mk $@
|
||||
|
||||
##############################
|
||||
@ -896,6 +887,66 @@ build-info:
|
||||
--template="make/templates/$@.txt" \
|
||||
--outfile="$(BUILD_DIR)/$@.txt"
|
||||
|
||||
##############################
|
||||
#
|
||||
# Source for distribution
|
||||
#
|
||||
##############################
|
||||
|
||||
.PHONY: dist
|
||||
dist:
|
||||
@$(ECHO) " SOURCE FOR DISTRIBUTION $(call toprel, $(DIST_DIR))"
|
||||
$(V1) $(MKDIR) -p "$(DIST_DIR)"
|
||||
$(V1) $(VERSION_INFO) \
|
||||
--jsonpath="$(DIST_DIR)"
|
||||
$(eval DIST_NAME := $(call toprel, "$(DIST_DIR)/OpenPilot-$(shell git describe).tar"))
|
||||
$(V1) git archive --prefix="OpenPilot/" -o "$(DIST_NAME)" HEAD
|
||||
$(V1) tar --append --file="$(DIST_NAME)" \
|
||||
--transform='s,.*version-info.json,OpenPilot/version-info.json,' \
|
||||
$(call toprel, "$(DIST_DIR)/version-info.json")
|
||||
$(V1) gzip -f "$(DIST_NAME)"
|
||||
|
||||
|
||||
|
||||
##############################
|
||||
#
|
||||
# Install OpenPilot
|
||||
#
|
||||
##############################
|
||||
prefix := /usr/local
|
||||
bindir := $(prefix)/bin
|
||||
libdir := $(prefix)/lib
|
||||
datadir := $(prefix)/share
|
||||
|
||||
INSTALL = cp -a --no-preserve=ownership
|
||||
LN = ln
|
||||
LN_S = ln -s
|
||||
|
||||
ifeq ($(MAKECMDGOALS), install)
|
||||
ifneq ($(UNAME), Linux)
|
||||
$(error install only supported for Linux)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
@$(ECHO) " INSTALLING GCS TO $(DESTDIR)/)"
|
||||
$(V1) $(MKDIR) -p $(DESTDIR)$(bindir)
|
||||
$(V1) $(MKDIR) -p $(DESTDIR)$(libdir)
|
||||
$(V1) $(MKDIR) -p $(DESTDIR)$(datadir)
|
||||
$(V1) $(MKDIR) -p $(DESTDIR)$(datadir)/applications
|
||||
$(V1) $(MKDIR) -p $(DESTDIR)$(datadir)/pixmaps
|
||||
$(V1) $(MKDIR) -p $(DESTDIR)$(udevdir)
|
||||
$(V1) $(INSTALL) $(BUILD_DIR)/openpilotgcs_$(GCS_BUILD_CONF)/bin/openpilotgcs $(DESTDIR)$(bindir)
|
||||
$(V1) $(INSTALL) $(BUILD_DIR)/openpilotgcs_$(GCS_BUILD_CONF)/bin/udp_test $(DESTDIR)$(bindir)
|
||||
$(V1) $(INSTALL) $(BUILD_DIR)/openpilotgcs_$(GCS_BUILD_CONF)/lib/openpilotgcs $(DESTDIR)$(libdir)
|
||||
$(V1) $(INSTALL) $(BUILD_DIR)/openpilotgcs_$(GCS_BUILD_CONF)/share/openpilotgcs $(DESTDIR)$(datadir)
|
||||
$(V1) $(INSTALL) $(ROOT_DIR)/package/linux/openpilot.desktop $(DESTDIR)$(datadir)/applications
|
||||
$(V1) $(INSTALL) $(ROOT_DIR)/package/linux/openpilot.png $(DESTDIR)$(datadir)/pixmaps
|
||||
$(V1) rm $(DESTDIR)/$(datadir)/openpilotgcs/translations/Makefile
|
||||
|
||||
|
||||
##############################
|
||||
#
|
||||
# Help message, the default Makefile goal
|
||||
@ -1031,6 +1082,8 @@ help:
|
||||
@$(ECHO) " clean_package - Clean, build and package the OpenPilot platform-dependent package"
|
||||
@$(ECHO) " package - Build and package the OpenPilot platform-dependent package (no clean)"
|
||||
@$(ECHO) " opfw_resource - Generate resources to embed firmware binaries into the GCS"
|
||||
@$(ECHO) " dist - Generate source archive for distribution"
|
||||
@$(ECHO) " install - Install GCS to \"DESTDIR\" with prefix \"prefix\" (Linux only)"
|
||||
@$(ECHO)
|
||||
@$(ECHO) " [Code Formatting]"
|
||||
@$(ECHO) " uncrustify_<source> - Reformat <source> code according to the project's standards"
|
||||
|
@ -32,7 +32,7 @@ Source code repository: http://git.openpilot.org/
|
||||
Crucible code reviews: http://reviews.openpilot.org/cru
|
||||
Project build server: http://bamboo.openpilot.org/
|
||||
Community blogs: http://forums.openpilot.org/blogs/
|
||||
Software downloads: http://wiki.openpilot.org/display/BUILDS/OpenPilot+Software+Downloads
|
||||
Software downloads: http://wiki.openpilot.org/display/WIKI/OpenPilot+Downloads
|
||||
|
||||
How to build from source?
|
||||
-------------------------
|
||||
|
65
WHATSNEW.txt
65
WHATSNEW.txt
@ -1,3 +1,68 @@
|
||||
--- RELEASE-15.01 ---
|
||||
This release mainly focuses on a new feature, GPSAssist which is a new form of assisted control for multirotors.
|
||||
Assisted Control provides assistance functions on top of existing flight modes. GPSAssist is the
|
||||
first assisted control option available from OpenPilot. GPSAssist supports Attitude and Rattitude
|
||||
flight modes and activates braking into position hold when the pitch/roll sticks are centered. Attitude/Rattitude
|
||||
may be flown with any of the current thrust modes (manual, cruisecontrol, altvario). GPSAssist for the PositionHold
|
||||
flight mode also introduces a braking sequence when first entering this flight mode and introduces altvario as the thrust mode.
|
||||
|
||||
The full list of features, improvements and bugfixes in this release is accessible here:
|
||||
|
||||
http://progress.openpilot.org/browse/OP/fixforversion/11020
|
||||
|
||||
|
||||
** Bug
|
||||
* [OP-1501] - Input wizard sets neutral point for throttle too low
|
||||
* [OP-1579] - Add LDFLAGS to Makefile sanitized variables
|
||||
* [OP-1626] - Satellite dsm2/dsmX binding should depend on proper window timing.
|
||||
* [OP-1638] - Transmitter setup can strip servo gears.
|
||||
* [OP-1646] - QQmlExpression: Expression qrc:/welcome/qml/main.qml:125:23 depends on non-NOTIFYable properties:
|
||||
* [OP-1647] - Welcome QML type error
|
||||
* [OP-1653] - Qt 5.2.1 fixes
|
||||
* [OP-1654] - Linux libs rpath incorrect
|
||||
* [OP-1660] - RCinput manual calibration issue
|
||||
* [OP-1662] - SystemHealt more verbose
|
||||
* [OP-1681] - Fix debug build of GCS
|
||||
* [OP-1699] - Fix build issue with tagged linux release
|
||||
* [OP-1701] - Fix linux Qt path config
|
||||
|
||||
|
||||
** Improvement
|
||||
* [OP-1071] - Make map "emergency" lines less strong and dashed
|
||||
* [OP-1256] - Add Erase Settings step to Vehicle Setup Wizard
|
||||
* [OP-1496] - Qt5.3.2 for OSX
|
||||
* [OP-1599] - rework position vario modes
|
||||
* [OP-1607] - Tab scaling on Vehicle Configuration pages is affected by clicking on "Ground" vehicle.
|
||||
* [OP-1611] - Add context menu to Scopes gadget.
|
||||
* [OP-1620] - Add fixed-wing Vtail to setup wizard
|
||||
* [OP-1622] - Update Ground config tab
|
||||
* [OP-1628] - Remove need to manually reboot by unplugging USB cable
|
||||
* [OP-1629] - Add erase settings check box (default checked) to wizard firmware page
|
||||
* [OP-1652] - Rename WS2811_LED_Out pins
|
||||
* [OP-1657] - Linux bin wrapper not needed
|
||||
* [OP-1663] - Move Qt inside openpilotgcs lib dir to avoid conflict
|
||||
* [OP-1664] - Github pull request clang warnings
|
||||
* [OP-1666] - Tidy deb package
|
||||
* [OP-1667] - Remove firmware files from packages
|
||||
* [OP-1668] - Add a way to select source for the 'Curve 2' in the custom mixer tab.
|
||||
* [OP-1674] - Oplink setup : frequency display
|
||||
|
||||
** New Feature
|
||||
* [OP-1503] - Yaffs2 file system library integration to Simposix
|
||||
* [OP-1637] - AssistedControl
|
||||
* [OP-1639] - Make non-git source
|
||||
* [OP-1640] - Write make install rule for Linux
|
||||
* [OP-1643] - Enable Ground Support in Wizard
|
||||
* [OP-1651] - Add GCS command line options to log to file
|
||||
|
||||
|
||||
** Task
|
||||
* [OP-1557] - Update Qwt
|
||||
* [OP-1558] - Evaluate removal of: ground/openpilotgcs/src/plugins/pathactioneditor/pathactioneditor.qrc
|
||||
* [OP-1648] - Update PFD after flight mode changes
|
||||
* [OP-1656] - Upgrade to Qt 5.4.0
|
||||
|
||||
|
||||
--- RELEASE-14.10 ---
|
||||
This release mainly focuses on flight performance, especially aimed at mini multirotors and adds support for the OpenPilot Platinum GPS (V9).
|
||||
This version supports the CopterControl, CC3D, Atom and the Revolution Flight controllers as well as the OPLink Modems.
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#ifndef PLANS_H_
|
||||
#define PLANS_H_
|
||||
#include <stdint.h>
|
||||
#include <pios_math.h>
|
||||
|
||||
/** \page standard Plans
|
||||
@ -67,19 +68,31 @@ void plan_setup_land();
|
||||
*/
|
||||
void plan_run_land();
|
||||
|
||||
/**
|
||||
* @brief setup pathplanner/pathfollower for braking
|
||||
*/
|
||||
void plan_setup_assistedcontrol(uint8_t timeout_occurred);
|
||||
|
||||
#define PATHDESIRED_MODEPARAMETER_BRAKE_STARTVELOCITYVECTOR_NORTH 0
|
||||
#define PATHDESIRED_MODEPARAMETER_BRAKE_STARTVELOCITYVECTOR_EAST 1
|
||||
#define PATHDESIRED_MODEPARAMETER_BRAKE_STARTVELOCITYVECTOR_DOWN 2
|
||||
#define PATHDESIRED_MODEPARAMETER_BRAKE_TIMEOUT 3
|
||||
|
||||
/**
|
||||
* @brief setup pathfollower for positionvario
|
||||
*/
|
||||
void plan_setup_PositionVarioFPV();
|
||||
void plan_setup_PositionVarioLOS();
|
||||
void plan_setup_PositionVarioNSEW();
|
||||
void plan_setup_CourseLock();
|
||||
void plan_setup_PositionRoam();
|
||||
void plan_setup_HomeLeash();
|
||||
void plan_setup_AbsolutePosition();
|
||||
|
||||
/**
|
||||
* @brief run for positionvario
|
||||
*/
|
||||
void plan_run_PositionVarioFPV();
|
||||
void plan_run_PositionVarioLOS();
|
||||
void plan_run_PositionVarioNSEW();
|
||||
void plan_run_CourseLock();
|
||||
void plan_run_PositionRoam();
|
||||
void plan_run_HomeLeash();
|
||||
void plan_run_AbsolutePosition();
|
||||
|
||||
/**
|
||||
* @brief setup pathplanner/pathfollower for AutoCruise
|
||||
|
@ -53,6 +53,6 @@ typedef enum {
|
||||
|
||||
extern int32_t configuration_check();
|
||||
|
||||
FrameType_t GetCurrentFrameType();
|
||||
extern FrameType_t GetCurrentFrameType();
|
||||
|
||||
#endif /* SANITYCHECK_H */
|
||||
|
@ -47,6 +47,7 @@ static void path_circle(PathDesiredData *path, float *cur_point, struct path_sta
|
||||
void path_progress(PathDesiredData *path, float *cur_point, struct path_status *status)
|
||||
{
|
||||
switch (path->Mode) {
|
||||
case PATHDESIRED_MODE_BRAKE: // should never get here...
|
||||
case PATHDESIRED_MODE_FLYVECTOR:
|
||||
return path_vector(path, cur_point, status, true);
|
||||
|
||||
|
@ -35,8 +35,11 @@
|
||||
#include <pathdesired.h>
|
||||
#include <positionstate.h>
|
||||
#include <flightmodesettings.h>
|
||||
#include <flightstatus.h>
|
||||
#include <velocitystate.h>
|
||||
#include <manualcontrolcommand.h>
|
||||
#include <attitudestate.h>
|
||||
#include <vtolpathfollowersettings.h>
|
||||
#include <sin_lookup.h>
|
||||
|
||||
#define UPDATE_EXPECTED 0.02f
|
||||
@ -53,8 +56,11 @@ void plan_initialize()
|
||||
PositionStateInitialize();
|
||||
PathDesiredInitialize();
|
||||
FlightModeSettingsInitialize();
|
||||
FlightStatusInitialize();
|
||||
AttitudeStateInitialize();
|
||||
ManualControlCommandInitialize();
|
||||
VelocityStateInitialize();
|
||||
VtolPathFollowerSettingsInitialize();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,6 +90,7 @@ void plan_setup_positionHold()
|
||||
PathDesiredSet(&pathDesired);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief setup pathplanner/pathfollower for return to base
|
||||
*/
|
||||
@ -165,26 +172,37 @@ void plan_run_land()
|
||||
/**
|
||||
* @brief positionvario functionality
|
||||
*/
|
||||
static bool vario_hold = true;
|
||||
static bool vario_hold = true;
|
||||
static float hold_position[3];
|
||||
static float vario_control_lowpass[3];
|
||||
static float vario_course = 0.0f;
|
||||
|
||||
static void plan_setup_PositionVario()
|
||||
{
|
||||
vario_hold = true;
|
||||
vario_control_lowpass[0] = 0.0f;
|
||||
vario_control_lowpass[1] = 0.0f;
|
||||
vario_control_lowpass[2] = 0.0f;
|
||||
AttitudeStateYawGet(&vario_course);
|
||||
plan_setup_positionHold();
|
||||
}
|
||||
|
||||
void plan_setup_PositionVarioFPV()
|
||||
void plan_setup_CourseLock()
|
||||
{
|
||||
plan_setup_PositionVario();
|
||||
}
|
||||
|
||||
void plan_setup_PositionVarioLOS()
|
||||
void plan_setup_PositionRoam()
|
||||
{
|
||||
plan_setup_PositionVario();
|
||||
}
|
||||
|
||||
void plan_setup_PositionVarioNSEW()
|
||||
void plan_setup_HomeLeash()
|
||||
{
|
||||
plan_setup_PositionVario();
|
||||
}
|
||||
|
||||
void plan_setup_AbsolutePosition()
|
||||
{
|
||||
plan_setup_PositionVario();
|
||||
}
|
||||
@ -218,7 +236,7 @@ static bool normalizeDeadband(float controlVector[4])
|
||||
return moving;
|
||||
}
|
||||
|
||||
typedef enum { FPV, LOS, NSEW } vario_type;
|
||||
typedef enum { COURSE, FPV, LOS, NSEW } vario_type;
|
||||
|
||||
static void getVector(float controlVector[4], vario_type type)
|
||||
{
|
||||
@ -249,6 +267,9 @@ static void getVector(float controlVector[4], vario_type type)
|
||||
// rotate north and east - rotation angle based on type
|
||||
float angle;
|
||||
switch (type) {
|
||||
case COURSE:
|
||||
angle = vario_course;
|
||||
break;
|
||||
case NSEW:
|
||||
angle = 0.0f;
|
||||
// NSEW no rotation takes place
|
||||
@ -283,6 +304,7 @@ static void getVector(float controlVector[4], vario_type type)
|
||||
static void plan_run_PositionVario(vario_type type)
|
||||
{
|
||||
float controlVector[4];
|
||||
float alpha;
|
||||
PathDesiredData pathDesired;
|
||||
|
||||
PathDesiredGet(&pathDesired);
|
||||
@ -295,6 +317,15 @@ static void plan_run_PositionVario(vario_type type)
|
||||
ManualControlCommandYawGet(&controlVector[2]);
|
||||
ManualControlCommandThrustGet(&controlVector[3]);
|
||||
|
||||
|
||||
FlightModeSettingsVarioControlLowPassAlphaGet(&alpha);
|
||||
vario_control_lowpass[0] = alpha * vario_control_lowpass[0] + (1.0f - alpha) * controlVector[0];
|
||||
vario_control_lowpass[1] = alpha * vario_control_lowpass[1] + (1.0f - alpha) * controlVector[1];
|
||||
vario_control_lowpass[2] = alpha * vario_control_lowpass[2] + (1.0f - alpha) * controlVector[2];
|
||||
controlVector[0] = vario_control_lowpass[0];
|
||||
controlVector[1] = vario_control_lowpass[1];
|
||||
controlVector[2] = vario_control_lowpass[2];
|
||||
|
||||
// check if movement is desired
|
||||
if (normalizeDeadband(controlVector) == false) {
|
||||
// no movement desired, re-enter positionHold at current start-position
|
||||
@ -349,17 +380,23 @@ static void plan_run_PositionVario(vario_type type)
|
||||
PathDesiredSet(&pathDesired);
|
||||
}
|
||||
}
|
||||
void plan_run_PositionVarioFPV()
|
||||
|
||||
void plan_run_CourseLock()
|
||||
{
|
||||
plan_run_PositionVario(COURSE);
|
||||
}
|
||||
|
||||
void plan_run_PositionRoam()
|
||||
{
|
||||
plan_run_PositionVario(FPV);
|
||||
}
|
||||
|
||||
void plan_run_PositionVarioLOS()
|
||||
void plan_run_HomeLeash()
|
||||
{
|
||||
plan_run_PositionVario(LOS);
|
||||
}
|
||||
|
||||
void plan_run_PositionVarioNSEW()
|
||||
void plan_run_AbsolutePosition()
|
||||
{
|
||||
plan_run_PositionVario(NSEW);
|
||||
}
|
||||
@ -474,3 +511,89 @@ void plan_run_AutoCruise()
|
||||
pathDesired.Start.Down = pathDesired.End.Down;
|
||||
PathDesiredSet(&pathDesired);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief setup pathplanner/pathfollower for braking in positionroam
|
||||
* timeout_occurred = false: Attempt to enter flyvector for braking
|
||||
* timeout_occurred = true: Revert to position hold
|
||||
*/
|
||||
#define ASSISTEDCONTROL_BRAKERATE_MINIMUM 0.2f // m/s2
|
||||
#define ASSISTEDCONTROL_TIMETOSTOP_MINIMUM 0.2f // seconds
|
||||
#define ASSISTEDCONTROL_TIMETOSTOP_MAXIMUM 9.0f // seconds
|
||||
#define ASSISTEDCONTROL_DELAY_TO_BRAKE 1.0f // seconds
|
||||
#define ASSISTEDCONTROL_TIMEOUT_MULTIPLIER 2.0f // actual deceleration rate can be 50% of desired...timeouts need to cater for this
|
||||
void plan_setup_assistedcontrol(uint8_t timeout_occurred)
|
||||
{
|
||||
PositionStateData positionState;
|
||||
|
||||
PositionStateGet(&positionState);
|
||||
PathDesiredData pathDesired;
|
||||
PathDesiredGet(&pathDesired);
|
||||
|
||||
FlightStatusAssistedControlStateOptions assistedControlFlightMode;
|
||||
FlightStatusAssistedControlStateGet(&assistedControlFlightMode);
|
||||
|
||||
if (timeout_occurred) {
|
||||
pathDesired.End.North = positionState.North;
|
||||
pathDesired.End.East = positionState.East;
|
||||
pathDesired.End.Down = positionState.Down;
|
||||
pathDesired.Start.North = positionState.North;
|
||||
pathDesired.Start.East = positionState.East;
|
||||
pathDesired.Start.Down = positionState.Down;
|
||||
pathDesired.StartingVelocity = 0.0f;
|
||||
pathDesired.EndingVelocity = 0.0f;
|
||||
pathDesired.Mode = PATHDESIRED_MODE_FLYENDPOINT;
|
||||
assistedControlFlightMode = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_HOLD;
|
||||
} else {
|
||||
VelocityStateData velocityState;
|
||||
VelocityStateGet(&velocityState);
|
||||
float brakeRate;
|
||||
VtolPathFollowerSettingsBrakeRateGet(&brakeRate);
|
||||
if (brakeRate < ASSISTEDCONTROL_BRAKERATE_MINIMUM) {
|
||||
brakeRate = ASSISTEDCONTROL_BRAKERATE_MINIMUM; // set a minimum to avoid a divide by zero potential below
|
||||
}
|
||||
// Calculate the velocity
|
||||
float velocity = velocityState.North * velocityState.North + velocityState.East * velocityState.East + velocityState.Down * velocityState.Down;
|
||||
velocity = sqrtf(velocity);
|
||||
|
||||
// Calculate the desired time to zero velocity.
|
||||
float time_to_stopped = ASSISTEDCONTROL_DELAY_TO_BRAKE; // we allow at least 0.5 seconds to rotate to a brake angle.
|
||||
time_to_stopped += velocity / brakeRate;
|
||||
|
||||
// Sanity check the brake rate by ensuring that the time to stop is within a range.
|
||||
if (time_to_stopped < ASSISTEDCONTROL_TIMETOSTOP_MINIMUM) {
|
||||
time_to_stopped = ASSISTEDCONTROL_TIMETOSTOP_MINIMUM;
|
||||
} else if (time_to_stopped > ASSISTEDCONTROL_TIMETOSTOP_MAXIMUM) {
|
||||
time_to_stopped = ASSISTEDCONTROL_TIMETOSTOP_MAXIMUM;
|
||||
}
|
||||
|
||||
// calculate the distance we will travel
|
||||
float north_delta = velocityState.North * ASSISTEDCONTROL_DELAY_TO_BRAKE; // we allow at least 0.5s to rotate to brake angle
|
||||
north_delta += (time_to_stopped - ASSISTEDCONTROL_DELAY_TO_BRAKE) * 0.5f * velocityState.North; // area under the linear deceleration plot
|
||||
float east_delta = velocityState.East * ASSISTEDCONTROL_DELAY_TO_BRAKE; // we allow at least 0.5s to rotate to brake angle
|
||||
east_delta += (time_to_stopped - ASSISTEDCONTROL_DELAY_TO_BRAKE) * 0.5f * velocityState.East; // area under the linear deceleration plot
|
||||
float down_delta = velocityState.Down * ASSISTEDCONTROL_DELAY_TO_BRAKE;
|
||||
down_delta += (time_to_stopped - ASSISTEDCONTROL_DELAY_TO_BRAKE) * 0.5f * velocityState.Down; // area under the linear deceleration plot
|
||||
float net_delta = east_delta * east_delta + north_delta * north_delta + down_delta * down_delta;
|
||||
net_delta = sqrtf(net_delta);
|
||||
|
||||
pathDesired.Start.North = positionState.North;
|
||||
pathDesired.Start.East = positionState.East;
|
||||
pathDesired.Start.Down = positionState.Down;
|
||||
pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_STARTVELOCITYVECTOR_NORTH] = velocityState.North;
|
||||
pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_STARTVELOCITYVECTOR_EAST] = velocityState.East;
|
||||
pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_STARTVELOCITYVECTOR_DOWN] = velocityState.Down;
|
||||
|
||||
pathDesired.End.North = positionState.North + north_delta;
|
||||
pathDesired.End.East = positionState.East + east_delta;
|
||||
pathDesired.End.Down = positionState.Down + down_delta;
|
||||
|
||||
pathDesired.StartingVelocity = velocity;
|
||||
pathDesired.EndingVelocity = 0.0f;
|
||||
pathDesired.Mode = PATHDESIRED_MODE_BRAKE;
|
||||
pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_TIMEOUT] = time_to_stopped * ASSISTEDCONTROL_TIMEOUT_MULTIPLIER;
|
||||
assistedControlFlightMode = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_BRAKE;
|
||||
}
|
||||
FlightStatusAssistedControlStateSet(&assistedControlFlightMode);
|
||||
PathDesiredSet(&pathDesired);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <manualcontrolsettings.h>
|
||||
#include <flightmodesettings.h>
|
||||
#include <systemsettings.h>
|
||||
#include <stabilizationsettings.h>
|
||||
#include <systemalarms.h>
|
||||
#include <revosettings.h>
|
||||
#include <positionstate.h>
|
||||
@ -45,14 +46,8 @@
|
||||
#define ADDSEVERITY(check) severity = (severity != SYSTEMALARMS_ALARM_OK ? severity : ((check) ? SYSTEMALARMS_ALARM_OK : SYSTEMALARMS_ALARM_CRITICAL))
|
||||
|
||||
|
||||
/****************************
|
||||
* Current checks:
|
||||
* 1. If a flight mode switch allows autotune and autotune module not running
|
||||
* 2. If airframe is a multirotor and either manual is available or a stabilization mode uses "none"
|
||||
****************************/
|
||||
|
||||
// ! Check a stabilization mode switch position for safety
|
||||
static bool check_stabilization_settings(int index, bool multirotor, bool coptercontrol);
|
||||
static bool check_stabilization_settings(int index, bool multirotor, bool coptercontrol, bool gpsassisted);
|
||||
|
||||
/**
|
||||
* Run a preflight check over the hardware configuration
|
||||
@ -101,34 +96,41 @@ int32_t configuration_check()
|
||||
// modes
|
||||
uint8_t num_modes;
|
||||
uint8_t modes[FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_NUMELEM];
|
||||
uint8_t FlightModeAssistMap[STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NUMELEM];
|
||||
ManualControlSettingsFlightModeNumberGet(&num_modes);
|
||||
StabilizationSettingsFlightModeAssistMapGet(FlightModeAssistMap);
|
||||
FlightModeSettingsFlightModePositionGet(modes);
|
||||
|
||||
for (uint32_t i = 0; i < num_modes; i++) {
|
||||
uint8_t gps_assisted = FlightModeAssistMap[i];
|
||||
if (gps_assisted) {
|
||||
ADDSEVERITY(!coptercontrol);
|
||||
ADDSEVERITY(multirotor);
|
||||
ADDSEVERITY(navCapableFusion);
|
||||
}
|
||||
|
||||
switch (modes[i]) {
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_MANUAL:
|
||||
ADDSEVERITY(!gps_assisted);
|
||||
ADDSEVERITY(!multirotor);
|
||||
break;
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED1:
|
||||
ADDSEVERITY(check_stabilization_settings(1, multirotor, coptercontrol));
|
||||
ADDSEVERITY(check_stabilization_settings(1, multirotor, coptercontrol, gps_assisted));
|
||||
break;
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED2:
|
||||
ADDSEVERITY(check_stabilization_settings(2, multirotor, coptercontrol));
|
||||
ADDSEVERITY(check_stabilization_settings(2, multirotor, coptercontrol, gps_assisted));
|
||||
break;
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED3:
|
||||
ADDSEVERITY(check_stabilization_settings(3, multirotor, coptercontrol));
|
||||
ADDSEVERITY(check_stabilization_settings(3, multirotor, coptercontrol, gps_assisted));
|
||||
break;
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED4:
|
||||
ADDSEVERITY(check_stabilization_settings(4, multirotor, coptercontrol));
|
||||
ADDSEVERITY(check_stabilization_settings(4, multirotor, coptercontrol, gps_assisted));
|
||||
break;
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED5:
|
||||
ADDSEVERITY(check_stabilization_settings(5, multirotor, coptercontrol));
|
||||
ADDSEVERITY(check_stabilization_settings(5, multirotor, coptercontrol, gps_assisted));
|
||||
break;
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED6:
|
||||
ADDSEVERITY(check_stabilization_settings(6, multirotor, coptercontrol));
|
||||
break;
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_AUTOTUNE:
|
||||
ADDSEVERITY(PIOS_TASK_MONITOR_IsRunning(TASKINFO_RUNNING_AUTOTUNE));
|
||||
ADDSEVERITY(check_stabilization_settings(6, multirotor, coptercontrol, gps_assisted));
|
||||
break;
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_PATHPLANNER:
|
||||
{
|
||||
@ -138,16 +140,22 @@ int32_t configuration_check()
|
||||
SystemAlarmsAlarmData alarms;
|
||||
SystemAlarmsAlarmGet(&alarms);
|
||||
ADDSEVERITY(alarms.PathPlan == SYSTEMALARMS_ALARM_OK);
|
||||
ADDSEVERITY(!gps_assisted);
|
||||
}
|
||||
// intentionally no break as this also needs pathfollower
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONHOLD:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONVARIOFPV:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONVARIOLOS:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONVARIONSEW:
|
||||
ADDSEVERITY(!coptercontrol);
|
||||
ADDSEVERITY(navCapableFusion);
|
||||
break;
|
||||
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_COURSELOCK:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONROAM:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_HOMELEASH:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_ABSOLUTEPOSITION:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_LAND:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POI:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_RETURNTOBASE:
|
||||
case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_AUTOCRUISE:
|
||||
ADDSEVERITY(!gps_assisted);
|
||||
ADDSEVERITY(!coptercontrol);
|
||||
ADDSEVERITY(navCapableFusion);
|
||||
break;
|
||||
@ -183,7 +191,7 @@ int32_t configuration_check()
|
||||
* @param[in] index Which stabilization mode to check
|
||||
* @returns true or false
|
||||
*/
|
||||
static bool check_stabilization_settings(int index, bool multirotor, bool coptercontrol)
|
||||
static bool check_stabilization_settings(int index, bool multirotor, bool coptercontrol, bool gpsassisted)
|
||||
{
|
||||
uint8_t modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_NUMELEM];
|
||||
|
||||
@ -221,6 +229,17 @@ static bool check_stabilization_settings(int index, bool multirotor, bool copter
|
||||
}
|
||||
}
|
||||
|
||||
if (gpsassisted) {
|
||||
// For multirotors verify that roll/pitch are either attitude or rattitude
|
||||
for (uint32_t i = 0; i < FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_YAW; i++) {
|
||||
if (!(modes[i] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ATTITUDE ||
|
||||
modes[i] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_RATTITUDE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// coptercontrol cannot do altitude holding
|
||||
if (coptercontrol) {
|
||||
if (modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEHOLD
|
||||
|
@ -278,9 +278,9 @@ static bool okToArm(void)
|
||||
|
||||
StabilizationDesiredStabilizationModeData stabDesired;
|
||||
|
||||
uint8_t flightMode;
|
||||
FlightStatusFlightModeGet(&flightMode);
|
||||
switch (flightMode) {
|
||||
FlightStatusData flightStatus;
|
||||
FlightStatusGet(&flightStatus);
|
||||
switch (flightStatus.FlightMode) {
|
||||
case FLIGHTSTATUS_FLIGHTMODE_MANUAL:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED2:
|
||||
@ -293,9 +293,16 @@ static bool okToArm(void)
|
||||
if (stabDesired.Thrust == STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEHOLD ||
|
||||
stabDesired.Thrust == STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
// avoid assisted control with auto throttle. As it sits waiting to launch,
|
||||
// it will move to hold, and auto thrust will auto launch otherwise!
|
||||
if (flightStatus.FlightModeAssist == FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -116,7 +116,6 @@ void takeOffLocationHandlerInit();
|
||||
ENTRY(STABILIZED4) \
|
||||
ENTRY(STABILIZED5) \
|
||||
ENTRY(STABILIZED6) \
|
||||
ENTRY(AUTOTUNE) \
|
||||
ENTRY(POSITIONHOLD) \
|
||||
ENTRY(RETURNTOBASE) \
|
||||
ENTRY(LAND) \
|
||||
|
@ -37,22 +37,30 @@
|
||||
#include <sanitycheck.h>
|
||||
#include <manualcontrolsettings.h>
|
||||
#include <manualcontrolcommand.h>
|
||||
#include <vtolselftuningstats.h>
|
||||
#include <flightmodesettings.h>
|
||||
#include <flightstatus.h>
|
||||
#include <systemsettings.h>
|
||||
#include <stabilizationdesired.h>
|
||||
#include <callbackinfo.h>
|
||||
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
#include <stabilizationsettings.h>
|
||||
#include <vtolpathfollowersettings.h>
|
||||
#endif
|
||||
|
||||
// Private constants
|
||||
#if defined(PIOS_MANUAL_STACK_SIZE)
|
||||
#define STACK_SIZE_BYTES PIOS_MANUAL_STACK_SIZE
|
||||
#define STACK_SIZE_BYTES PIOS_MANUAL_STACK_SIZE
|
||||
#else
|
||||
#define STACK_SIZE_BYTES 1152
|
||||
#define STACK_SIZE_BYTES 1152
|
||||
#endif
|
||||
|
||||
#define CALLBACK_PRIORITY CALLBACK_PRIORITY_REGULAR
|
||||
#define CBTASK_PRIORITY CALLBACK_TASK_FLIGHTCONTROL
|
||||
#define CALLBACK_PRIORITY CALLBACK_PRIORITY_REGULAR
|
||||
#define CBTASK_PRIORITY CALLBACK_TASK_FLIGHTCONTROL
|
||||
|
||||
#define ASSISTEDCONTROL_NEUTRALTHROTTLERANGE_FACTOR 0.2f
|
||||
#define ASSISTEDCONTROL_BRAKETHRUST_DEADBAND_FACTOR_LO 0.92f
|
||||
#define ASSISTEDCONTROL_BRAKETHRUST_DEADBAND_FACTOR_HI 1.08f
|
||||
|
||||
|
||||
// defined handlers
|
||||
@ -75,15 +83,6 @@ static const controlHandler handler_STABILIZED = {
|
||||
};
|
||||
|
||||
|
||||
static const controlHandler handler_AUTOTUNE = {
|
||||
.controlChain = {
|
||||
.Stabilization = false,
|
||||
.PathFollower = false,
|
||||
.PathPlanner = false,
|
||||
},
|
||||
.handler = NULL,
|
||||
};
|
||||
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
static const controlHandler handler_PATHFOLLOWER = {
|
||||
.controlChain = {
|
||||
@ -102,6 +101,9 @@ static const controlHandler handler_PATHPLANNER = {
|
||||
},
|
||||
.handler = &pathPlannerHandler,
|
||||
};
|
||||
static float thrustAtBrakeStart = 0.0f;
|
||||
static float thrustLo = 0.0f;
|
||||
static float thrustHi = 0.0f;
|
||||
|
||||
#endif /* ifndef PIOS_EXCLUDE_ADVANCED_FEATURES */
|
||||
// Private variables
|
||||
@ -110,8 +112,10 @@ static DelayedCallbackInfo *callbackHandle;
|
||||
// Private functions
|
||||
static void configurationUpdatedCb(UAVObjEvent *ev);
|
||||
static void commandUpdatedCb(UAVObjEvent *ev);
|
||||
|
||||
static void manualControlTask(void);
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
static uint8_t isAssistedFlightMode(uint8_t position, uint8_t flightMode, FlightModeSettingsData *modeSettings);
|
||||
#endif
|
||||
|
||||
#define assumptions (assumptions1 && assumptions2 && assumptions3 && assumptions4 && assumptions5 && assumptions6 && assumptions7 && assumptions_flightmode)
|
||||
|
||||
@ -156,7 +160,11 @@ int32_t ManualControlInitialize()
|
||||
ManualControlSettingsInitialize();
|
||||
FlightModeSettingsInitialize();
|
||||
SystemSettingsInitialize();
|
||||
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
VtolSelfTuningStatsInitialize();
|
||||
StabilizationSettingsInitialize();
|
||||
VtolPathFollowerSettingsInitialize();
|
||||
#endif
|
||||
callbackHandle = PIOS_CALLBACKSCHEDULER_Create(&manualControlTask, CALLBACK_PRIORITY, CBTASK_PRIORITY, CALLBACKINFO_RUNNING_MANUALCONTROL, STACK_SIZE_BYTES);
|
||||
|
||||
return 0;
|
||||
@ -179,16 +187,36 @@ static void manualControlTask(void)
|
||||
FlightStatusGet(&flightStatus);
|
||||
ManualControlCommandData cmd;
|
||||
ManualControlCommandGet(&cmd);
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
VtolPathFollowerSettingsThrustLimitsData thrustLimits;
|
||||
VtolPathFollowerSettingsThrustLimitsGet(&thrustLimits);
|
||||
#endif
|
||||
|
||||
FlightModeSettingsData modeSettings;
|
||||
FlightModeSettingsGet(&modeSettings);
|
||||
|
||||
uint8_t position = cmd.FlightModeSwitchPosition;
|
||||
uint8_t newMode = flightStatus.FlightMode;
|
||||
uint8_t newFlightModeAssist = flightStatus.FlightModeAssist;
|
||||
uint8_t newAssistedControlState = flightStatus.AssistedControlState;
|
||||
uint8_t newAssistedThrottleState = flightStatus.AssistedThrottleState;
|
||||
if (position < FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_NUMELEM) {
|
||||
newMode = modeSettings.FlightModePosition[position];
|
||||
}
|
||||
|
||||
// if a mode change occurs we default the assist mode and states here
|
||||
// to avoid having to add it to all of the below modes that are
|
||||
// otherwise unrelated
|
||||
if (newMode != flightStatus.FlightMode) {
|
||||
// set assist mode to none to avoid an assisted flight mode position
|
||||
// carrying over and impacting a newly selected non-assisted flight mode pos
|
||||
newFlightModeAssist = FLIGHTSTATUS_FLIGHTMODEASSIST_NONE;
|
||||
// The following are eqivalent to none effectively. Code should always
|
||||
// check the flightmodeassist state.
|
||||
newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_PRIMARY;
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL;
|
||||
}
|
||||
|
||||
// Depending on the mode update the Stabilization or Actuator objects
|
||||
const controlHandler *handler = &handler_MANUAL;
|
||||
switch (newMode) {
|
||||
@ -202,12 +230,177 @@ static void manualControlTask(void)
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED5:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED6:
|
||||
handler = &handler_STABILIZED;
|
||||
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
newFlightModeAssist = isAssistedFlightMode(position, newMode, &modeSettings);
|
||||
if (newFlightModeAssist) {
|
||||
// assess roll/pitch state
|
||||
bool flagRollPitchHasInput = (fabsf(cmd.Roll) > 0.0f || fabsf(cmd.Pitch) > 0.0f);
|
||||
|
||||
// assess throttle state
|
||||
bool throttleNeutral = false;
|
||||
bool throttleNeutralOrLow = false;
|
||||
float neutralThrustOffset = 0.0f;
|
||||
VtolSelfTuningStatsNeutralThrustOffsetGet(&neutralThrustOffset);
|
||||
|
||||
|
||||
float throttleRangeDelta = (thrustLimits.Neutral + neutralThrustOffset) * ASSISTEDCONTROL_NEUTRALTHROTTLERANGE_FACTOR;
|
||||
float throttleNeutralLow = (thrustLimits.Neutral + neutralThrustOffset) - throttleRangeDelta;
|
||||
float throttleNeutralHi = (thrustLimits.Neutral + neutralThrustOffset) + throttleRangeDelta;
|
||||
if (cmd.Thrust > throttleNeutralLow && cmd.Thrust < throttleNeutralHi) {
|
||||
throttleNeutral = true;
|
||||
}
|
||||
if (cmd.Thrust < throttleNeutralHi) {
|
||||
throttleNeutralOrLow = true;
|
||||
}
|
||||
|
||||
// determine default thrust mode for hold/brake states
|
||||
uint8_t pathfollowerthrustmode = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL;
|
||||
if (newFlightModeAssist == FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST) {
|
||||
pathfollowerthrustmode = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_AUTO;
|
||||
}
|
||||
|
||||
switch (newAssistedControlState) {
|
||||
case FLIGHTSTATUS_ASSISTEDCONTROLSTATE_PRIMARY:
|
||||
if (!flagRollPitchHasInput) {
|
||||
// no stick input on roll/pitch so enter brake state
|
||||
newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_BRAKE;
|
||||
newAssistedThrottleState = pathfollowerthrustmode;
|
||||
handler = &handler_PATHFOLLOWER;
|
||||
// retain thrust cmd for later comparison with actual in braking
|
||||
thrustAtBrakeStart = cmd.Thrust;
|
||||
|
||||
// calculate hi and low value of +-8% as a mini-deadband
|
||||
// for use in auto-override in brake sequence
|
||||
thrustLo = ASSISTEDCONTROL_BRAKETHRUST_DEADBAND_FACTOR_LO * thrustAtBrakeStart;
|
||||
thrustHi = ASSISTEDCONTROL_BRAKETHRUST_DEADBAND_FACTOR_HI * thrustAtBrakeStart;
|
||||
|
||||
// The purpose for auto throttle assist is to go from a mid to high thrust range to a
|
||||
// neutral vertical-holding/maintaining ~50% thrust range. It is not designed/intended
|
||||
// to go from near zero to 50%...we don't want an auto-takeoff feature here!
|
||||
// Also for rapid decents a user might have a bit of forward stick and low throttle
|
||||
// then stick-off for auto-braking...but if below the vtol min of 20% it will not
|
||||
// kick in...the flyer needs to manually manage throttle to slow down decent,
|
||||
// and the next time they put in a bit of stick, revert to primary, and then
|
||||
// sticks-off it will brake and hold with auto-thrust
|
||||
if (thrustAtBrakeStart < thrustLimits.Min) {
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL; // Effectively None
|
||||
}
|
||||
} else {
|
||||
// stick input so stay in primary mode control state
|
||||
// newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_PRIMARY;
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL; // Effectively None
|
||||
}
|
||||
break;
|
||||
|
||||
case FLIGHTSTATUS_ASSISTEDCONTROLSTATE_BRAKE:
|
||||
if (flagRollPitchHasInput) {
|
||||
// stick input during brake sequence allows immediate resumption of control
|
||||
newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_PRIMARY;
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL; // Effectively None
|
||||
} else {
|
||||
// no stick input, stay in brake mode
|
||||
newAssistedThrottleState = pathfollowerthrustmode;
|
||||
handler = &handler_PATHFOLLOWER;
|
||||
|
||||
// if auto thrust and user adjusts thrust outside of a deadband in which case revert to manual
|
||||
if ((newAssistedThrottleState == FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_AUTO) &&
|
||||
(cmd.Thrust < thrustLo || cmd.Thrust > thrustHi)) {
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FLIGHTSTATUS_ASSISTEDCONTROLSTATE_HOLD:
|
||||
|
||||
if (newFlightModeAssist == FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST_PRIMARYTHRUST ||
|
||||
(newFlightModeAssist == FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST &&
|
||||
newAssistedThrottleState == FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL)) {
|
||||
// for manual or primary throttle states/modes, stick control immediately reverts to primary mode control
|
||||
if (flagRollPitchHasInput) {
|
||||
newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_PRIMARY;
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL; // Effectively None
|
||||
} else {
|
||||
// otherwise stay in the hold state
|
||||
// newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_HOLD;
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL;
|
||||
handler = &handler_PATHFOLLOWER;
|
||||
}
|
||||
} else if (newFlightModeAssist == FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST &&
|
||||
newAssistedThrottleState != FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL) {
|
||||
// ok auto thrust mode applies in hold unless overridden
|
||||
|
||||
if (throttleNeutralOrLow && flagRollPitchHasInput) {
|
||||
// throttle is neutral approximately and stick input present so revert to primary mode control
|
||||
newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_PRIMARY;
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL; // Effectively None
|
||||
} else {
|
||||
// otherwise hold, autothrust, no stick input...we watch throttle for need to change mode
|
||||
switch (newAssistedThrottleState) {
|
||||
case FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_AUTO:
|
||||
// whilst in auto, watch for neutral range, from which we allow override
|
||||
if (throttleNeutral) {
|
||||
pathfollowerthrustmode = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_AUTOOVERRIDE;
|
||||
} else { pathfollowerthrustmode = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_AUTO; }
|
||||
break;
|
||||
case FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_AUTOOVERRIDE:
|
||||
// previously user has set throttle to neutral range, apply a deadband and revert to manual
|
||||
// if moving out of deadband. This allows for landing in hold state.
|
||||
if (!throttleNeutral) {
|
||||
pathfollowerthrustmode = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL;
|
||||
} else { pathfollowerthrustmode = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_AUTOOVERRIDE; }
|
||||
break;
|
||||
case FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL:
|
||||
pathfollowerthrustmode = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL;
|
||||
break;
|
||||
}
|
||||
|
||||
newAssistedThrottleState = pathfollowerthrustmode;
|
||||
handler = &handler_PATHFOLLOWER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* ifndef PIOS_EXCLUDE_ADVANCED_FEATURES */
|
||||
break;
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOFPV:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOLOS:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIONSEW:
|
||||
newFlightModeAssist = isAssistedFlightMode(position, newMode, &modeSettings);
|
||||
if (newFlightModeAssist) {
|
||||
switch (newFlightModeAssist) {
|
||||
case FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST_PRIMARYTHRUST:
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST:
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_AUTO;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODEASSIST_NONE:
|
||||
default:
|
||||
newAssistedThrottleState = FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL; // Effectively None
|
||||
break;
|
||||
}
|
||||
|
||||
switch (newAssistedControlState) {
|
||||
case FLIGHTSTATUS_ASSISTEDCONTROLSTATE_BRAKE:
|
||||
newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_BRAKE;
|
||||
break;
|
||||
case FLIGHTSTATUS_ASSISTEDCONTROLSTATE_PRIMARY:
|
||||
newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_BRAKE;
|
||||
break;
|
||||
case FLIGHTSTATUS_ASSISTEDCONTROLSTATE_HOLD:
|
||||
newAssistedControlState = FLIGHTSTATUS_ASSISTEDCONTROLSTATE_HOLD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
handler = &handler_PATHFOLLOWER;
|
||||
break;
|
||||
|
||||
case FLIGHTSTATUS_FLIGHTMODE_COURSELOCK:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONROAM:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_HOMELEASH:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_ABSOLUTEPOSITION:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_LAND:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POI:
|
||||
@ -217,10 +410,7 @@ static void manualControlTask(void)
|
||||
case FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER:
|
||||
handler = &handler_PATHPLANNER;
|
||||
break;
|
||||
#endif
|
||||
case FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE:
|
||||
handler = &handler_AUTOTUNE;
|
||||
break;
|
||||
#endif /* ifndef PIOS_EXCLUDE_ADVANCED_FEATURES */
|
||||
// There is no default, so if a flightmode is forgotten the compiler can throw a warning!
|
||||
}
|
||||
|
||||
@ -229,10 +419,16 @@ static void manualControlTask(void)
|
||||
// FlightMode needs to be set correctly on first run (otherwise ControlChain is invalid)
|
||||
static bool firstRun = true;
|
||||
|
||||
if (flightStatus.FlightMode != newMode || firstRun) {
|
||||
if (flightStatus.FlightMode != newMode || firstRun ||
|
||||
newFlightModeAssist != flightStatus.FlightModeAssist ||
|
||||
newAssistedControlState != flightStatus.AssistedControlState ||
|
||||
flightStatus.AssistedThrottleState != newAssistedThrottleState) {
|
||||
firstRun = false;
|
||||
flightStatus.ControlChain = handler->controlChain;
|
||||
flightStatus.FlightMode = newMode;
|
||||
flightStatus.ControlChain = handler->controlChain;
|
||||
flightStatus.FlightMode = newMode;
|
||||
flightStatus.FlightModeAssist = newFlightModeAssist;
|
||||
flightStatus.AssistedControlState = newAssistedControlState;
|
||||
flightStatus.AssistedThrottleState = newAssistedThrottleState;
|
||||
FlightStatusSet(&flightStatus);
|
||||
newinit = true;
|
||||
}
|
||||
@ -257,6 +453,81 @@ static void commandUpdatedCb(__attribute__((unused)) UAVObjEvent *ev)
|
||||
PIOS_CALLBACKSCHEDULER_Dispatch(callbackHandle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check and set modes for gps assisted stablised flight modes
|
||||
*/
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
static uint8_t isAssistedFlightMode(uint8_t position, uint8_t flightMode, FlightModeSettingsData *modeSettings)
|
||||
{
|
||||
uint8_t flightModeAssistOption = STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NONE;
|
||||
uint8_t isAssistedFlag = FLIGHTSTATUS_FLIGHTMODEASSIST_NONE;
|
||||
uint8_t FlightModeAssistMap[STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NUMELEM];
|
||||
|
||||
StabilizationSettingsFlightModeAssistMapGet(FlightModeAssistMap);
|
||||
if (position < STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NUMELEM) {
|
||||
flightModeAssistOption = FlightModeAssistMap[position];
|
||||
}
|
||||
|
||||
switch (flightModeAssistOption) {
|
||||
case STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NONE:
|
||||
break;
|
||||
case STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_GPSASSIST:
|
||||
{
|
||||
// default to cruise control.
|
||||
FlightModeSettingsStabilization1SettingsOptions thrustMode = FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_CRUISECONTROL;
|
||||
|
||||
switch (flightMode) {
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1:
|
||||
thrustMode = modeSettings->Stabilization1Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED2:
|
||||
thrustMode = modeSettings->Stabilization2Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED3:
|
||||
thrustMode = modeSettings->Stabilization3Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED4:
|
||||
thrustMode = modeSettings->Stabilization4Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED5:
|
||||
thrustMode = modeSettings->Stabilization5Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED6:
|
||||
thrustMode = modeSettings->Stabilization6Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD:
|
||||
// we hard code the "GPS Assisted" PostionHold to use alt-vario which
|
||||
// is a more appropriate throttle mode. "GPSAssist" adds braking
|
||||
// and a better throttle management to the standard Position Hold.
|
||||
thrustMode = FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEVARIO;
|
||||
break;
|
||||
|
||||
// other modes will use cruisecontrol as default
|
||||
}
|
||||
|
||||
|
||||
switch (thrustMode) {
|
||||
case FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEHOLD:
|
||||
case FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEVARIO:
|
||||
// this is only for use with stabi mods with althold/vario.
|
||||
isAssistedFlag = FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST_PRIMARYTHRUST;
|
||||
break;
|
||||
case FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_MANUAL:
|
||||
case FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_CRUISECONTROL:
|
||||
default:
|
||||
// this is the default for non stabi modes also
|
||||
isAssistedFlag = FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return isAssistedFlag;
|
||||
}
|
||||
#endif /* ifndef PIOS_EXCLUDE_ADVANCED_FEATURES */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
|
@ -56,7 +56,9 @@ void pathFollowerHandler(bool newinit)
|
||||
}
|
||||
|
||||
uint8_t flightMode;
|
||||
uint8_t assistedControlFlightMode;
|
||||
FlightStatusFlightModeGet(&flightMode);
|
||||
FlightStatusAssistedControlStateGet(&assistedControlFlightMode);
|
||||
|
||||
if (newinit) {
|
||||
// After not being in this mode for a while init at current height
|
||||
@ -64,18 +66,25 @@ void pathFollowerHandler(bool newinit)
|
||||
case FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE:
|
||||
plan_setup_returnToBase();
|
||||
break;
|
||||
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD:
|
||||
plan_setup_positionHold();
|
||||
if (assistedControlFlightMode == FLIGHTSTATUS_ASSISTEDCONTROLSTATE_BRAKE) {
|
||||
// Just initiated braking after returning from stabi control
|
||||
plan_setup_assistedcontrol(false);
|
||||
} else {
|
||||
plan_setup_positionHold();
|
||||
}
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOFPV:
|
||||
plan_setup_PositionVarioFPV();
|
||||
case FLIGHTSTATUS_FLIGHTMODE_COURSELOCK:
|
||||
plan_setup_CourseLock();
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOLOS:
|
||||
plan_setup_PositionVarioLOS();
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONROAM:
|
||||
plan_setup_PositionRoam();
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIONSEW:
|
||||
plan_setup_PositionVarioNSEW();
|
||||
case FLIGHTSTATUS_FLIGHTMODE_HOMELEASH:
|
||||
plan_setup_HomeLeash();
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_ABSOLUTEPOSITION:
|
||||
plan_setup_AbsolutePosition();
|
||||
break;
|
||||
|
||||
case FLIGHTSTATUS_FLIGHTMODE_LAND:
|
||||
@ -85,6 +94,18 @@ void pathFollowerHandler(bool newinit)
|
||||
plan_setup_AutoCruise();
|
||||
break;
|
||||
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED2:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED3:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED4:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED5:
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED6:
|
||||
if (assistedControlFlightMode == FLIGHTSTATUS_ASSISTEDCONTROLSTATE_BRAKE) {
|
||||
// Just initiated braking after returning from stabi control
|
||||
plan_setup_assistedcontrol(false);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
plan_setup_positionHold();
|
||||
break;
|
||||
@ -92,14 +113,17 @@ void pathFollowerHandler(bool newinit)
|
||||
}
|
||||
|
||||
switch (flightMode) {
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOFPV:
|
||||
plan_run_PositionVarioFPV();
|
||||
case FLIGHTSTATUS_FLIGHTMODE_COURSELOCK:
|
||||
plan_run_CourseLock();
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOLOS:
|
||||
plan_run_PositionVarioLOS();
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONROAM:
|
||||
plan_run_PositionRoam();
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIONSEW:
|
||||
plan_run_PositionVarioNSEW();
|
||||
case FLIGHTSTATUS_FLIGHTMODE_HOMELEASH:
|
||||
plan_run_HomeLeash();
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_ABSOLUTEPOSITION:
|
||||
plan_run_AbsolutePosition();
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_LAND:
|
||||
plan_run_land();
|
||||
|
@ -93,6 +93,7 @@ void stabilizedHandler(bool newinit)
|
||||
uint8_t *stab_settings;
|
||||
FlightStatusData flightStatus;
|
||||
FlightStatusGet(&flightStatus);
|
||||
|
||||
switch (flightStatus.FlightMode) {
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1:
|
||||
stab_settings = FlightModeSettingsStabilization1SettingsToArray(settings.Stabilization1Settings);
|
||||
@ -163,8 +164,8 @@ void stabilizedHandler(bool newinit)
|
||||
0; // this is an invalid mode
|
||||
}
|
||||
|
||||
stabilization.Thrust = cmd.Thrust;
|
||||
stabilization.StabilizationMode.Thrust = stab_settings[3];
|
||||
stabilization.Thrust = cmd.Thrust;
|
||||
StabilizationDesiredSet(&stabilization);
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@ typedef enum {
|
||||
NOTIFY_SEQUENCE_ARMED_FM_STABILIZED4 = 4,
|
||||
NOTIFY_SEQUENCE_ARMED_FM_STABILIZED5 = 5,
|
||||
NOTIFY_SEQUENCE_ARMED_FM_STABILIZED6 = 6,
|
||||
NOTIFY_SEQUENCE_ARMED_FM_AUTOTUNE = 7,
|
||||
NOTIFY_SEQUENCE_ARMED_FM_GPS = 8,
|
||||
NOTIFY_SEQUENCE_ARMED_FM_RTH = 9,
|
||||
NOTIFY_SEQUENCE_ARMED_FM_LAND = 10,
|
||||
@ -116,9 +115,6 @@ const LedSequence_t notifications[] = {
|
||||
{ .time_off = 100, .time_on = 100, .color = COLOR_PURPLE, .repeats = 1, },
|
||||
{ .time_off = 500, .time_on = 100, .color = COLOR_BLUE, .repeats = 1, },
|
||||
}, },
|
||||
[NOTIFY_SEQUENCE_ARMED_FM_AUTOTUNE] = { .repeats = -1, .steps = {
|
||||
{ .time_off = 800, .time_on = 200, .color = COLOR_BLUE, .repeats = 1, },
|
||||
}, },
|
||||
[NOTIFY_SEQUENCE_ARMED_FM_GPS] = { .repeats = -1, .steps = {
|
||||
{ .time_off = 800, .time_on = 200, .color = COLOR_GREEN, .repeats = 1, },
|
||||
}, },
|
||||
@ -165,22 +161,22 @@ const LedSequence_t notifications[] = {
|
||||
// List of background sequences attached to each flight mode
|
||||
const LedSequence_t *flightModeMap[] = {
|
||||
[FLIGHTSTATUS_FLIGHTMODE_MANUAL] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_MANUAL],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED1] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED1],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED2] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED2],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED3] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED3],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED4] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED4],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED5] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED5],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED6] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED6],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_AUTO],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_AUTOTUNE],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOFPV] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOLOS] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIONSEW] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_RTH],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED1] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED1],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED2] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED2],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED3] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED3],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED4] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED4],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED5] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED5],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_STABILIZED6] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_STABILIZED6],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_AUTO],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_COURSELOCK] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_POSITIONROAM] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_HOMELEASH] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_ABSOLUTEPOSITION] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_RTH],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_LAND] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_LAND],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_POI] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_AUTOCRUISE] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
[FLIGHTSTATUS_FLIGHTMODE_AUTOCRUISE] = ¬ifications[NOTIFY_SEQUENCE_ARMED_FM_GPS],
|
||||
};
|
||||
|
||||
// List of alarms to show with attached sequences for each status
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include <sin_lookup.h>
|
||||
#include <pathdesired.h>
|
||||
#include <paths.h>
|
||||
#include "plans.h"
|
||||
#include <sanitycheck.h>
|
||||
|
||||
|
||||
@ -61,6 +62,7 @@
|
||||
#include <fixedwingpathfollowerstatus.h>
|
||||
#include <vtolpathfollowersettings.h>
|
||||
#include <flightstatus.h>
|
||||
#include <flightmodesettings.h>
|
||||
#include <pathstatus.h>
|
||||
#include <positionstate.h>
|
||||
#include <velocitystate.h>
|
||||
@ -73,25 +75,42 @@
|
||||
#include <manualcontrolcommand.h>
|
||||
#include <systemsettings.h>
|
||||
#include <stabilizationbank.h>
|
||||
#include <vtolselftuningstats.h>
|
||||
#include <pathsummary.h>
|
||||
|
||||
|
||||
// Private constants
|
||||
|
||||
#define CALLBACK_PRIORITY CALLBACK_PRIORITY_LOW
|
||||
#define CBTASK_PRIORITY CALLBACK_TASK_FLIGHTCONTROL
|
||||
#define CALLBACK_PRIORITY CALLBACK_PRIORITY_LOW
|
||||
#define CBTASK_PRIORITY CALLBACK_TASK_FLIGHTCONTROL
|
||||
|
||||
#define PF_IDLE_UPDATE_RATE_MS 100
|
||||
#define PF_IDLE_UPDATE_RATE_MS 100
|
||||
|
||||
#define STACK_SIZE_BYTES 2048
|
||||
#define STACK_SIZE_BYTES 2048
|
||||
|
||||
#define DEADBAND_HIGH 0.10f
|
||||
#define DEADBAND_LOW -0.10f
|
||||
|
||||
#define BRAKE_FRACTIONALPROGRESS_STARTVELOCITYCHECK 0.95f
|
||||
#define BRAKE_EXIT_VELOCITY_LIMIT 0.2f
|
||||
|
||||
#define BRAKE_RATE_MINIMUM 0.2f
|
||||
|
||||
#define NEUTRALTHRUST_PH_POSITIONAL_ERROR_LIMIT 0.5f
|
||||
#define NEUTRALTHRUST_PH_VEL_DESIRED_LIMIT 0.2f
|
||||
#define NEUTRALTHRUST_PH_VEL_STATE_LIMIT 0.2f
|
||||
#define NEUTRALTHRUST_PH_VEL_ERROR_LIMIT 0.1f
|
||||
|
||||
#define NEUTRALTHRUST_START_DELAY (2 * 20) // 2 seconds at rate of 20Hz (50ms update rate)
|
||||
#define NEUTRALTHRUST_END_COUNT (NEUTRALTHRUST_START_DELAY + (4 * 20)) // 4 second sample
|
||||
|
||||
#define DEADBAND_HIGH 0.10f
|
||||
#define DEADBAND_LOW -0.10f
|
||||
// Private types
|
||||
|
||||
struct Globals {
|
||||
struct pid PIDposH[2];
|
||||
struct pid PIDposV;
|
||||
struct pid PIDvel[3];
|
||||
struct pid PIDvel[3]; // North, East, Down
|
||||
struct pid BrakePIDvel[2]; // North, East
|
||||
struct pid PIDcourse;
|
||||
struct pid PIDspeed;
|
||||
struct pid PIDpower;
|
||||
@ -100,6 +119,19 @@ struct Globals {
|
||||
bool vtolEmergencyFallbackSwitch;
|
||||
};
|
||||
|
||||
struct NeutralThrustEstimation {
|
||||
uint32_t count;
|
||||
float sum;
|
||||
float average;
|
||||
float correction;
|
||||
float algo_erro_check;
|
||||
float min;
|
||||
float max;
|
||||
bool start_sampling;
|
||||
bool have_correction;
|
||||
};
|
||||
static struct NeutralThrustEstimation neutralThrustEst;
|
||||
|
||||
|
||||
// Private variables
|
||||
static DelayedCallbackInfo *pathFollowerCBInfo;
|
||||
@ -109,6 +141,8 @@ static PathStatusData pathStatus;
|
||||
static PathDesiredData pathDesired;
|
||||
static FixedWingPathFollowerSettingsData fixedWingPathFollowerSettings;
|
||||
static VtolPathFollowerSettingsData vtolPathFollowerSettings;
|
||||
static FlightStatusData flightStatus;
|
||||
static PathSummaryData pathSummary;
|
||||
|
||||
// correct speed by measured airspeed
|
||||
static float indicatedAirspeedStateBias = 0.0f;
|
||||
@ -126,6 +160,7 @@ static float updateCourseBearing();
|
||||
static float updatePathBearing();
|
||||
static float updatePOIBearing();
|
||||
static void processPOI();
|
||||
static void updateBrakeVelocity(float startingVelocity, float dT, float brakeRate, float currentVelocity, float *updatedVelocity);
|
||||
static void updatePathVelocity(float kFF, bool limited);
|
||||
static uint8_t updateFixedDesiredAttitude();
|
||||
static int8_t updateVtolDesiredAttitude(bool yaw_attitude, float yaw_direction);
|
||||
@ -159,7 +194,9 @@ int32_t PathFollowerInitialize()
|
||||
FixedWingPathFollowerStatusInitialize();
|
||||
VtolPathFollowerSettingsInitialize();
|
||||
FlightStatusInitialize();
|
||||
FlightModeSettingsInitialize();
|
||||
PathStatusInitialize();
|
||||
PathSummaryInitialize();
|
||||
PathDesiredInitialize();
|
||||
PositionStateInitialize();
|
||||
VelocityStateInitialize();
|
||||
@ -172,6 +209,8 @@ int32_t PathFollowerInitialize()
|
||||
ManualControlCommandInitialize();
|
||||
SystemSettingsInitialize();
|
||||
StabilizationBankInitialize();
|
||||
VtolSelfTuningStatsInitialize();
|
||||
|
||||
|
||||
// reset integrals
|
||||
resetGlobals();
|
||||
@ -193,8 +232,6 @@ MODULE_INITCALL(PathFollowerInitialize, PathFollowerStart);
|
||||
*/
|
||||
static void pathFollowerTask(void)
|
||||
{
|
||||
FlightStatusData flightStatus;
|
||||
|
||||
FlightStatusGet(&flightStatus);
|
||||
|
||||
if (flightStatus.ControlChain.PathFollower != FLIGHTSTATUS_CONTROLCHAIN_TRUE) {
|
||||
@ -208,11 +245,21 @@ static void pathFollowerTask(void)
|
||||
processPOI();
|
||||
}
|
||||
|
||||
int16_t old_uid = pathStatus.UID;
|
||||
pathStatus.UID = pathDesired.UID;
|
||||
pathStatus.Status = PATHSTATUS_STATUS_INPROGRESS;
|
||||
if (pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_TIMEOUT] > 0.0f) {
|
||||
if (old_uid != pathStatus.UID) {
|
||||
pathStatus.path_time = 0.0f;
|
||||
} else {
|
||||
pathStatus.path_time += updatePeriod / 1000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
switch (pathDesired.Mode) {
|
||||
case PATHDESIRED_MODE_FLYENDPOINT:
|
||||
case PATHDESIRED_MODE_FLYVECTOR:
|
||||
case PATHDESIRED_MODE_BRAKE:
|
||||
case PATHDESIRED_MODE_FLYCIRCLERIGHT:
|
||||
case PATHDESIRED_MODE_FLYCIRCLELEFT:
|
||||
{
|
||||
@ -239,6 +286,7 @@ static void pathFollowerTask(void)
|
||||
}
|
||||
PathStatusSet(&pathStatus);
|
||||
|
||||
|
||||
PIOS_CALLBACKSCHEDULER_Schedule(pathFollowerCBInfo, updatePeriod, CALLBACK_UPDATEMODE_SOONER);
|
||||
}
|
||||
|
||||
@ -258,6 +306,9 @@ static void SettingsUpdatedCb(__attribute__((unused)) UAVObjEvent *ev)
|
||||
pid_configure(&global.PIDvel[1], vtolPathFollowerSettings.HorizontalVelPID.Kp, vtolPathFollowerSettings.HorizontalVelPID.Ki, vtolPathFollowerSettings.HorizontalVelPID.Kd, vtolPathFollowerSettings.HorizontalVelPID.ILimit);
|
||||
pid_configure(&global.PIDvel[2], vtolPathFollowerSettings.VerticalVelPID.Kp, vtolPathFollowerSettings.VerticalVelPID.Ki, vtolPathFollowerSettings.VerticalVelPID.Kd, vtolPathFollowerSettings.VerticalVelPID.ILimit);
|
||||
|
||||
pid_configure(&global.BrakePIDvel[0], vtolPathFollowerSettings.BrakeHorizontalVelPID.Kp, vtolPathFollowerSettings.BrakeHorizontalVelPID.Ki, vtolPathFollowerSettings.BrakeHorizontalVelPID.Kd, vtolPathFollowerSettings.BrakeHorizontalVelPID.ILimit);
|
||||
pid_configure(&global.BrakePIDvel[1], vtolPathFollowerSettings.BrakeHorizontalVelPID.Kp, vtolPathFollowerSettings.BrakeHorizontalVelPID.Ki, vtolPathFollowerSettings.BrakeHorizontalVelPID.Kd, vtolPathFollowerSettings.BrakeHorizontalVelPID.ILimit);
|
||||
|
||||
PathDesiredGet(&pathDesired);
|
||||
}
|
||||
|
||||
@ -297,12 +348,27 @@ static void resetGlobals()
|
||||
pid_zero(&global.PIDvel[0]);
|
||||
pid_zero(&global.PIDvel[1]);
|
||||
pid_zero(&global.PIDvel[2]);
|
||||
pid_zero(&global.BrakePIDvel[0]);
|
||||
pid_zero(&global.BrakePIDvel[1]);
|
||||
pid_zero(&global.PIDcourse);
|
||||
pid_zero(&global.PIDspeed);
|
||||
pid_zero(&global.PIDpower);
|
||||
global.poiRadius = 0.0f;
|
||||
global.vtolEmergencyFallback = 0;
|
||||
global.vtolEmergencyFallbackSwitch = false;
|
||||
|
||||
// reset neutral thrust assessment. We restart this process
|
||||
// and do once for each position hold engagement
|
||||
neutralThrustEst.start_sampling = false;
|
||||
neutralThrustEst.count = 0;
|
||||
neutralThrustEst.sum = 0.0f;
|
||||
neutralThrustEst.have_correction = false;
|
||||
neutralThrustEst.average = 0.0f;
|
||||
neutralThrustEst.correction = 0.0f;
|
||||
neutralThrustEst.min = 0.0f;
|
||||
neutralThrustEst.max = 0.0f;
|
||||
|
||||
pathStatus.path_time = 0.0f;
|
||||
}
|
||||
|
||||
static uint8_t updateAutoPilotByFrameType()
|
||||
@ -389,28 +455,37 @@ static uint8_t updateAutoPilotVtol()
|
||||
// yaw behaviour is configurable in vtolpathfollower, select yaw control algorithm
|
||||
bool yaw_attitude = true;
|
||||
float yaw = 0.0f;
|
||||
switch (vtolPathFollowerSettings.YawControl) {
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_MANUAL:
|
||||
|
||||
if (pathDesired.Mode == PATHDESIRED_MODE_BRAKE) {
|
||||
yaw_attitude = false;
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_TAILIN:
|
||||
yaw = updateTailInBearing();
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_MOVEMENTDIRECTION:
|
||||
yaw = updateCourseBearing();
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_PATHDIRECTION:
|
||||
yaw = updatePathBearing();
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_POI:
|
||||
yaw = updatePOIBearing();
|
||||
break;
|
||||
} else {
|
||||
switch (vtolPathFollowerSettings.YawControl) {
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_MANUAL:
|
||||
yaw_attitude = false;
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_TAILIN:
|
||||
yaw = updateTailInBearing();
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_MOVEMENTDIRECTION:
|
||||
yaw = updateCourseBearing();
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_PATHDIRECTION:
|
||||
yaw = updatePathBearing();
|
||||
break;
|
||||
case VTOLPATHFOLLOWERSETTINGS_YAWCONTROL_POI:
|
||||
yaw = updatePOIBearing();
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = updateVtolDesiredAttitude(yaw_attitude, yaw);
|
||||
|
||||
// switch to emergency follower if follower indicates problems
|
||||
if (!result && (vtolPathFollowerSettings.FlyawayEmergencyFallback != VTOLPATHFOLLOWERSETTINGS_FLYAWAYEMERGENCYFALLBACK_DISABLED)) {
|
||||
global.vtolEmergencyFallbackSwitch = true;
|
||||
if (!result) {
|
||||
if (pathDesired.Mode == PATHDESIRED_MODE_BRAKE) {
|
||||
plan_setup_assistedcontrol(true); // revert braking to position hold, user can always stick override
|
||||
} else if (vtolPathFollowerSettings.FlyawayEmergencyFallback != VTOLPATHFOLLOWERSETTINGS_FLYAWAYEMERGENCYFALLBACK_DISABLED) {
|
||||
// switch to emergency follower if follower indicates problems
|
||||
global.vtolEmergencyFallbackSwitch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -427,6 +502,48 @@ static uint8_t updateAutoPilotVtol()
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Brake mode end condition checks
|
||||
if (pathDesired.Mode == PATHDESIRED_MODE_BRAKE) {
|
||||
bool exit_brake = false;
|
||||
VelocityStateData velocityState;
|
||||
if (pathStatus.path_time > pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_TIMEOUT]) { // enter hold on timeout
|
||||
pathSummary.brake_exit_reason = PATHSUMMARY_BRAKE_EXIT_REASON_TIMEOUT;
|
||||
exit_brake = true;
|
||||
} else if (pathStatus.fractional_progress > BRAKE_FRACTIONALPROGRESS_STARTVELOCITYCHECK) {
|
||||
VelocityStateGet(&velocityState);
|
||||
if (fabsf(velocityState.East) < BRAKE_EXIT_VELOCITY_LIMIT && fabsf(velocityState.North) < BRAKE_EXIT_VELOCITY_LIMIT) {
|
||||
pathSummary.brake_exit_reason = PATHSUMMARY_BRAKE_EXIT_REASON_PATHCOMPLETED;
|
||||
exit_brake = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (exit_brake) {
|
||||
// Calculate the distance error between the originally desired
|
||||
// stopping point and the actual brake-exit point.
|
||||
|
||||
PositionStateData p;
|
||||
PositionStateGet(&p);
|
||||
float north_offset = pathDesired.End.North - p.North;
|
||||
float east_offset = pathDesired.End.East - p.East;
|
||||
float down_offset = pathDesired.End.Down - p.Down;
|
||||
pathSummary.brake_distance_offset = sqrtf(north_offset * north_offset + east_offset * east_offset + down_offset * down_offset);
|
||||
pathSummary.time_remaining = pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_TIMEOUT] - pathStatus.path_time;
|
||||
pathSummary.fractional_progress = pathStatus.fractional_progress;
|
||||
float cur_velocity = velocityState.North * velocityState.North + velocityState.East * velocityState.East + velocityState.Down * velocityState.Down;
|
||||
cur_velocity = sqrtf(cur_velocity);
|
||||
pathSummary.decelrate = (pathDesired.StartingVelocity - cur_velocity) / pathStatus.path_time;
|
||||
pathSummary.brakeRateActualDesiredRatio = pathSummary.decelrate / vtolPathFollowerSettings.BrakeRate;
|
||||
pathSummary.velocityIntoHold = cur_velocity;
|
||||
pathSummary.UID = pathStatus.UID;
|
||||
PathSummarySet(&pathSummary);
|
||||
|
||||
plan_setup_assistedcontrol(true); // braking timeout true
|
||||
// having re-entered hold allow reassessment of neutral thrust
|
||||
neutralThrustEst.have_correction = false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (returnmode) {
|
||||
case RETURN_RESULT:
|
||||
return result;
|
||||
@ -607,6 +724,28 @@ static void processPOI()
|
||||
}
|
||||
}
|
||||
|
||||
static void updateBrakeVelocity(float startingVelocity, float dT, float brakeRate, float currentVelocity, float *updatedVelocity)
|
||||
{
|
||||
if (startingVelocity >= 0.0f) {
|
||||
*updatedVelocity = startingVelocity - dT * brakeRate;
|
||||
if (*updatedVelocity > currentVelocity) {
|
||||
*updatedVelocity = currentVelocity;
|
||||
}
|
||||
if (*updatedVelocity < 0.0f) {
|
||||
*updatedVelocity = 0.0f;
|
||||
}
|
||||
} else {
|
||||
*updatedVelocity = startingVelocity + dT * brakeRate;
|
||||
if (*updatedVelocity < currentVelocity) {
|
||||
*updatedVelocity = currentVelocity;
|
||||
}
|
||||
if (*updatedVelocity > 0.0f) {
|
||||
*updatedVelocity = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute desired velocity from the current position and path
|
||||
*/
|
||||
@ -617,57 +756,91 @@ static void updatePathVelocity(float kFF, bool limited)
|
||||
PositionStateGet(&positionState);
|
||||
VelocityStateData velocityState;
|
||||
VelocityStateGet(&velocityState);
|
||||
VelocityDesiredData velocityDesired;
|
||||
|
||||
const float dT = updatePeriod / 1000.0f;
|
||||
|
||||
// look ahead kFF seconds
|
||||
float cur[3] = { positionState.North + (velocityState.North * kFF),
|
||||
positionState.East + (velocityState.East * kFF),
|
||||
positionState.Down + (velocityState.Down * kFF) };
|
||||
struct path_status progress;
|
||||
if (pathDesired.Mode == PATHDESIRED_MODE_BRAKE) {
|
||||
float brakeRate = vtolPathFollowerSettings.BrakeRate;
|
||||
if (brakeRate < BRAKE_RATE_MINIMUM) {
|
||||
brakeRate = BRAKE_RATE_MINIMUM; // set a minimum to avoid a divide by zero potential below
|
||||
}
|
||||
updateBrakeVelocity(pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_STARTVELOCITYVECTOR_NORTH], pathStatus.path_time, brakeRate, velocityState.North, &velocityDesired.North);
|
||||
updateBrakeVelocity(pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_STARTVELOCITYVECTOR_EAST], pathStatus.path_time, brakeRate, velocityState.East, &velocityDesired.East);
|
||||
updateBrakeVelocity(pathDesired.ModeParameters[PATHDESIRED_MODEPARAMETER_BRAKE_STARTVELOCITYVECTOR_DOWN], pathStatus.path_time, brakeRate, velocityState.Down, &velocityDesired.Down);
|
||||
|
||||
path_progress(&pathDesired, cur, &progress);
|
||||
float cur_velocity = velocityState.North * velocityState.North + velocityState.East * velocityState.East + velocityState.Down * velocityState.Down;
|
||||
cur_velocity = sqrtf(cur_velocity);
|
||||
float desired_velocity = velocityDesired.North * velocityDesired.North + velocityDesired.East * velocityDesired.East + velocityDesired.Down * velocityDesired.Down;
|
||||
desired_velocity = sqrtf(desired_velocity);
|
||||
|
||||
// calculate velocity - can be zero if waypoints are too close
|
||||
VelocityDesiredData velocityDesired;
|
||||
velocityDesired.North = progress.path_vector[0];
|
||||
velocityDesired.East = progress.path_vector[1];
|
||||
velocityDesired.Down = progress.path_vector[2];
|
||||
// update pathstatus
|
||||
pathStatus.error = cur_velocity - desired_velocity;
|
||||
pathStatus.fractional_progress = 1.0f;
|
||||
if (pathDesired.StartingVelocity > 0.0f) {
|
||||
pathStatus.fractional_progress = (pathDesired.StartingVelocity - cur_velocity) / pathDesired.StartingVelocity;
|
||||
|
||||
if (limited &&
|
||||
// if a plane is crossing its desired flightpath facing the wrong way (away from flight direction)
|
||||
// it would turn towards the flightpath to get on its desired course. This however would reverse the correction vector
|
||||
// once it crosses the flightpath again, which would make it again turn towards the flightpath (but away from its desired heading)
|
||||
// leading to an S-shape snake course the wrong way
|
||||
// this only happens especially if HorizontalPosP is too high, as otherwise the angle between velocity desired and path_direction won't
|
||||
// turn steep unless there is enough space complete the turn before crossing the flightpath
|
||||
// in this case the plane effectively needs to be turned around
|
||||
// indicators:
|
||||
// difference between correction_direction and velocitystate >90 degrees and
|
||||
// difference between path_direction and velocitystate >90 degrees ( 4th sector, facing away from everything )
|
||||
// fix: ignore correction, steer in path direction until the situation has become better (condition doesn't apply anymore)
|
||||
// calculating angles < 90 degrees through dot products
|
||||
(vector_lengthf(progress.path_vector, 2) > 1e-6f) &&
|
||||
((progress.path_vector[0] * velocityState.North + progress.path_vector[1] * velocityState.East) < 0.0f) &&
|
||||
((progress.correction_vector[0] * velocityState.North + progress.correction_vector[1] * velocityState.East) < 0.0f)) {
|
||||
;
|
||||
// sometimes current velocity can exceed starting velocity due to initial acceleration
|
||||
if (pathStatus.fractional_progress < 0.0f) {
|
||||
pathStatus.fractional_progress = 0.0f;
|
||||
}
|
||||
}
|
||||
pathStatus.path_direction_north = velocityDesired.North;
|
||||
pathStatus.path_direction_east = velocityDesired.East;
|
||||
pathStatus.path_direction_down = velocityDesired.Down;
|
||||
|
||||
pathStatus.correction_direction_north = velocityDesired.North - velocityState.North;
|
||||
pathStatus.correction_direction_east = velocityDesired.East - velocityState.East;
|
||||
pathStatus.correction_direction_down = velocityDesired.Down - velocityState.Down;
|
||||
} else {
|
||||
// calculate correction
|
||||
velocityDesired.North += pid_apply(&global.PIDposH[0], progress.correction_vector[0], dT);
|
||||
velocityDesired.East += pid_apply(&global.PIDposH[1], progress.correction_vector[1], dT);
|
||||
// look ahead kFF seconds
|
||||
float cur[3] = { positionState.North + (velocityState.North * kFF),
|
||||
positionState.East + (velocityState.East * kFF),
|
||||
positionState.Down + (velocityState.Down * kFF) };
|
||||
struct path_status progress;
|
||||
path_progress(&pathDesired, cur, &progress);
|
||||
|
||||
// calculate velocity - can be zero if waypoints are too close
|
||||
velocityDesired.North = progress.path_vector[0];
|
||||
velocityDesired.East = progress.path_vector[1];
|
||||
velocityDesired.Down = progress.path_vector[2];
|
||||
|
||||
if (limited &&
|
||||
// if a plane is crossing its desired flightpath facing the wrong way (away from flight direction)
|
||||
// it would turn towards the flightpath to get on its desired course. This however would reverse the correction vector
|
||||
// once it crosses the flightpath again, which would make it again turn towards the flightpath (but away from its desired heading)
|
||||
// leading to an S-shape snake course the wrong way
|
||||
// this only happens especially if HorizontalPosP is too high, as otherwise the angle between velocity desired and path_direction won't
|
||||
// turn steep unless there is enough space complete the turn before crossing the flightpath
|
||||
// in this case the plane effectively needs to be turned around
|
||||
// indicators:
|
||||
// difference between correction_direction and velocitystate >90 degrees and
|
||||
// difference between path_direction and velocitystate >90 degrees ( 4th sector, facing away from everything )
|
||||
// fix: ignore correction, steer in path direction until the situation has become better (condition doesn't apply anymore)
|
||||
// calculating angles < 90 degrees through dot products
|
||||
(vector_lengthf(progress.path_vector, 2) > 1e-6f) &&
|
||||
((progress.path_vector[0] * velocityState.North + progress.path_vector[1] * velocityState.East) < 0.0f) &&
|
||||
((progress.correction_vector[0] * velocityState.North + progress.correction_vector[1] * velocityState.East) < 0.0f)) {
|
||||
;
|
||||
} else {
|
||||
// calculate correction
|
||||
velocityDesired.North += pid_apply(&global.PIDposH[0], progress.correction_vector[0], dT);
|
||||
velocityDesired.East += pid_apply(&global.PIDposH[1], progress.correction_vector[1], dT);
|
||||
}
|
||||
velocityDesired.Down += pid_apply(&global.PIDposV, progress.correction_vector[2], dT);
|
||||
|
||||
// update pathstatus
|
||||
pathStatus.error = progress.error;
|
||||
pathStatus.fractional_progress = progress.fractional_progress;
|
||||
pathStatus.path_direction_north = progress.path_vector[0];
|
||||
pathStatus.path_direction_east = progress.path_vector[1];
|
||||
pathStatus.path_direction_down = progress.path_vector[2];
|
||||
|
||||
pathStatus.correction_direction_north = progress.correction_vector[0];
|
||||
pathStatus.correction_direction_east = progress.correction_vector[1];
|
||||
pathStatus.correction_direction_down = progress.correction_vector[2];
|
||||
}
|
||||
velocityDesired.Down += pid_apply(&global.PIDposV, progress.correction_vector[2], dT);
|
||||
|
||||
// update pathstatus
|
||||
pathStatus.error = progress.error;
|
||||
pathStatus.fractional_progress = progress.fractional_progress;
|
||||
pathStatus.path_direction_north = progress.path_vector[0];
|
||||
pathStatus.path_direction_east = progress.path_vector[1];
|
||||
pathStatus.path_direction_down = progress.path_vector[2];
|
||||
|
||||
pathStatus.correction_direction_north = progress.correction_vector[0];
|
||||
pathStatus.correction_direction_east = progress.correction_vector[1];
|
||||
pathStatus.correction_direction_down = progress.correction_vector[2];
|
||||
|
||||
VelocityDesiredSet(&velocityDesired);
|
||||
}
|
||||
@ -1036,8 +1209,9 @@ static bool correctCourse(float *C, float *V, float *F, float s)
|
||||
*/
|
||||
static int8_t updateVtolDesiredAttitude(bool yaw_attitude, float yaw_direction)
|
||||
{
|
||||
const float dT = updatePeriod / 1000.0f;
|
||||
uint8_t result = 1;
|
||||
const float dT = updatePeriod / 1000.0f;
|
||||
uint8_t result = 1;
|
||||
bool manual_thrust = false;
|
||||
|
||||
VelocityDesiredData velocityDesired;
|
||||
VelocityStateData velocityState;
|
||||
@ -1045,6 +1219,7 @@ static int8_t updateVtolDesiredAttitude(bool yaw_attitude, float yaw_direction)
|
||||
AttitudeStateData attitudeState;
|
||||
StabilizationBankData stabSettings;
|
||||
SystemSettingsData systemSettings;
|
||||
VtolSelfTuningStatsData vtolSelfTuningStats;
|
||||
|
||||
float northError;
|
||||
float northCommand;
|
||||
@ -1062,36 +1237,121 @@ static int8_t updateVtolDesiredAttitude(bool yaw_attitude, float yaw_direction)
|
||||
VelocityDesiredGet(&velocityDesired);
|
||||
AttitudeStateGet(&attitudeState);
|
||||
StabilizationBankGet(&stabSettings);
|
||||
VtolSelfTuningStatsGet(&vtolSelfTuningStats);
|
||||
|
||||
// Testing code - refactor into manual control command
|
||||
ManualControlCommandData manualControlData;
|
||||
ManualControlCommandGet(&manualControlData);
|
||||
|
||||
// scale velocity if it is above configured maximum
|
||||
float velH = sqrtf(velocityDesired.North * velocityDesired.North + velocityDesired.East * velocityDesired.East);
|
||||
if (velH > vtolPathFollowerSettings.HorizontalVelMax) {
|
||||
velocityDesired.North *= vtolPathFollowerSettings.HorizontalVelMax / velH;
|
||||
velocityDesired.East *= vtolPathFollowerSettings.HorizontalVelMax / velH;
|
||||
}
|
||||
if (fabsf(velocityDesired.Down) > vtolPathFollowerSettings.VerticalVelMax) {
|
||||
velocityDesired.Down *= vtolPathFollowerSettings.VerticalVelMax / fabsf(velocityDesired.Down);
|
||||
if (pathDesired.Mode != PATHDESIRED_MODE_BRAKE) {
|
||||
// scale velocity if it is above configured maximum
|
||||
// for braking, we can not help it if initial velocity was greater
|
||||
float velH = sqrtf(velocityDesired.North * velocityDesired.North + velocityDesired.East * velocityDesired.East);
|
||||
if (velH > vtolPathFollowerSettings.HorizontalVelMax) {
|
||||
velocityDesired.North *= vtolPathFollowerSettings.HorizontalVelMax / velH;
|
||||
velocityDesired.East *= vtolPathFollowerSettings.HorizontalVelMax / velH;
|
||||
}
|
||||
if (fabsf(velocityDesired.Down) > vtolPathFollowerSettings.VerticalVelMax) {
|
||||
velocityDesired.Down *= vtolPathFollowerSettings.VerticalVelMax / fabsf(velocityDesired.Down);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute desired north command
|
||||
northError = velocityDesired.North - velocityState.North;
|
||||
northCommand = pid_apply(&global.PIDvel[0], northError, dT) + velocityDesired.North * vtolPathFollowerSettings.VelocityFeedforward;
|
||||
// calculate the velocity errors between desired and actual
|
||||
northError = velocityDesired.North - velocityState.North;
|
||||
eastError = velocityDesired.East - velocityState.East;
|
||||
downError = velocityDesired.Down - velocityState.Down;
|
||||
|
||||
// Compute desired east command
|
||||
eastError = velocityDesired.East - velocityState.East;
|
||||
eastCommand = pid_apply(&global.PIDvel[1], eastError, dT) + velocityDesired.East * vtolPathFollowerSettings.VelocityFeedforward;
|
||||
|
||||
// Compute desired down command
|
||||
downError = velocityDesired.Down - velocityState.Down;
|
||||
// Must flip this sign
|
||||
downError = -downError;
|
||||
downCommand = pid_apply(&global.PIDvel[2], downError, dT);
|
||||
downError = -downError;
|
||||
|
||||
stabDesired.Thrust = boundf(downCommand + vtolPathFollowerSettings.ThrustLimits.Neutral, vtolPathFollowerSettings.ThrustLimits.Min, vtolPathFollowerSettings.ThrustLimits.Max);
|
||||
// Compute desired commands
|
||||
if (pathDesired.Mode != PATHDESIRED_MODE_BRAKE) {
|
||||
northCommand = pid_apply(&global.PIDvel[0], northError, dT) + velocityDesired.North * vtolPathFollowerSettings.VelocityFeedforward;
|
||||
eastCommand = pid_apply(&global.PIDvel[1], eastError, dT) + velocityDesired.East * vtolPathFollowerSettings.VelocityFeedforward;
|
||||
} else {
|
||||
northCommand = pid_apply(&global.BrakePIDvel[0], northError, dT) + velocityDesired.North * vtolPathFollowerSettings.BrakeVelocityFeedforward;
|
||||
eastCommand = pid_apply(&global.BrakePIDvel[1], eastError, dT) + velocityDesired.East * vtolPathFollowerSettings.BrakeVelocityFeedforward;
|
||||
}
|
||||
downCommand = pid_apply(&global.PIDvel[2], downError, dT);
|
||||
|
||||
|
||||
if ((vtolPathFollowerSettings.ThrustControl == VTOLPATHFOLLOWERSETTINGS_THRUSTCONTROL_MANUAL &&
|
||||
flightStatus.FlightModeAssist == FLIGHTSTATUS_FLIGHTMODEASSIST_NONE) ||
|
||||
(flightStatus.FlightModeAssist && flightStatus.AssistedThrottleState == FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL)) {
|
||||
manual_thrust = true;
|
||||
}
|
||||
|
||||
// if auto thrust and we have not run a correction calc check for PH and stability to then run an assessment
|
||||
// note that arming into this flight mode is not allowed, so assumption here is that
|
||||
// we have not landed. If GPSAssist+Manual/Cruise control thrust mode is used, a user overriding the
|
||||
// altitude maintaining PID will have moved the throttle state to FLIGHTSTATUS_ASSISTEDTHROTTLESTATE_MANUAL.
|
||||
// In manualcontrol.c the state will stay in manual throttle until the throttle command exceeds the vtol thrust min,
|
||||
// avoiding auto-takeoffs. Therefore no need to check that here.
|
||||
if (!manual_thrust && neutralThrustEst.have_correction != true) {
|
||||
// Assess if position hold state running. This can be normal position hold or
|
||||
// another mode with assist-hold active.
|
||||
bool ph_active =
|
||||
((flightStatus.FlightMode == FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD &&
|
||||
flightStatus.FlightModeAssist == FLIGHTSTATUS_FLIGHTMODEASSIST_NONE) ||
|
||||
(flightStatus.FlightModeAssist != FLIGHTSTATUS_FLIGHTMODEASSIST_NONE &&
|
||||
flightStatus.AssistedControlState == FLIGHTSTATUS_ASSISTEDCONTROLSTATE_HOLD));
|
||||
|
||||
|
||||
bool stable = (fabsf(pathStatus.correction_direction_down) < NEUTRALTHRUST_PH_POSITIONAL_ERROR_LIMIT &&
|
||||
fabsf(velocityDesired.Down) < NEUTRALTHRUST_PH_VEL_DESIRED_LIMIT &&
|
||||
fabsf(velocityState.Down) < NEUTRALTHRUST_PH_VEL_STATE_LIMIT &&
|
||||
fabsf(downError) < NEUTRALTHRUST_PH_VEL_ERROR_LIMIT);
|
||||
|
||||
if (ph_active && stable) {
|
||||
if (neutralThrustEst.start_sampling) {
|
||||
neutralThrustEst.count++;
|
||||
|
||||
|
||||
// delay count for 2 seconds into hold allowing for stablisation
|
||||
if (neutralThrustEst.count > NEUTRALTHRUST_START_DELAY) {
|
||||
neutralThrustEst.sum += global.PIDvel[2].iAccumulator;
|
||||
if (global.PIDvel[2].iAccumulator < neutralThrustEst.min) {
|
||||
neutralThrustEst.min = global.PIDvel[2].iAccumulator;
|
||||
}
|
||||
if (global.PIDvel[2].iAccumulator > neutralThrustEst.max) {
|
||||
neutralThrustEst.max = global.PIDvel[2].iAccumulator;
|
||||
}
|
||||
}
|
||||
|
||||
if (neutralThrustEst.count >= NEUTRALTHRUST_END_COUNT) {
|
||||
// 6 seconds have past
|
||||
// lets take an average
|
||||
neutralThrustEst.average = neutralThrustEst.sum / (float)(NEUTRALTHRUST_END_COUNT - NEUTRALTHRUST_START_DELAY);
|
||||
neutralThrustEst.correction = neutralThrustEst.average / 1000.0f;
|
||||
|
||||
global.PIDvel[2].iAccumulator -= neutralThrustEst.average;
|
||||
|
||||
neutralThrustEst.start_sampling = false;
|
||||
neutralThrustEst.have_correction = true;
|
||||
|
||||
// Write a new adjustment value
|
||||
// vtolSelfTuningStats.NeutralThrustOffset was incremental adjusted above
|
||||
VtolSelfTuningStatsData new_vtolSelfTuningStats;
|
||||
// add the average remaining i value to the
|
||||
new_vtolSelfTuningStats.NeutralThrustOffset = vtolSelfTuningStats.NeutralThrustOffset + neutralThrustEst.correction;
|
||||
new_vtolSelfTuningStats.NeutralThrustCorrection = neutralThrustEst.correction; // the i term thrust correction value applied
|
||||
new_vtolSelfTuningStats.NeutralThrustAccumulator = global.PIDvel[2].iAccumulator; // the actual iaccumulator value after correction
|
||||
new_vtolSelfTuningStats.NeutralThrustRange = neutralThrustEst.max - neutralThrustEst.min;
|
||||
VtolSelfTuningStatsSet(&new_vtolSelfTuningStats);
|
||||
}
|
||||
} else {
|
||||
// start a tick count
|
||||
neutralThrustEst.start_sampling = true;
|
||||
neutralThrustEst.count = 0;
|
||||
neutralThrustEst.sum = 0.0f;
|
||||
neutralThrustEst.max = 0.0f;
|
||||
neutralThrustEst.min = 0.0f;
|
||||
}
|
||||
} else {
|
||||
// reset sampling as we did't get 6 continuous seconds
|
||||
neutralThrustEst.start_sampling = false;
|
||||
}
|
||||
} // else we already have a correction for this PH run
|
||||
|
||||
// Generally in braking the downError will be an increased altitude. We really will rely on cruisecontrol to backoff.
|
||||
stabDesired.Thrust = boundf(vtolSelfTuningStats.NeutralThrustOffset + downCommand + vtolPathFollowerSettings.ThrustLimits.Neutral, vtolPathFollowerSettings.ThrustLimits.Min, vtolPathFollowerSettings.ThrustLimits.Max);
|
||||
|
||||
|
||||
// DEBUG HACK: allow user to skew compass on purpose to see if emergency failsafe kicks in
|
||||
@ -1102,6 +1362,7 @@ static int8_t updateVtolDesiredAttitude(bool yaw_attitude, float yaw_direction)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( // emergency flyaway detection
|
||||
( // integral already at its limit
|
||||
vtolPathFollowerSettings.HorizontalVelPID.ILimit - fabsf(global.PIDvel[0].iAccumulator) < 1e-6f ||
|
||||
@ -1124,19 +1385,22 @@ static int8_t updateVtolDesiredAttitude(bool yaw_attitude, float yaw_direction)
|
||||
|
||||
// Project the north and east command signals into the pitch and roll based on yaw. For this to behave well the
|
||||
// craft should move similarly for 5 deg roll versus 5 deg pitch
|
||||
|
||||
float maxPitch = vtolPathFollowerSettings.MaxRollPitch;
|
||||
if (pathDesired.Mode == PATHDESIRED_MODE_BRAKE) {
|
||||
maxPitch = vtolPathFollowerSettings.BrakeMaxPitch;
|
||||
}
|
||||
|
||||
stabDesired.Pitch = boundf(-northCommand * cosf(DEG2RAD(attitudeState.Yaw)) +
|
||||
-eastCommand * sinf(DEG2RAD(attitudeState.Yaw)),
|
||||
-vtolPathFollowerSettings.MaxRollPitch, vtolPathFollowerSettings.MaxRollPitch);
|
||||
-maxPitch, maxPitch);
|
||||
stabDesired.Roll = boundf(-northCommand * sinf(DEG2RAD(attitudeState.Yaw)) +
|
||||
eastCommand * cosf(DEG2RAD(attitudeState.Yaw)),
|
||||
-vtolPathFollowerSettings.MaxRollPitch, vtolPathFollowerSettings.MaxRollPitch);
|
||||
-maxPitch, maxPitch);
|
||||
|
||||
ManualControlCommandData manualControl;
|
||||
ManualControlCommandGet(&manualControl);
|
||||
|
||||
if (vtolPathFollowerSettings.ThrustControl == VTOLPATHFOLLOWERSETTINGS_THRUSTCONTROL_MANUAL) {
|
||||
// For now override thrust with manual control. Disable at your risk, quad goes to China.
|
||||
ManualControlCommandData manualControl;
|
||||
ManualControlCommandGet(&manualControl);
|
||||
stabDesired.Thrust = manualControl.Thrust;
|
||||
}
|
||||
|
||||
stabDesired.StabilizationMode.Roll = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE;
|
||||
stabDesired.StabilizationMode.Pitch = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE;
|
||||
@ -1145,9 +1409,51 @@ static int8_t updateVtolDesiredAttitude(bool yaw_attitude, float yaw_direction)
|
||||
stabDesired.Yaw = yaw_direction;
|
||||
} else {
|
||||
stabDesired.StabilizationMode.Yaw = STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK;
|
||||
stabDesired.Yaw = stabSettings.MaximumRate.Yaw * manualControlData.Yaw;
|
||||
stabDesired.Yaw = stabSettings.MaximumRate.Yaw * manualControl.Yaw;
|
||||
}
|
||||
|
||||
// default thrust mode to cruise control
|
||||
stabDesired.StabilizationMode.Thrust = STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL;
|
||||
|
||||
// when flight mode assist is active but in primary-thrust mode, the thrust mode must be set to the same as per the primary mode.
|
||||
if (flightStatus.FlightModeAssist == FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST_PRIMARYTHRUST) {
|
||||
FlightModeSettingsData settings;
|
||||
FlightModeSettingsGet(&settings);
|
||||
FlightModeSettingsStabilization1SettingsOptions thrustMode = FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_CRUISECONTROL;
|
||||
|
||||
switch (flightStatus.FlightMode) {
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1:
|
||||
thrustMode = settings.Stabilization1Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED2:
|
||||
thrustMode = settings.Stabilization2Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED3:
|
||||
thrustMode = settings.Stabilization3Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED4:
|
||||
thrustMode = settings.Stabilization4Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED5:
|
||||
thrustMode = settings.Stabilization5Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED6:
|
||||
thrustMode = settings.Stabilization6Settings.Thrust;
|
||||
break;
|
||||
case FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD:
|
||||
// we hard code the "GPS Assisted" PostionHold to use alt-vario which
|
||||
// is a more appropriate throttle mode. "GPSAssist" adds braking
|
||||
// and a better throttle management to the standard Position Hold.
|
||||
thrustMode = FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEVARIO;
|
||||
break;
|
||||
}
|
||||
stabDesired.StabilizationMode.Thrust = thrustMode;
|
||||
stabDesired.Thrust = manualControl.Thrust;
|
||||
} else if (manual_thrust) {
|
||||
stabDesired.Thrust = manualControl.Thrust;
|
||||
}
|
||||
// else thrust is set by the PID controller
|
||||
|
||||
StabilizationDesiredSet(&stabDesired);
|
||||
|
||||
return result;
|
||||
|
@ -39,27 +39,33 @@
|
||||
#include <receiveractivity.h>
|
||||
#include <flightstatus.h>
|
||||
#include <flighttelemetrystats.h>
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
#include <stabilizationsettings.h>
|
||||
#endif
|
||||
#include <flightmodesettings.h>
|
||||
#include <systemsettings.h>
|
||||
#include <taskinfo.h>
|
||||
|
||||
|
||||
#if defined(PIOS_INCLUDE_USB_RCTX)
|
||||
#include "pios_usb_rctx.h"
|
||||
#endif /* PIOS_INCLUDE_USB_RCTX */
|
||||
|
||||
// Private constants
|
||||
#if defined(PIOS_RECEIVER_STACK_SIZE)
|
||||
#define STACK_SIZE_BYTES PIOS_RECEIVER_STACK_SIZE
|
||||
#define STACK_SIZE_BYTES PIOS_RECEIVER_STACK_SIZE
|
||||
#else
|
||||
#define STACK_SIZE_BYTES 1152
|
||||
#define STACK_SIZE_BYTES 1152
|
||||
#endif
|
||||
|
||||
#define TASK_PRIORITY (tskIDLE_PRIORITY + 3) // 3 = flight control
|
||||
#define UPDATE_PERIOD_MS 20
|
||||
#define THROTTLE_FAILSAFE -0.1f
|
||||
#define ARMED_THRESHOLD 0.50f
|
||||
#define TASK_PRIORITY (tskIDLE_PRIORITY + 3) // 3 = flight control
|
||||
#define UPDATE_PERIOD_MS 20
|
||||
#define THROTTLE_FAILSAFE -0.1f
|
||||
#define ARMED_THRESHOLD 0.50f
|
||||
// safe band to allow a bit of calibration error or trim offset (in microseconds)
|
||||
#define CONNECTION_OFFSET 250
|
||||
#define CONNECTION_OFFSET 250
|
||||
|
||||
#define ASSISTEDCONTROL_DEADBAND_MINIMUM 0.02f // minimum value for a well bahaved Tx.
|
||||
|
||||
// Private types
|
||||
|
||||
@ -79,8 +85,12 @@ static uint32_t timeDifferenceMs(portTickType start_time, portTickType end_time)
|
||||
static bool validInputRange(int16_t min, int16_t max, uint16_t value);
|
||||
static void applyDeadband(float *value, float deadband);
|
||||
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
static uint8_t isAssistedFlightMode(uint8_t position);
|
||||
#endif
|
||||
|
||||
#ifdef USE_INPUT_LPF
|
||||
static void applyLPF(float *value, ManualControlSettingsResponseTimeElem channel, ManualControlSettingsData *settings, float dT);
|
||||
static void applyLPF(float *value, ManualControlSettingsResponseTimeElem channel, ManualControlSettingsResponseTimeData *responseTime, float deadband, float dT);
|
||||
#endif
|
||||
|
||||
#define RCVR_ACTIVITY_MONITOR_CHANNELS_PER_GROUP 12
|
||||
@ -129,6 +139,10 @@ int32_t ReceiverInitialize()
|
||||
ManualControlCommandInitialize();
|
||||
ReceiverActivityInitialize();
|
||||
ManualControlSettingsInitialize();
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
StabilizationSettingsInitialize();
|
||||
#endif
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -363,11 +377,30 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
cmd.FlightModeSwitchPosition = settings.FlightModeNumber - 1;
|
||||
}
|
||||
|
||||
float deadband_checked = settings.Deadband;
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
// AssistedControl must have deadband set for pitch/roll hence
|
||||
// we default to a higher value for badly behaved TXs and also enforce a minimum value
|
||||
// for well behaved TXs
|
||||
uint8_t assistedMode = isAssistedFlightMode(cmd.FlightModeSwitchPosition);
|
||||
if (assistedMode) {
|
||||
deadband_checked = settings.DeadbandAssistedControl;
|
||||
if (deadband_checked < ASSISTEDCONTROL_DEADBAND_MINIMUM) {
|
||||
deadband_checked = ASSISTEDCONTROL_DEADBAND_MINIMUM;
|
||||
}
|
||||
|
||||
// If user has set settings.Deadband to a higher value...we use that.
|
||||
if (deadband_checked < settings.Deadband) {
|
||||
deadband_checked = settings.Deadband;
|
||||
}
|
||||
}
|
||||
#endif // PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
|
||||
// Apply deadband for Roll/Pitch/Yaw stick inputs
|
||||
if (settings.Deadband > 0.0f) {
|
||||
applyDeadband(&cmd.Roll, settings.Deadband);
|
||||
applyDeadband(&cmd.Pitch, settings.Deadband);
|
||||
applyDeadband(&cmd.Yaw, settings.Deadband);
|
||||
if (deadband_checked > 0.0f) {
|
||||
applyDeadband(&cmd.Roll, deadband_checked);
|
||||
applyDeadband(&cmd.Pitch, deadband_checked);
|
||||
applyDeadband(&cmd.Yaw, deadband_checked);
|
||||
}
|
||||
#ifdef USE_INPUT_LPF
|
||||
// Apply Low Pass Filter to input channels, time delta between calls in ms
|
||||
@ -377,9 +410,9 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
(float)UPDATE_PERIOD_MS;
|
||||
lastSysTimeLPF = thisSysTime;
|
||||
|
||||
applyLPF(&cmd.Roll, MANUALCONTROLSETTINGS_RESPONSETIME_ROLL, &settings, dT);
|
||||
applyLPF(&cmd.Pitch, MANUALCONTROLSETTINGS_RESPONSETIME_PITCH, &settings, dT);
|
||||
applyLPF(&cmd.Yaw, MANUALCONTROLSETTINGS_RESPONSETIME_YAW, &settings, dT);
|
||||
applyLPF(&cmd.Roll, MANUALCONTROLSETTINGS_RESPONSETIME_ROLL, &settings.ResponseTime, deadband_checked, dT);
|
||||
applyLPF(&cmd.Pitch, MANUALCONTROLSETTINGS_RESPONSETIME_PITCH, &settings.ResponseTime, deadband_checked, dT);
|
||||
applyLPF(&cmd.Yaw, MANUALCONTROLSETTINGS_RESPONSETIME_YAW, &settings.ResponseTime, deadband_checked, dT);
|
||||
#endif // USE_INPUT_LPF
|
||||
if (cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE] != (uint16_t)PIOS_RCVR_INVALID
|
||||
&& cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE] != (uint16_t)PIOS_RCVR_NODRIVER
|
||||
@ -389,7 +422,7 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
applyDeadband(&cmd.Collective, settings.Deadband);
|
||||
}
|
||||
#ifdef USE_INPUT_LPF
|
||||
applyLPF(&cmd.Collective, MANUALCONTROLSETTINGS_RESPONSETIME_COLLECTIVE, &settings, dT);
|
||||
applyLPF(&cmd.Collective, MANUALCONTROLSETTINGS_RESPONSETIME_COLLECTIVE, &settings.ResponseTime, settings.Deadband, dT);
|
||||
#endif // USE_INPUT_LPF
|
||||
}
|
||||
|
||||
@ -409,7 +442,7 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
if (settings.ChannelGroups.Accessory0 != MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
|
||||
accessory.AccessoryVal = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY0];
|
||||
#ifdef USE_INPUT_LPF
|
||||
applyLPF(&accessory.AccessoryVal, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY0, &settings, dT);
|
||||
applyLPF(&accessory.AccessoryVal, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY0, &settings.ResponseTime, settings.Deadband, dT);
|
||||
#endif
|
||||
if (AccessoryDesiredInstSet(0, &accessory) != 0) {
|
||||
AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER, SYSTEMALARMS_ALARM_WARNING);
|
||||
@ -419,7 +452,7 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
if (settings.ChannelGroups.Accessory1 != MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
|
||||
accessory.AccessoryVal = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY1];
|
||||
#ifdef USE_INPUT_LPF
|
||||
applyLPF(&accessory.AccessoryVal, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY1, &settings, dT);
|
||||
applyLPF(&accessory.AccessoryVal, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY1, &settings.ResponseTime, settings.Deadband, dT);
|
||||
#endif
|
||||
if (AccessoryDesiredInstSet(1, &accessory) != 0) {
|
||||
AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER, SYSTEMALARMS_ALARM_WARNING);
|
||||
@ -429,7 +462,7 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
if (settings.ChannelGroups.Accessory2 != MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
|
||||
accessory.AccessoryVal = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY2];
|
||||
#ifdef USE_INPUT_LPF
|
||||
applyLPF(&accessory.AccessoryVal, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY2, &settings, dT);
|
||||
applyLPF(&accessory.AccessoryVal, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY2, &settings.ResponseTime, settings.Deadband, dT);
|
||||
#endif
|
||||
|
||||
if (AccessoryDesiredInstSet(2, &accessory) != 0) {
|
||||
@ -440,6 +473,8 @@ static void receiverTask(__attribute__((unused)) void *parameters)
|
||||
|
||||
// Update cmd object
|
||||
ManualControlCommandSet(&cmd);
|
||||
|
||||
|
||||
#if defined(PIOS_INCLUDE_USB_RCTX)
|
||||
if (pios_usb_rctx_id) {
|
||||
PIOS_USB_RCTX_Update(pios_usb_rctx_id,
|
||||
@ -658,16 +693,42 @@ static void applyDeadband(float *value, float deadband)
|
||||
/**
|
||||
* @brief Apply Low Pass Filter to Throttle/Roll/Pitch/Yaw or Accessory channel
|
||||
*/
|
||||
static void applyLPF(float *value, ManualControlSettingsResponseTimeElem channel, ManualControlSettingsData *settings, float dT)
|
||||
static void applyLPF(float *value, ManualControlSettingsResponseTimeElem channel, ManualControlSettingsResponseTimeData *responseTime, float deadband, float dT)
|
||||
{
|
||||
if (ManualControlSettingsResponseTimeToArray(settings->ResponseTime)[channel]) {
|
||||
float rt = (float)ManualControlSettingsResponseTimeToArray(settings->ResponseTime)[channel];
|
||||
float rt = (float)ManualControlSettingsResponseTimeToArray((*responseTime))[channel];
|
||||
|
||||
if (rt > 0.0f) {
|
||||
inputFiltered[channel] = ((rt * inputFiltered[channel]) + (dT * (*value))) / (rt + dT);
|
||||
|
||||
// avoid a long tail of non-zero data. if we have deadband, once the filtered result reduces to 1/10th
|
||||
// of deadband revert to 0. We downstream rely on this to know when sticks are centered.
|
||||
if (deadband > 0.0f && fabsf(inputFiltered[channel]) < deadband / 10.0f) {
|
||||
inputFiltered[channel] = 0.0f;
|
||||
}
|
||||
*value = inputFiltered[channel];
|
||||
}
|
||||
}
|
||||
#endif // USE_INPUT_LPF
|
||||
|
||||
/**
|
||||
* Check and set modes for gps assisted stablised flight modes
|
||||
*/
|
||||
#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
|
||||
static uint8_t isAssistedFlightMode(uint8_t position)
|
||||
{
|
||||
uint8_t isAssistedFlag = STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NONE;
|
||||
uint8_t FlightModeAssistMap[STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NUMELEM];
|
||||
|
||||
StabilizationSettingsFlightModeAssistMapGet(FlightModeAssistMap);
|
||||
|
||||
if (position < STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NUMELEM) {
|
||||
isAssistedFlag = FlightModeAssistMap[position];
|
||||
}
|
||||
|
||||
return isAssistedFlag;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
|
@ -102,7 +102,6 @@ int32_t StabilizationInitialize()
|
||||
StabilizationSettingsBank3Initialize();
|
||||
RateDesiredInitialize();
|
||||
ManualControlCommandInitialize(); // only used for PID bank selection based on flight mode switch
|
||||
// Code required for relay tuning
|
||||
sin_lookup_initalize();
|
||||
|
||||
stabilizationOuterloopInit();
|
||||
|
32
flight/pios/common/libraries/yaffs2/README.txt
Normal file
32
flight/pios/common/libraries/yaffs2/README.txt
Normal file
@ -0,0 +1,32 @@
|
||||
yaffs2 library in flight/pios/common/libraries/yaffs2
|
||||
|
||||
Implementation of core Yaffs Direct:
|
||||
yaffs_allocator.c Allocates Yaffs object and tnode structures.
|
||||
yaffs_checkpointrw.c Streamer for writing checkpoint data
|
||||
yaffs_ecc.c ECC code
|
||||
yaffs_guts.c The major Yaffs algorithms.
|
||||
yaffs_nand.c Flash interfacing abstraction.
|
||||
yaffs_packedtags1.c Tags packing code
|
||||
yaffs_packedtags2.c
|
||||
yaffs_qsort.c Qsort used during Yaffs2 scanning
|
||||
yaffs_tagscompat.c Tags compatibility code to support Yaffs1 mode.
|
||||
yaffs_tagsvalidity.c Tags validity checking.
|
||||
yaffsfs.c The Yaffs direct interface
|
||||
yaffs_hweight.c Linux hweight implementation equivalent (Is this in OP TODO)
|
||||
yaffs_list.c Linked list implementation
|
||||
yaffs_tarsmarshall.c
|
||||
Interface between Yaffs and OP PiOS or Posix:
|
||||
ydirectenv.h Environment wrappers for Yaffs direct to suit the OP firmware environment
|
||||
yaffs_osglue.h Interface for Yaffs to use to access OS method
|
||||
Interface to Yaffs Direct:
|
||||
yaffsfs.h and interface structures and functions defined here
|
||||
|
||||
POSIX Implementation in flight/pios/common/libraries/posix
|
||||
PiOS Implementation in flight/pios/common/libraries/PiOS
|
||||
|
||||
CHANGE LOG:
|
||||
1. Initial import
|
||||
2. ydirectenv.h Defined Y_LOFF_T to be int32_t. Needs to be signed to return -1 for lseek invalid
|
||||
3. library.mk Compiler defines to set yaffs2 diect mode and port options
|
||||
4. simposix Added simposix implementation of lower layers
|
||||
5. arm compilation Addressed compile errors in arm toolchain. Generally marked changes with CONFIG_YAFFS_OP
|
30
flight/pios/common/libraries/yaffs2/inc/yaffs_allocator.h
Normal file
30
flight/pios/common/libraries/yaffs2/inc/yaffs_allocator.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_ALLOCATOR_H__
|
||||
#define __YAFFS_ALLOCATOR_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev);
|
||||
void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev);
|
||||
|
||||
struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev);
|
||||
void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn);
|
||||
|
||||
struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev);
|
||||
void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj);
|
||||
|
||||
#endif
|
28
flight/pios/common/libraries/yaffs2/inc/yaffs_attribs.h
Normal file
28
flight/pios/common/libraries/yaffs2/inc/yaffs_attribs.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_ATTRIBS_H__
|
||||
#define __YAFFS_ATTRIBS_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh);
|
||||
void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj);
|
||||
void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev);
|
||||
void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c);
|
||||
int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr);
|
||||
int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr);
|
||||
|
||||
#endif
|
33
flight/pios/common/libraries/yaffs2/inc/yaffs_bitmap.h
Normal file
33
flight/pios/common/libraries/yaffs2/inc/yaffs_bitmap.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Chunk bitmap manipulations
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_BITMAP_H__
|
||||
#define __YAFFS_BITMAP_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk);
|
||||
void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk);
|
||||
void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
|
||||
void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
|
||||
int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
|
||||
int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk);
|
||||
int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk);
|
||||
|
||||
#endif
|
33
flight/pios/common/libraries/yaffs2/inc/yaffs_checkptrw.h
Normal file
33
flight/pios/common/libraries/yaffs2/inc/yaffs_checkptrw.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_CHECKPTRW_H__
|
||||
#define __YAFFS_CHECKPTRW_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing);
|
||||
|
||||
int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes);
|
||||
|
||||
int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes);
|
||||
|
||||
int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum);
|
||||
|
||||
int yaffs_checkpt_close(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
44
flight/pios/common/libraries/yaffs2/inc/yaffs_ecc.h
Normal file
44
flight/pios/common/libraries/yaffs2/inc/yaffs_ecc.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code implements the ECC algorithm used in SmartMedia.
|
||||
*
|
||||
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
||||
* The two unused bit are set to 1.
|
||||
* The ECC can correct single bit errors in a 256-byte page of data.
|
||||
* Thus, two such ECC blocks are used on a 512-byte NAND page.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_ECC_H__
|
||||
#define __YAFFS_ECC_H__
|
||||
|
||||
struct yaffs_ecc_other {
|
||||
unsigned char col_parity;
|
||||
unsigned line_parity;
|
||||
unsigned line_parity_prime;
|
||||
};
|
||||
|
||||
void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc);
|
||||
int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
|
||||
const unsigned char *test_ecc);
|
||||
|
||||
void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
|
||||
struct yaffs_ecc_other *ecc);
|
||||
int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
|
||||
struct yaffs_ecc_other *read_ecc,
|
||||
const struct yaffs_ecc_other *test_ecc);
|
||||
#endif
|
35
flight/pios/common/libraries/yaffs2/inc/yaffs_flashif.h
Normal file
35
flight/pios/common/libraries/yaffs2/inc/yaffs_flashif.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_FLASH_H__
|
||||
#define __YAFFS_FLASH_H__
|
||||
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
int yflash_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber);
|
||||
int yflash_WriteChunkToNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, const struct yaffs_spare *spare);
|
||||
int yflash_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, const struct yaffs_ext_tags *tags);
|
||||
int yflash_ReadChunkFromNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, struct yaffs_spare *spare);
|
||||
int yflash_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, struct yaffs_ext_tags *tags);
|
||||
int yflash_InitialiseNAND(struct yaffs_dev *dev);
|
||||
int yflash_MarkNANDBlockBad(struct yaffs_dev *dev, int block_no);
|
||||
int yflash_QueryNANDBlock(struct yaffs_dev *dev, int block_no,
|
||||
enum yaffs_block_state *state, u32 *seq_number);
|
||||
|
||||
#endif
|
35
flight/pios/common/libraries/yaffs2/inc/yaffs_flashif2.h
Normal file
35
flight/pios/common/libraries/yaffs2/inc/yaffs_flashif2.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_FLASH2_H__
|
||||
#define __YAFFS_FLASH2_H__
|
||||
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
int yflash2_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber);
|
||||
int yflash2_WriteChunkToNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, const struct yaffs_spare *spare);
|
||||
int yflash2_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, const struct yaffs_ext_tags *tags);
|
||||
int yflash2_ReadChunkFromNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, struct yaffs_spare *spare);
|
||||
int yflash2_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, struct yaffs_ext_tags *tags);
|
||||
int yflash2_InitialiseNAND(struct yaffs_dev *dev);
|
||||
int yflash2_MarkNANDBlockBad(struct yaffs_dev *dev, int block_no);
|
||||
int yflash2_QueryNANDBlock(struct yaffs_dev *dev, int block_no,
|
||||
enum yaffs_block_state *state, u32 *seq_number);
|
||||
|
||||
#endif
|
35
flight/pios/common/libraries/yaffs2/inc/yaffs_getblockinfo.h
Normal file
35
flight/pios/common/libraries/yaffs2/inc/yaffs_getblockinfo.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_GETBLOCKINFO_H__
|
||||
#define __YAFFS_GETBLOCKINFO_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
/* Function to manipulate block info */
|
||||
static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev
|
||||
*dev, int blk)
|
||||
{
|
||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>> yaffs: get_block_info block %d is not valid",
|
||||
blk);
|
||||
BUG();
|
||||
}
|
||||
return &dev->block_info[blk - dev->internal_start_block];
|
||||
}
|
||||
|
||||
#endif
|
1010
flight/pios/common/libraries/yaffs2/inc/yaffs_guts.h
Normal file
1010
flight/pios/common/libraries/yaffs2/inc/yaffs_guts.h
Normal file
@ -0,0 +1,1010 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_GUTS_H__
|
||||
#define __YAFFS_GUTS_H__
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
#define YAFFS_OK 1
|
||||
#define YAFFS_FAIL 0
|
||||
|
||||
/* Give us a Y=0x59,
|
||||
* Give us an A=0x41,
|
||||
* Give us an FF=0xff
|
||||
* Give us an S=0x53
|
||||
* And what have we got...
|
||||
*/
|
||||
#define YAFFS_MAGIC 0x5941ff53
|
||||
|
||||
/*
|
||||
* Tnodes form a tree with the tnodes in "levels"
|
||||
* Levels greater than 0 hold 8 slots which point to other tnodes.
|
||||
* Those at level 0 hold 16 slots which point to chunks in NAND.
|
||||
*
|
||||
* A maximum level of 8 thust supports files of size up to:
|
||||
*
|
||||
* 2^(3*MAX_LEVEL+4)
|
||||
*
|
||||
* Thus a max level of 8 supports files with up to 2^^28 chunks which gives
|
||||
* a maximum file size of around 512Gbytees with 2k chunks.
|
||||
*/
|
||||
#define YAFFS_NTNODES_LEVEL0 16
|
||||
#define YAFFS_TNODES_LEVEL0_BITS 4
|
||||
#define YAFFS_TNODES_LEVEL0_MASK 0xf
|
||||
|
||||
#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2)
|
||||
#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1)
|
||||
#define YAFFS_TNODES_INTERNAL_MASK 0x7
|
||||
#define YAFFS_TNODES_MAX_LEVEL 8
|
||||
#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \
|
||||
YAFFS_TNODES_INTERNAL_BITS * \
|
||||
YAFFS_TNODES_MAX_LEVEL)
|
||||
#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1)
|
||||
|
||||
#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff
|
||||
|
||||
/* Constants for YAFFS1 mode */
|
||||
#define YAFFS_BYTES_PER_SPARE 16
|
||||
#define YAFFS_BYTES_PER_CHUNK 512
|
||||
#define YAFFS_CHUNK_SIZE_SHIFT 9
|
||||
#define YAFFS_CHUNKS_PER_BLOCK 32
|
||||
#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
|
||||
|
||||
#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024
|
||||
#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32
|
||||
|
||||
|
||||
|
||||
#define YAFFS_ALLOCATION_NOBJECTS 100
|
||||
#define YAFFS_ALLOCATION_NTNODES 100
|
||||
#define YAFFS_ALLOCATION_NLINKS 100
|
||||
|
||||
#define YAFFS_NOBJECT_BUCKETS 256
|
||||
|
||||
#define YAFFS_OBJECT_SPACE 0x40000
|
||||
#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1)
|
||||
|
||||
/* Binary data version stamps */
|
||||
#define YAFFS_SUMMARY_VERSION 1
|
||||
#define YAFFS_CHECKPOINT_VERSION 7
|
||||
|
||||
#ifdef CONFIG_YAFFS_UNICODE
|
||||
#define YAFFS_MAX_NAME_LENGTH 127
|
||||
#define YAFFS_MAX_ALIAS_LENGTH 79
|
||||
#else
|
||||
#define YAFFS_MAX_NAME_LENGTH 255
|
||||
#define YAFFS_MAX_ALIAS_LENGTH 159
|
||||
#endif
|
||||
|
||||
#define YAFFS_SHORT_NAME_LENGTH 15
|
||||
|
||||
/* Some special object ids for pseudo objects */
|
||||
#define YAFFS_OBJECTID_ROOT 1
|
||||
#define YAFFS_OBJECTID_LOSTNFOUND 2
|
||||
#define YAFFS_OBJECTID_UNLINKED 3
|
||||
#define YAFFS_OBJECTID_DELETED 4
|
||||
|
||||
/* Fake object Id for summary data */
|
||||
#define YAFFS_OBJECTID_SUMMARY 0x10
|
||||
|
||||
/* Pseudo object ids for checkpointing */
|
||||
#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20
|
||||
#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21
|
||||
|
||||
#define YAFFS_MAX_SHORT_OP_CACHES 20
|
||||
|
||||
#define YAFFS_N_TEMP_BUFFERS 6
|
||||
|
||||
/* We limit the number attempts at sucessfully saving a chunk of data.
|
||||
* Small-page devices have 32 pages per block; large-page devices have 64.
|
||||
* Default to something in the order of 5 to 10 blocks worth of chunks.
|
||||
*/
|
||||
#define YAFFS_WR_ATTEMPTS (5*64)
|
||||
|
||||
/* Sequence numbers are used in YAFFS2 to determine block allocation order.
|
||||
* The range is limited slightly to help distinguish bad numbers from good.
|
||||
* This also allows us to perhaps in the future use special numbers for
|
||||
* special purposes.
|
||||
* EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years,
|
||||
* and is a larger number than the lifetime of a 2GB device.
|
||||
*/
|
||||
#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000
|
||||
#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00
|
||||
|
||||
/* Special sequence number for bad block that failed to be marked bad */
|
||||
#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000
|
||||
|
||||
/* ChunkCache is used for short read/write operations.*/
|
||||
struct yaffs_cache {
|
||||
struct yaffs_obj *object;
|
||||
int chunk_id;
|
||||
int last_use;
|
||||
int dirty;
|
||||
int n_bytes; /* Only valid if the cache is dirty */
|
||||
int locked; /* Can't push out or flush while locked. */
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
/* yaffs1 tags structures in RAM
|
||||
* NB This uses bitfield. Bitfields should not straddle a u32 boundary
|
||||
* otherwise the structure size will get blown out.
|
||||
*/
|
||||
|
||||
struct yaffs_tags {
|
||||
u32 chunk_id:20;
|
||||
u32 serial_number:2;
|
||||
u32 n_bytes_lsb:10;
|
||||
u32 obj_id:18;
|
||||
u32 ecc:12;
|
||||
u32 n_bytes_msb:2;
|
||||
};
|
||||
|
||||
union yaffs_tags_union {
|
||||
struct yaffs_tags as_tags;
|
||||
u8 as_bytes[8];
|
||||
};
|
||||
|
||||
|
||||
/* Stuff used for extended tags in YAFFS2 */
|
||||
|
||||
enum yaffs_ecc_result {
|
||||
YAFFS_ECC_RESULT_UNKNOWN,
|
||||
YAFFS_ECC_RESULT_NO_ERROR,
|
||||
YAFFS_ECC_RESULT_FIXED,
|
||||
YAFFS_ECC_RESULT_UNFIXED
|
||||
};
|
||||
|
||||
enum yaffs_obj_type {
|
||||
YAFFS_OBJECT_TYPE_UNKNOWN,
|
||||
YAFFS_OBJECT_TYPE_FILE,
|
||||
YAFFS_OBJECT_TYPE_SYMLINK,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY,
|
||||
YAFFS_OBJECT_TYPE_HARDLINK,
|
||||
YAFFS_OBJECT_TYPE_SPECIAL
|
||||
};
|
||||
|
||||
#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL
|
||||
|
||||
struct yaffs_ext_tags {
|
||||
unsigned chunk_used; /* Status of the chunk: used or unused */
|
||||
unsigned obj_id; /* If 0 this is not used */
|
||||
unsigned chunk_id; /* If 0 this is a header, else a data chunk */
|
||||
unsigned n_bytes; /* Only valid for data chunks */
|
||||
|
||||
/* The following stuff only has meaning when we read */
|
||||
enum yaffs_ecc_result ecc_result;
|
||||
unsigned block_bad;
|
||||
|
||||
/* YAFFS 1 stuff */
|
||||
unsigned is_deleted; /* The chunk is marked deleted */
|
||||
unsigned serial_number; /* Yaffs1 2-bit serial number */
|
||||
|
||||
/* YAFFS2 stuff */
|
||||
unsigned seq_number; /* The sequence number of this block */
|
||||
|
||||
/* Extra info if this is an object header (YAFFS2 only) */
|
||||
|
||||
unsigned extra_available; /* Extra info available if not zero */
|
||||
unsigned extra_parent_id; /* The parent object */
|
||||
unsigned extra_is_shrink; /* Is it a shrink header? */
|
||||
unsigned extra_shadows; /* Does this shadow another object? */
|
||||
|
||||
enum yaffs_obj_type extra_obj_type; /* What object type? */
|
||||
|
||||
Y_LOFF_T extra_file_size; /* Length if it is a file */
|
||||
unsigned extra_equiv_id; /* Equivalent object for a hard link */
|
||||
};
|
||||
|
||||
/* Spare structure for YAFFS1 */
|
||||
struct yaffs_spare {
|
||||
u8 tb0;
|
||||
u8 tb1;
|
||||
u8 tb2;
|
||||
u8 tb3;
|
||||
u8 page_status; /* set to 0 to delete the chunk */
|
||||
u8 block_status;
|
||||
u8 tb4;
|
||||
u8 tb5;
|
||||
u8 ecc1[3];
|
||||
u8 tb6;
|
||||
u8 tb7;
|
||||
u8 ecc2[3];
|
||||
};
|
||||
|
||||
/*Special structure for passing through to mtd */
|
||||
struct yaffs_nand_spare {
|
||||
struct yaffs_spare spare;
|
||||
int eccres1;
|
||||
int eccres2;
|
||||
};
|
||||
|
||||
/* Block data in RAM */
|
||||
|
||||
enum yaffs_block_state {
|
||||
YAFFS_BLOCK_STATE_UNKNOWN = 0,
|
||||
|
||||
YAFFS_BLOCK_STATE_SCANNING,
|
||||
/* Being scanned */
|
||||
|
||||
YAFFS_BLOCK_STATE_NEEDS_SCAN,
|
||||
/* The block might have something on it (ie it is allocating or full,
|
||||
* perhaps empty) but it needs to be scanned to determine its true
|
||||
* state.
|
||||
* This state is only valid during scanning.
|
||||
* NB We tolerate empty because the pre-scanner might be incapable of
|
||||
* deciding
|
||||
* However, if this state is returned on a YAFFS2 device,
|
||||
* then we expect a sequence number
|
||||
*/
|
||||
|
||||
YAFFS_BLOCK_STATE_EMPTY,
|
||||
/* This block is empty */
|
||||
|
||||
YAFFS_BLOCK_STATE_ALLOCATING,
|
||||
/* This block is partially allocated.
|
||||
* At least one page holds valid data.
|
||||
* This is the one currently being used for page
|
||||
* allocation. Should never be more than one of these.
|
||||
* If a block is only partially allocated at mount it is treated as
|
||||
* full.
|
||||
*/
|
||||
|
||||
YAFFS_BLOCK_STATE_FULL,
|
||||
/* All the pages in this block have been allocated.
|
||||
* If a block was only partially allocated when mounted we treat
|
||||
* it as fully allocated.
|
||||
*/
|
||||
|
||||
YAFFS_BLOCK_STATE_DIRTY,
|
||||
/* The block was full and now all chunks have been deleted.
|
||||
* Erase me, reuse me.
|
||||
*/
|
||||
|
||||
YAFFS_BLOCK_STATE_CHECKPOINT,
|
||||
/* This block is assigned to holding checkpoint data. */
|
||||
|
||||
YAFFS_BLOCK_STATE_COLLECTING,
|
||||
/* This block is being garbage collected */
|
||||
|
||||
YAFFS_BLOCK_STATE_DEAD
|
||||
/* This block has failed and is not in use */
|
||||
};
|
||||
|
||||
#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1)
|
||||
|
||||
struct yaffs_block_info {
|
||||
|
||||
s32 soft_del_pages:10; /* number of soft deleted pages */
|
||||
s32 pages_in_use:10; /* number of pages in use */
|
||||
u32 block_state:4; /* One of the above block states. */
|
||||
/* NB use unsigned because enum is sometimes
|
||||
* an int */
|
||||
u32 needs_retiring:1; /* Data has failed on this block, */
|
||||
/*need to get valid data off and retire*/
|
||||
u32 skip_erased_check:1;/* Skip the erased check on this block */
|
||||
u32 gc_prioritise:1; /* An ECC check or blank check has failed.
|
||||
Block should be prioritised for GC */
|
||||
u32 chunk_error_strikes:3; /* How many times we've had ecc etc
|
||||
failures on this block and tried to reuse it */
|
||||
u32 has_summary:1; /* The block has a summary */
|
||||
|
||||
u32 has_shrink_hdr:1; /* This block has at least one shrink header */
|
||||
u32 seq_number; /* block sequence number for yaffs2 */
|
||||
|
||||
};
|
||||
|
||||
/* -------------------------- Object structure -------------------------------*/
|
||||
/* This is the object structure as stored on NAND */
|
||||
|
||||
struct yaffs_obj_hdr {
|
||||
enum yaffs_obj_type type;
|
||||
|
||||
/* Apply to everything */
|
||||
int parent_obj_id;
|
||||
u16 sum_no_longer_used; /* checksum of name. No longer used */
|
||||
YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
|
||||
/* The following apply to all object types except for hard links */
|
||||
u32 yst_mode; /* protection */
|
||||
|
||||
u32 yst_uid;
|
||||
u32 yst_gid;
|
||||
u32 yst_atime;
|
||||
u32 yst_mtime;
|
||||
u32 yst_ctime;
|
||||
|
||||
/* File size applies to files only */
|
||||
u32 file_size_low;
|
||||
|
||||
/* Equivalent object id applies to hard links only. */
|
||||
int equiv_id;
|
||||
|
||||
/* Alias is for symlinks only. */
|
||||
YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1];
|
||||
|
||||
u32 yst_rdev; /* stuff for block and char devices (major/min) */
|
||||
|
||||
u32 win_ctime[2];
|
||||
u32 win_atime[2];
|
||||
u32 win_mtime[2];
|
||||
|
||||
u32 inband_shadowed_obj_id;
|
||||
u32 inband_is_shrink;
|
||||
|
||||
u32 file_size_high;
|
||||
u32 reserved[1];
|
||||
int shadows_obj; /* This object header shadows the
|
||||
specified object if > 0 */
|
||||
|
||||
/* is_shrink applies to object headers written when wemake a hole. */
|
||||
u32 is_shrink;
|
||||
|
||||
};
|
||||
|
||||
/*--------------------------- Tnode -------------------------- */
|
||||
|
||||
struct yaffs_tnode {
|
||||
struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL];
|
||||
};
|
||||
|
||||
/*------------------------ Object -----------------------------*/
|
||||
/* An object can be one of:
|
||||
* - a directory (no data, has children links
|
||||
* - a regular file (data.... not prunes :->).
|
||||
* - a symlink [symbolic link] (the alias).
|
||||
* - a hard link
|
||||
*/
|
||||
|
||||
struct yaffs_file_var {
|
||||
Y_LOFF_T file_size;
|
||||
Y_LOFF_T scanned_size;
|
||||
Y_LOFF_T shrink_size;
|
||||
int top_level;
|
||||
struct yaffs_tnode *top;
|
||||
};
|
||||
|
||||
struct yaffs_dir_var {
|
||||
struct list_head children; /* list of child links */
|
||||
struct list_head dirty; /* Entry for list of dirty directories */
|
||||
};
|
||||
|
||||
struct yaffs_symlink_var {
|
||||
YCHAR *alias;
|
||||
};
|
||||
|
||||
struct yaffs_hardlink_var {
|
||||
struct yaffs_obj *equiv_obj;
|
||||
u32 equiv_id;
|
||||
};
|
||||
|
||||
union yaffs_obj_var {
|
||||
struct yaffs_file_var file_variant;
|
||||
struct yaffs_dir_var dir_variant;
|
||||
struct yaffs_symlink_var symlink_variant;
|
||||
struct yaffs_hardlink_var hardlink_variant;
|
||||
};
|
||||
|
||||
struct yaffs_obj {
|
||||
u8 deleted:1; /* This should only apply to unlinked files. */
|
||||
u8 soft_del:1; /* it has also been soft deleted */
|
||||
u8 unlinked:1; /* An unlinked file.*/
|
||||
u8 fake:1; /* A fake object has no presence on NAND. */
|
||||
u8 rename_allowed:1; /* Some objects cannot be renamed. */
|
||||
u8 unlink_allowed:1;
|
||||
u8 dirty:1; /* the object needs to be written to flash */
|
||||
u8 valid:1; /* When the file system is being loaded up, this
|
||||
* object might be created before the data
|
||||
* is available
|
||||
* ie. file data chunks encountered before
|
||||
* the header.
|
||||
*/
|
||||
u8 lazy_loaded:1; /* This object has been lazy loaded and
|
||||
* is missing some detail */
|
||||
|
||||
u8 defered_free:1; /* Object is removed from NAND, but is
|
||||
* still in the inode cache.
|
||||
* Free of object is defered.
|
||||
* until the inode is released.
|
||||
*/
|
||||
u8 being_created:1; /* This object is still being created
|
||||
* so skip some verification checks. */
|
||||
u8 is_shadowed:1; /* This object is shadowed on the way
|
||||
* to being renamed. */
|
||||
|
||||
u8 xattr_known:1; /* We know if this has object has xattribs
|
||||
* or not. */
|
||||
u8 has_xattr:1; /* This object has xattribs.
|
||||
* Only valid if xattr_known. */
|
||||
|
||||
u8 serial; /* serial number of chunk in NAND.*/
|
||||
u16 sum; /* sum of the name to speed searching */
|
||||
|
||||
struct yaffs_dev *my_dev; /* The device I'm on */
|
||||
|
||||
struct list_head hash_link; /* list of objects in hash bucket */
|
||||
|
||||
struct list_head hard_links; /* hard linked object chain*/
|
||||
|
||||
/* directory structure stuff */
|
||||
/* also used for linking up the free list */
|
||||
struct yaffs_obj *parent;
|
||||
struct list_head siblings;
|
||||
|
||||
/* Where's my object header in NAND? */
|
||||
int hdr_chunk;
|
||||
|
||||
int n_data_chunks; /* Number of data chunks for this file. */
|
||||
|
||||
u32 obj_id; /* the object id value */
|
||||
|
||||
u32 yst_mode;
|
||||
|
||||
YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1];
|
||||
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
u32 win_ctime[2];
|
||||
u32 win_mtime[2];
|
||||
u32 win_atime[2];
|
||||
#else
|
||||
u32 yst_uid;
|
||||
u32 yst_gid;
|
||||
u32 yst_atime;
|
||||
u32 yst_mtime;
|
||||
u32 yst_ctime;
|
||||
#endif
|
||||
|
||||
u32 yst_rdev;
|
||||
|
||||
void *my_inode;
|
||||
|
||||
enum yaffs_obj_type variant_type;
|
||||
|
||||
union yaffs_obj_var variant;
|
||||
|
||||
};
|
||||
|
||||
struct yaffs_obj_bucket {
|
||||
struct list_head list;
|
||||
int count;
|
||||
};
|
||||
|
||||
/* yaffs_checkpt_obj holds the definition of an object as dumped
|
||||
* by checkpointing.
|
||||
*/
|
||||
|
||||
struct yaffs_checkpt_obj {
|
||||
int struct_type;
|
||||
u32 obj_id;
|
||||
u32 parent_id;
|
||||
int hdr_chunk;
|
||||
enum yaffs_obj_type variant_type:3;
|
||||
u8 deleted:1;
|
||||
u8 soft_del:1;
|
||||
u8 unlinked:1;
|
||||
u8 fake:1;
|
||||
u8 rename_allowed:1;
|
||||
u8 unlink_allowed:1;
|
||||
u8 serial;
|
||||
int n_data_chunks;
|
||||
Y_LOFF_T size_or_equiv_obj;
|
||||
};
|
||||
|
||||
/*--------------------- Temporary buffers ----------------
|
||||
*
|
||||
* These are chunk-sized working buffers. Each device has a few.
|
||||
*/
|
||||
|
||||
struct yaffs_buffer {
|
||||
u8 *buffer;
|
||||
int in_use;
|
||||
};
|
||||
|
||||
/*----------------- Device ---------------------------------*/
|
||||
|
||||
struct yaffs_param {
|
||||
const YCHAR *name;
|
||||
|
||||
/*
|
||||
* Entry parameters set up way early. Yaffs sets up the rest.
|
||||
* The structure should be zeroed out before use so that unused
|
||||
* and default values are zero.
|
||||
*/
|
||||
|
||||
int inband_tags; /* Use unband tags */
|
||||
u32 total_bytes_per_chunk; /* Should be >= 512, does not need to
|
||||
be a power of 2 */
|
||||
int chunks_per_block; /* does not need to be a power of 2 */
|
||||
int spare_bytes_per_chunk; /* spare area size */
|
||||
int start_block; /* Start block we're allowed to use */
|
||||
int end_block; /* End block we're allowed to use */
|
||||
int n_reserved_blocks; /* Tuneable so that we can reduce
|
||||
* reserved blocks on NOR and RAM. */
|
||||
|
||||
int n_caches; /* If <= 0, then short op caching is disabled,
|
||||
* else the number of short op caches.
|
||||
*/
|
||||
int cache_bypass_aligned; /* If non-zero then bypass the cache for
|
||||
* aligned writes.
|
||||
*/
|
||||
|
||||
int use_nand_ecc; /* Flag to decide whether or not to use
|
||||
* NAND driver ECC on data (yaffs1) */
|
||||
int tags_9bytes; /* Use 9 byte tags */
|
||||
int no_tags_ecc; /* Flag to decide whether or not to do ECC
|
||||
* on packed tags (yaffs2) */
|
||||
|
||||
int is_yaffs2; /* Use yaffs2 mode on this device */
|
||||
|
||||
int empty_lost_n_found; /* Auto-empty lost+found directory on mount */
|
||||
|
||||
int refresh_period; /* How often to check for a block refresh */
|
||||
|
||||
/* Checkpoint control. Can be set before or after initialisation */
|
||||
u8 skip_checkpt_rd;
|
||||
u8 skip_checkpt_wr;
|
||||
|
||||
int enable_xattr; /* Enable xattribs */
|
||||
|
||||
int max_objects; /*
|
||||
* Set to limit the number of objects created.
|
||||
* 0 = no limit.
|
||||
*/
|
||||
|
||||
/* The remove_obj_fn function must be supplied by OS flavours that
|
||||
* need it.
|
||||
* yaffs direct uses it to implement the faster readdir.
|
||||
* Linux uses it to protect the directory during unlocking.
|
||||
*/
|
||||
void (*remove_obj_fn) (struct yaffs_obj *obj);
|
||||
|
||||
/* Callback to mark the superblock dirty */
|
||||
void (*sb_dirty_fn) (struct yaffs_dev *dev);
|
||||
|
||||
/* Callback to control garbage collection. */
|
||||
unsigned (*gc_control_fn) (struct yaffs_dev *dev);
|
||||
|
||||
/* Debug control flags. Don't use unless you know what you're doing */
|
||||
int use_header_file_size; /* Flag to determine if we should use
|
||||
* file sizes from the header */
|
||||
int disable_lazy_load; /* Disable lazy loading on this device */
|
||||
int wide_tnodes_disabled; /* Set to disable wide tnodes */
|
||||
int disable_soft_del; /* yaffs 1 only: Set to disable the use of
|
||||
* softdeletion. */
|
||||
|
||||
int defered_dir_update; /* Set to defer directory updates */
|
||||
|
||||
#ifdef CONFIG_YAFFS_AUTO_UNICODE
|
||||
int auto_unicode;
|
||||
#endif
|
||||
int always_check_erased; /* Force chunk erased check always on */
|
||||
|
||||
int disable_summary;
|
||||
int disable_bad_block_marking;
|
||||
|
||||
};
|
||||
|
||||
struct yaffs_driver {
|
||||
int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, int data_len,
|
||||
const u8 *oob, int oob_len);
|
||||
int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, int data_len,
|
||||
u8 *oob, int oob_len,
|
||||
enum yaffs_ecc_result *ecc_result);
|
||||
int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no);
|
||||
int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no);
|
||||
int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no);
|
||||
int (*drv_initialise_fn) (struct yaffs_dev *dev);
|
||||
int (*drv_deinitialise_fn) (struct yaffs_dev *dev);
|
||||
};
|
||||
|
||||
struct yaffs_tags_handler {
|
||||
int (*write_chunk_tags_fn) (struct yaffs_dev *dev,
|
||||
int nand_chunk, const u8 *data,
|
||||
const struct yaffs_ext_tags *tags);
|
||||
int (*read_chunk_tags_fn) (struct yaffs_dev *dev,
|
||||
int nand_chunk, u8 *data,
|
||||
struct yaffs_ext_tags *tags);
|
||||
|
||||
int (*query_block_fn) (struct yaffs_dev *dev, int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number);
|
||||
int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no);
|
||||
};
|
||||
|
||||
struct yaffs_dev {
|
||||
struct yaffs_param param;
|
||||
struct yaffs_driver drv;
|
||||
struct yaffs_tags_handler tagger;
|
||||
|
||||
/* Context storage. Holds extra OS specific data for this device */
|
||||
|
||||
void *os_context;
|
||||
void *driver_context;
|
||||
|
||||
struct list_head dev_list;
|
||||
|
||||
int ll_init;
|
||||
/* Runtime parameters. Set up by YAFFS. */
|
||||
int data_bytes_per_chunk;
|
||||
|
||||
/* Non-wide tnode stuff */
|
||||
u16 chunk_grp_bits; /* Number of bits that need to be resolved if
|
||||
* the tnodes are not wide enough.
|
||||
*/
|
||||
u16 chunk_grp_size; /* == 2^^chunk_grp_bits */
|
||||
|
||||
/* Stuff to support wide tnodes */
|
||||
u32 tnode_width;
|
||||
u32 tnode_mask;
|
||||
u32 tnode_size;
|
||||
|
||||
/* Stuff for figuring out file offset to chunk conversions */
|
||||
u32 chunk_shift; /* Shift value */
|
||||
u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */
|
||||
u32 chunk_mask; /* Mask to use for power-of-2 case */
|
||||
|
||||
int is_mounted;
|
||||
int read_only;
|
||||
int is_checkpointed;
|
||||
|
||||
/* Stuff to support block offsetting to support start block zero */
|
||||
int internal_start_block;
|
||||
int internal_end_block;
|
||||
int block_offset;
|
||||
int chunk_offset;
|
||||
|
||||
/* Runtime checkpointing stuff */
|
||||
int checkpt_page_seq; /* running sequence number of checkpt pages */
|
||||
int checkpt_byte_count;
|
||||
int checkpt_byte_offs;
|
||||
u8 *checkpt_buffer;
|
||||
int checkpt_open_write;
|
||||
int blocks_in_checkpt;
|
||||
int checkpt_cur_chunk;
|
||||
int checkpt_cur_block;
|
||||
int checkpt_next_block;
|
||||
int *checkpt_block_list;
|
||||
int checkpt_max_blocks;
|
||||
u32 checkpt_sum;
|
||||
u32 checkpt_xor;
|
||||
|
||||
int checkpoint_blocks_required; /* Number of blocks needed to store
|
||||
* current checkpoint set */
|
||||
|
||||
/* Block Info */
|
||||
struct yaffs_block_info *block_info;
|
||||
u8 *chunk_bits; /* bitmap of chunks in use */
|
||||
u8 block_info_alt:1; /* allocated using alternative alloc */
|
||||
u8 chunk_bits_alt:1; /* allocated using alternative alloc */
|
||||
int chunk_bit_stride; /* Number of bytes of chunk_bits per block.
|
||||
* Must be consistent with chunks_per_block.
|
||||
*/
|
||||
|
||||
int n_erased_blocks;
|
||||
int alloc_block; /* Current block being allocated off */
|
||||
u32 alloc_page;
|
||||
int alloc_block_finder; /* Used to search for next allocation block */
|
||||
|
||||
/* Object and Tnode memory management */
|
||||
void *allocator;
|
||||
int n_obj;
|
||||
int n_tnodes;
|
||||
|
||||
int n_hardlinks;
|
||||
|
||||
struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS];
|
||||
u32 bucket_finder;
|
||||
|
||||
int n_free_chunks;
|
||||
|
||||
/* Garbage collection control */
|
||||
u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */
|
||||
u32 n_clean_ups;
|
||||
|
||||
unsigned has_pending_prioritised_gc; /* We think this device might
|
||||
have pending prioritised gcs */
|
||||
unsigned gc_disable;
|
||||
unsigned gc_block_finder;
|
||||
unsigned gc_dirtiest;
|
||||
unsigned gc_pages_in_use;
|
||||
unsigned gc_not_done;
|
||||
unsigned gc_block;
|
||||
unsigned gc_chunk;
|
||||
unsigned gc_skip;
|
||||
struct yaffs_summary_tags *gc_sum_tags;
|
||||
|
||||
/* Special directories */
|
||||
struct yaffs_obj *root_dir;
|
||||
struct yaffs_obj *lost_n_found;
|
||||
|
||||
int buffered_block; /* Which block is buffered here? */
|
||||
int doing_buffered_block_rewrite;
|
||||
|
||||
struct yaffs_cache *cache;
|
||||
int cache_last_use;
|
||||
|
||||
/* Stuff for background deletion and unlinked files. */
|
||||
struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted
|
||||
files live. */
|
||||
struct yaffs_obj *del_dir; /* Directory where deleted objects are
|
||||
sent to disappear. */
|
||||
struct yaffs_obj *unlinked_deletion; /* Current file being
|
||||
background deleted. */
|
||||
int n_deleted_files; /* Count of files awaiting deletion; */
|
||||
int n_unlinked_files; /* Count of unlinked files. */
|
||||
int n_bg_deletions; /* Count of background deletions. */
|
||||
|
||||
/* Temporary buffer management */
|
||||
struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS];
|
||||
int max_temp;
|
||||
int temp_in_use;
|
||||
int unmanaged_buffer_allocs;
|
||||
int unmanaged_buffer_deallocs;
|
||||
|
||||
/* yaffs2 runtime stuff */
|
||||
unsigned seq_number; /* Sequence number of currently
|
||||
allocating block */
|
||||
unsigned oldest_dirty_seq;
|
||||
unsigned oldest_dirty_block;
|
||||
|
||||
/* Block refreshing */
|
||||
int refresh_skip; /* A skip down counter.
|
||||
* Refresh happens when this gets to zero. */
|
||||
|
||||
/* Dirty directory handling */
|
||||
struct list_head dirty_dirs; /* List of dirty directories */
|
||||
|
||||
/* Summary */
|
||||
int chunks_per_summary;
|
||||
struct yaffs_summary_tags *sum_tags;
|
||||
|
||||
/* Statistics */
|
||||
u32 n_page_writes;
|
||||
u32 n_page_reads;
|
||||
u32 n_erasures;
|
||||
u32 n_bad_queries;
|
||||
u32 n_bad_markings;
|
||||
u32 n_erase_failures;
|
||||
u32 n_gc_copies;
|
||||
u32 all_gcs;
|
||||
u32 passive_gc_count;
|
||||
u32 oldest_dirty_gc_count;
|
||||
u32 n_gc_blocks;
|
||||
u32 bg_gcs;
|
||||
u32 n_retried_writes;
|
||||
u32 n_retired_blocks;
|
||||
u32 n_ecc_fixed;
|
||||
u32 n_ecc_unfixed;
|
||||
u32 n_tags_ecc_fixed;
|
||||
u32 n_tags_ecc_unfixed;
|
||||
u32 n_deletions;
|
||||
u32 n_unmarked_deletions;
|
||||
u32 refresh_count;
|
||||
u32 cache_hits;
|
||||
u32 tags_used;
|
||||
u32 summary_used;
|
||||
|
||||
};
|
||||
|
||||
/* The CheckpointDevice structure holds the device information that changes
|
||||
*at runtime and must be preserved over unmount/mount cycles.
|
||||
*/
|
||||
struct yaffs_checkpt_dev {
|
||||
int struct_type;
|
||||
int n_erased_blocks;
|
||||
int alloc_block; /* Current block being allocated off */
|
||||
u32 alloc_page;
|
||||
int n_free_chunks;
|
||||
|
||||
int n_deleted_files; /* Count of files awaiting deletion; */
|
||||
int n_unlinked_files; /* Count of unlinked files. */
|
||||
int n_bg_deletions; /* Count of background deletions. */
|
||||
|
||||
/* yaffs2 runtime stuff */
|
||||
unsigned seq_number; /* Sequence number of currently
|
||||
* allocating block */
|
||||
|
||||
};
|
||||
|
||||
struct yaffs_checkpt_validity {
|
||||
int struct_type;
|
||||
u32 magic;
|
||||
u32 version;
|
||||
u32 head;
|
||||
};
|
||||
|
||||
struct yaffs_shadow_fixer {
|
||||
int obj_id;
|
||||
int shadowed_id;
|
||||
struct yaffs_shadow_fixer *next;
|
||||
};
|
||||
|
||||
/* Structure for doing xattr modifications */
|
||||
struct yaffs_xattr_mod {
|
||||
int set; /* If 0 then this is a deletion */
|
||||
const YCHAR *name;
|
||||
const void *data;
|
||||
int size;
|
||||
int flags;
|
||||
int result;
|
||||
};
|
||||
|
||||
/*----------------------- YAFFS Functions -----------------------*/
|
||||
|
||||
int yaffs_guts_initialise(struct yaffs_dev *dev);
|
||||
void yaffs_deinitialise(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_get_n_free_chunks(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name,
|
||||
struct yaffs_obj *new_dir, const YCHAR * new_name);
|
||||
|
||||
int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name);
|
||||
int yaffs_del_obj(struct yaffs_obj *obj);
|
||||
struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj,
|
||||
enum yaffs_obj_type type);
|
||||
|
||||
|
||||
int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size);
|
||||
Y_LOFF_T yaffs_get_obj_length(struct yaffs_obj *obj);
|
||||
int yaffs_get_obj_inode(struct yaffs_obj *obj);
|
||||
unsigned yaffs_get_obj_type(struct yaffs_obj *obj);
|
||||
int yaffs_get_obj_link_count(struct yaffs_obj *obj);
|
||||
|
||||
/* File operations */
|
||||
int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, Y_LOFF_T offset,
|
||||
int n_bytes);
|
||||
int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, Y_LOFF_T offset,
|
||||
int n_bytes, int write_trhrough);
|
||||
int yaffs_resize_file(struct yaffs_obj *obj, Y_LOFF_T new_size);
|
||||
|
||||
struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid);
|
||||
|
||||
int yaffs_flush_file(struct yaffs_obj *in,
|
||||
int update_time,
|
||||
int data_sync,
|
||||
int discard_cache);
|
||||
|
||||
/* Flushing and checkpointing */
|
||||
void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard);
|
||||
|
||||
int yaffs_checkpoint_save(struct yaffs_dev *dev);
|
||||
int yaffs_checkpoint_restore(struct yaffs_dev *dev);
|
||||
|
||||
/* Directory operations */
|
||||
struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name,
|
||||
u32 mode, u32 uid, u32 gid);
|
||||
struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir,
|
||||
const YCHAR *name);
|
||||
struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number);
|
||||
|
||||
/* Link operations */
|
||||
struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name,
|
||||
struct yaffs_obj *equiv_obj);
|
||||
|
||||
struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj);
|
||||
|
||||
/* Symlink operations */
|
||||
struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid, const YCHAR *alias);
|
||||
YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj);
|
||||
|
||||
/* Special inodes (fifos, sockets and devices) */
|
||||
struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid, u32 rdev);
|
||||
|
||||
int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name,
|
||||
const void *value, int size, int flags);
|
||||
int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value,
|
||||
int size);
|
||||
int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size);
|
||||
int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name);
|
||||
|
||||
/* Special directories */
|
||||
struct yaffs_obj *yaffs_root(struct yaffs_dev *dev);
|
||||
struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev);
|
||||
|
||||
void yaffs_handle_defered_free(struct yaffs_obj *obj);
|
||||
|
||||
void yaffs_update_dirty_dirs(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency);
|
||||
|
||||
/* Debug dump */
|
||||
int yaffs_dump_obj(struct yaffs_obj *obj);
|
||||
|
||||
void yaffs_guts_test(struct yaffs_dev *dev);
|
||||
int yaffs_guts_ll_init(struct yaffs_dev *dev);
|
||||
|
||||
|
||||
/* A few useful functions to be used within the core files*/
|
||||
void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,
|
||||
int lyn);
|
||||
int yaffs_check_ff(u8 *buffer, int n_bytes);
|
||||
void yaffs_handle_chunk_error(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi);
|
||||
|
||||
u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev);
|
||||
void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer);
|
||||
|
||||
struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev,
|
||||
int number,
|
||||
enum yaffs_obj_type type);
|
||||
int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
|
||||
int nand_chunk, int in_scan);
|
||||
void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name);
|
||||
void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj,
|
||||
const struct yaffs_obj_hdr *oh);
|
||||
void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj);
|
||||
YCHAR *yaffs_clone_str(const YCHAR *str);
|
||||
void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list);
|
||||
void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no);
|
||||
int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name,
|
||||
int force, int is_shrink, int shadows,
|
||||
struct yaffs_xattr_mod *xop);
|
||||
void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id,
|
||||
int backward_scanning);
|
||||
int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks);
|
||||
struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev);
|
||||
struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct,
|
||||
u32 chunk_id,
|
||||
struct yaffs_tnode *passed_tn);
|
||||
|
||||
int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, Y_LOFF_T offset,
|
||||
int n_bytes, int write_trhrough);
|
||||
void yaffs_resize_file_down(struct yaffs_obj *obj, Y_LOFF_T new_size);
|
||||
void yaffs_skip_rest_of_block(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_count_free_chunks(struct yaffs_dev *dev);
|
||||
|
||||
struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct,
|
||||
u32 chunk_id);
|
||||
|
||||
u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn,
|
||||
unsigned pos);
|
||||
|
||||
int yaffs_is_non_empty_dir(struct yaffs_obj *obj);
|
||||
|
||||
int yaffs_guts_format_dev(struct yaffs_dev *dev);
|
||||
|
||||
void yaffs_addr_to_chunk(struct yaffs_dev *dev, Y_LOFF_T addr,
|
||||
int *chunk_out, u32 *offset_out);
|
||||
/*
|
||||
* Marshalling functions to get Y_LOFF_T file sizes into aand out of
|
||||
* object headers.
|
||||
*/
|
||||
void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, Y_LOFF_T fsize);
|
||||
Y_LOFF_T yaffs_oh_to_size(struct yaffs_obj_hdr *oh);
|
||||
Y_LOFF_T yaffs_max_file_size(struct yaffs_dev *dev);
|
||||
|
||||
/*
|
||||
* Debug function to count number of blocks in each state
|
||||
* NB Needs to be called with correct number of integers
|
||||
*/
|
||||
|
||||
void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]);
|
||||
|
||||
int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
|
||||
struct yaffs_ext_tags *tags);
|
||||
|
||||
#endif
|
24
flight/pios/common/libraries/yaffs2/inc/yaffs_hweight.h
Normal file
24
flight/pios/common/libraries/yaffs2/inc/yaffs_hweight.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_HWEIGHT_H__
|
||||
#define __YAFFS_HWEIGHT_H__
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
int yaffs_hweight8(u8 x);
|
||||
int yaffs_hweight32(u32 x);
|
||||
|
||||
#endif
|
126
flight/pios/common/libraries/yaffs2/inc/yaffs_list.h
Normal file
126
flight/pios/common/libraries/yaffs2/inc/yaffs_list.h
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is just holds extra declarations of macros that would normally
|
||||
* be providesd in the Linux kernel. These macros have been written from
|
||||
* scratch but are functionally equivalent to the Linux ones.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_LIST_H__
|
||||
#define __YAFFS_LIST_H__
|
||||
|
||||
|
||||
/*
|
||||
* This is a simple doubly linked list implementation that matches the
|
||||
* way the Linux kernel doubly linked list implementation works.
|
||||
*/
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next; /* next in chain */
|
||||
struct list_head *prev; /* previous in chain */
|
||||
};
|
||||
|
||||
|
||||
/* Initialise a static list */
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = { &(name), &(name)}
|
||||
|
||||
|
||||
|
||||
/* Initialise a list head to an empty list */
|
||||
#define INIT_LIST_HEAD(p) \
|
||||
do { \
|
||||
(p)->next = (p);\
|
||||
(p)->prev = (p); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Add an element to a list */
|
||||
static inline void list_add(struct list_head *new_entry,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct list_head *list_next = list->next;
|
||||
|
||||
list->next = new_entry;
|
||||
new_entry->prev = list;
|
||||
new_entry->next = list_next;
|
||||
list_next->prev = new_entry;
|
||||
|
||||
}
|
||||
|
||||
static inline void list_add_tail(struct list_head *new_entry,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct list_head *list_prev = list->prev;
|
||||
|
||||
list->prev = new_entry;
|
||||
new_entry->next = list;
|
||||
new_entry->prev = list_prev;
|
||||
list_prev->next = new_entry;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Take an element out of its current list, with or without
|
||||
* reinitialising the links.of the entry*/
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
struct list_head *list_next = entry->next;
|
||||
struct list_head *list_prev = entry->prev;
|
||||
|
||||
list_next->prev = list_prev;
|
||||
list_prev->next = list_next;
|
||||
|
||||
}
|
||||
|
||||
static inline void list_del_init(struct list_head *entry)
|
||||
{
|
||||
list_del(entry);
|
||||
entry->next = entry->prev = entry;
|
||||
}
|
||||
|
||||
|
||||
/* Test if the list is empty */
|
||||
static inline int list_empty(struct list_head *entry)
|
||||
{
|
||||
return (entry->next == entry);
|
||||
}
|
||||
|
||||
|
||||
/* list_entry takes a pointer to a list entry and offsets it to that
|
||||
* we can find a pointer to the object it is embedded in.
|
||||
*/
|
||||
|
||||
|
||||
#define list_entry(entry, type, member) \
|
||||
((type *)((char *)(entry)-(unsigned long)(&((type *)NULL)->member)))
|
||||
|
||||
|
||||
/* list_for_each and list_for_each_safe iterate over lists.
|
||||
* list_for_each_safe uses temporary storage to make the list delete safe
|
||||
*/
|
||||
|
||||
#define list_for_each(itervar, list) \
|
||||
for (itervar = (list)->next; itervar != (list); itervar = itervar->next)
|
||||
|
||||
#define list_for_each_safe(itervar, save_var, list) \
|
||||
for (itervar = (list)->next, save_var = (list)->next->next; \
|
||||
itervar != (list); \
|
||||
itervar = save_var, save_var = save_var->next)
|
||||
|
||||
|
||||
#endif
|
28
flight/pios/common/libraries/yaffs2/inc/yaffs_nameval.h
Normal file
28
flight/pios/common/libraries/yaffs2/inc/yaffs_nameval.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __NAMEVAL_H__
|
||||
#define __NAMEVAL_H__
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
int nval_del(char *xb, int xb_size, const YCHAR * name);
|
||||
int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf,
|
||||
int bsize, int flags);
|
||||
int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,
|
||||
int bsize);
|
||||
int nval_list(const char *xb, int xb_size, char *buf, int bsize);
|
||||
int nval_hasvalues(const char *xb, int xb_size);
|
||||
#endif
|
39
flight/pios/common/libraries/yaffs2/inc/yaffs_nand.h
Normal file
39
flight/pios/common/libraries/yaffs2/inc/yaffs_nand.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_NAND_H__
|
||||
#define __YAFFS_NAND_H__
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *buffer, struct yaffs_ext_tags *tags);
|
||||
|
||||
int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *buffer, struct yaffs_ext_tags *tags);
|
||||
|
||||
int yaffs_mark_bad(struct yaffs_dev *dev, int block_no);
|
||||
|
||||
int yaffs_query_init_block_state(struct yaffs_dev *dev,
|
||||
int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
unsigned *seq_number);
|
||||
|
||||
int yaffs_erase_block(struct yaffs_dev *dev, int flash_block);
|
||||
|
||||
int yaffs_init_nand(struct yaffs_dev *dev);
|
||||
int yaffs_deinit_nand(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
44
flight/pios/common/libraries/yaffs2/inc/yaffs_osglue.h
Normal file
44
flight/pios/common/libraries/yaffs2/inc/yaffs_osglue.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Header file for using yaffs in an application via
|
||||
* a direct interface.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YAFFS_OSGLUE_H__
|
||||
#define __YAFFS_OSGLUE_H__
|
||||
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
void yaffsfs_Lock(void);
|
||||
void yaffsfs_Unlock(void);
|
||||
|
||||
u32 yaffsfs_CurrentTime(void);
|
||||
|
||||
void yaffsfs_SetError(int err);
|
||||
|
||||
void *yaffsfs_malloc(size_t size);
|
||||
void yaffsfs_free(void *ptr);
|
||||
|
||||
int yaffsfs_CheckMemRegion(const void *addr, size_t size, int write_request);
|
||||
|
||||
void yaffsfs_OSInitialisation(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
47
flight/pios/common/libraries/yaffs2/inc/yaffs_packedtags2.h
Normal file
47
flight/pios/common/libraries/yaffs2/inc/yaffs_packedtags2.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/* This is used to pack YAFFS2 tags, not YAFFS1tags. */
|
||||
|
||||
#ifndef __YAFFS_PACKEDTAGS2_H__
|
||||
#define __YAFFS_PACKEDTAGS2_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_ecc.h"
|
||||
|
||||
struct yaffs_packed_tags2_tags_only {
|
||||
unsigned seq_number;
|
||||
unsigned obj_id;
|
||||
unsigned chunk_id;
|
||||
unsigned n_bytes;
|
||||
};
|
||||
|
||||
struct yaffs_packed_tags2 {
|
||||
struct yaffs_packed_tags2_tags_only t;
|
||||
struct yaffs_ecc_other ecc;
|
||||
};
|
||||
|
||||
/* Full packed tags with ECC, used for oob tags */
|
||||
void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,
|
||||
const struct yaffs_ext_tags *t, int tags_ecc);
|
||||
void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,
|
||||
int tags_ecc);
|
||||
|
||||
/* Only the tags part (no ECC for use with inband tags */
|
||||
void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt,
|
||||
const struct yaffs_ext_tags *t);
|
||||
void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,
|
||||
struct yaffs_packed_tags2_tags_only *pt);
|
||||
#endif
|
37
flight/pios/common/libraries/yaffs2/inc/yaffs_summary.h
Normal file
37
flight/pios/common/libraries/yaffs2/inc/yaffs_summary.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_SUMMARY_H__
|
||||
#define __YAFFS_SUMMARY_H__
|
||||
|
||||
#include "yaffs_packedtags2.h"
|
||||
|
||||
|
||||
int yaffs_summary_init(struct yaffs_dev *dev);
|
||||
void yaffs_summary_deinit(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_summary_add(struct yaffs_dev *dev,
|
||||
struct yaffs_ext_tags *tags,
|
||||
int chunk_in_block);
|
||||
int yaffs_summary_fetch(struct yaffs_dev *dev,
|
||||
struct yaffs_ext_tags *tags,
|
||||
int chunk_in_block);
|
||||
int yaffs_summary_read(struct yaffs_dev *dev,
|
||||
struct yaffs_summary_tags *st,
|
||||
int blk);
|
||||
void yaffs_summary_gc(struct yaffs_dev *dev, int blk);
|
||||
|
||||
|
||||
#endif
|
44
flight/pios/common/libraries/yaffs2/inc/yaffs_tagscompat.h
Normal file
44
flight/pios/common/libraries/yaffs2/inc/yaffs_tagscompat.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_TAGSCOMPAT_H__
|
||||
#define __YAFFS_TAGSCOMPAT_H__
|
||||
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
int yaffs_tags_compat_wr(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *data, const struct yaffs_ext_tags *tags);
|
||||
int yaffs_tags_compat_rd(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
u8 *data, struct yaffs_ext_tags *tags);
|
||||
int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no);
|
||||
int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
|
||||
int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void yaffs_tags_compat_install(struct yaffs_dev *dev);
|
||||
void yaffs_calc_tags_ecc(struct yaffs_tags *tags);
|
||||
int yaffs_check_tags_ecc(struct yaffs_tags *tags);
|
||||
|
||||
#endif
|
22
flight/pios/common/libraries/yaffs2/inc/yaffs_tagsmarshall.h
Normal file
22
flight/pios/common/libraries/yaffs2/inc/yaffs_tagsmarshall.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_TAGSMARSHALL_H__
|
||||
#define __YAFFS_TAGSMARSHALL_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
void yaffs_tags_marshall_install(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
57
flight/pios/common/libraries/yaffs2/inc/yaffs_trace.h
Normal file
57
flight/pios/common/libraries/yaffs2/inc/yaffs_trace.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YTRACE_H__
|
||||
#define __YTRACE_H__
|
||||
|
||||
extern unsigned int yaffs_trace_mask;
|
||||
extern unsigned int yaffs_wr_attempts;
|
||||
|
||||
/*
|
||||
* Tracing flags.
|
||||
* The flags masked in YAFFS_TRACE_ALWAYS are always traced.
|
||||
*/
|
||||
|
||||
#define YAFFS_TRACE_OS 0x00000002
|
||||
#define YAFFS_TRACE_ALLOCATE 0x00000004
|
||||
#define YAFFS_TRACE_SCAN 0x00000008
|
||||
#define YAFFS_TRACE_BAD_BLOCKS 0x00000010
|
||||
#define YAFFS_TRACE_ERASE 0x00000020
|
||||
#define YAFFS_TRACE_GC 0x00000040
|
||||
#define YAFFS_TRACE_WRITE 0x00000080
|
||||
#define YAFFS_TRACE_TRACING 0x00000100
|
||||
#define YAFFS_TRACE_DELETION 0x00000200
|
||||
#define YAFFS_TRACE_BUFFERS 0x00000400
|
||||
#define YAFFS_TRACE_NANDACCESS 0x00000800
|
||||
#define YAFFS_TRACE_GC_DETAIL 0x00001000
|
||||
#define YAFFS_TRACE_SCAN_DEBUG 0x00002000
|
||||
#define YAFFS_TRACE_MTD 0x00004000
|
||||
#define YAFFS_TRACE_CHECKPOINT 0x00008000
|
||||
|
||||
#define YAFFS_TRACE_VERIFY 0x00010000
|
||||
#define YAFFS_TRACE_VERIFY_NAND 0x00020000
|
||||
#define YAFFS_TRACE_VERIFY_FULL 0x00040000
|
||||
#define YAFFS_TRACE_VERIFY_ALL 0x000f0000
|
||||
|
||||
#define YAFFS_TRACE_SYNC 0x00100000
|
||||
#define YAFFS_TRACE_BACKGROUND 0x00200000
|
||||
#define YAFFS_TRACE_LOCK 0x00400000
|
||||
#define YAFFS_TRACE_MOUNT 0x00800000
|
||||
|
||||
#define YAFFS_TRACE_ERROR 0x40000000
|
||||
#define YAFFS_TRACE_BUG 0x80000000
|
||||
#define YAFFS_TRACE_ALWAYS 0xf0000000
|
||||
|
||||
#endif
|
43
flight/pios/common/libraries/yaffs2/inc/yaffs_verify.h
Normal file
43
flight/pios/common/libraries/yaffs2/inc/yaffs_verify.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_VERIFY_H__
|
||||
#define __YAFFS_VERIFY_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi,
|
||||
int n);
|
||||
void yaffs_verify_collected_blk(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi, int n);
|
||||
void yaffs_verify_blocks(struct yaffs_dev *dev);
|
||||
|
||||
void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,
|
||||
struct yaffs_ext_tags *tags, int parent_check);
|
||||
void yaffs_verify_file(struct yaffs_obj *obj);
|
||||
void yaffs_verify_link(struct yaffs_obj *obj);
|
||||
void yaffs_verify_symlink(struct yaffs_obj *obj);
|
||||
void yaffs_verify_special(struct yaffs_obj *obj);
|
||||
void yaffs_verify_obj(struct yaffs_obj *obj);
|
||||
void yaffs_verify_objects(struct yaffs_dev *dev);
|
||||
void yaffs_verify_obj_in_dir(struct yaffs_obj *obj);
|
||||
void yaffs_verify_dir(struct yaffs_obj *directory);
|
||||
void yaffs_verify_free_chunks(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_verify_file_sane(struct yaffs_obj *obj);
|
||||
|
||||
int yaffs_skip_verification(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
22
flight/pios/common/libraries/yaffs2/inc/yaffs_yaffs1.h
Normal file
22
flight/pios/common/libraries/yaffs2/inc/yaffs_yaffs1.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_YAFFS1_H__
|
||||
#define __YAFFS_YAFFS1_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
int yaffs1_scan(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
39
flight/pios/common/libraries/yaffs2/inc/yaffs_yaffs2.h
Normal file
39
flight/pios/common/libraries/yaffs2/inc/yaffs_yaffs2.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_YAFFS2_H__
|
||||
#define __YAFFS_YAFFS2_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev);
|
||||
void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev);
|
||||
void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi);
|
||||
void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,
|
||||
struct yaffs_block_info *bi);
|
||||
int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi);
|
||||
u32 yaffs2_find_refresh_block(struct yaffs_dev *dev);
|
||||
int yaffs2_checkpt_required(struct yaffs_dev *dev);
|
||||
int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev);
|
||||
|
||||
void yaffs2_checkpt_invalidate(struct yaffs_dev *dev);
|
||||
int yaffs2_checkpt_save(struct yaffs_dev *dev);
|
||||
int yaffs2_checkpt_restore(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs2_handle_hole(struct yaffs_obj *obj, Y_LOFF_T new_size);
|
||||
int yaffs2_scan_backwards(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
40
flight/pios/common/libraries/yaffs2/inc/yaffscfg.h
Normal file
40
flight/pios/common/libraries/yaffs2/inc/yaffscfg.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Header file for using yaffs in an application via
|
||||
* a direct interface.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YAFFSCFG_H__
|
||||
#define __YAFFSCFG_H__
|
||||
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
#define YAFFSFS_N_HANDLES 100
|
||||
|
||||
#define YAFFSFS_N_DSC 20
|
||||
|
||||
|
||||
struct yaffsfs_DeviceConfiguration {
|
||||
const YCHAR *prefix;
|
||||
struct yaffs_dev *dev;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
334
flight/pios/common/libraries/yaffs2/inc/yaffsfs.h
Normal file
334
flight/pios/common/libraries/yaffs2/inc/yaffsfs.h
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Header file for using yaffs in an application via
|
||||
* a direct interface.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YAFFSFS_H__
|
||||
#define __YAFFSFS_H__
|
||||
|
||||
#include "yaffscfg.h"
|
||||
#include "yportenv.h"
|
||||
|
||||
|
||||
#ifndef NAME_MAX
|
||||
#define NAME_MAX 256
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_YAFFS_OP
|
||||
#define YAFFS_MAX_FILE_SIZE (Y_LOFF_T)16000000
|
||||
#else
|
||||
#define YAFFS_MAX_FILE_SIZE \
|
||||
( (sizeof(Y_LOFF_T) < 8) ? YAFFS_MAX_FILE_SIZE_32 : (0x800000000LL - 1) )
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
struct yaffs_dirent {
|
||||
long d_ino; /* inode number */
|
||||
off_t d_off; /* offset to this dirent */
|
||||
unsigned short d_reclen; /* length of this dirent */
|
||||
YUCHAR d_type; /* type of this record */
|
||||
YCHAR d_name[NAME_MAX+1]; /* file name (null-terminated) */
|
||||
unsigned d_dont_use; /* debug: not for public consumption */
|
||||
};
|
||||
|
||||
typedef struct opaque_structure yaffs_DIR;
|
||||
|
||||
|
||||
|
||||
struct yaffs_stat {
|
||||
int st_dev; /* device */
|
||||
int st_ino; /* inode */
|
||||
unsigned st_mode; /* protection */
|
||||
int st_nlink; /* number of hard links */
|
||||
int st_uid; /* user ID of owner */
|
||||
int st_gid; /* group ID of owner */
|
||||
unsigned st_rdev; /* device type (if inode device) */
|
||||
Y_LOFF_T st_size; /* total size, in bytes */
|
||||
unsigned long st_blksize; /* blocksize for filesystem I/O */
|
||||
unsigned long st_blocks; /* number of blocks allocated */
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
/* Special 64-bit times for WinCE */
|
||||
unsigned long yst_wince_atime[2];
|
||||
unsigned long yst_wince_mtime[2];
|
||||
unsigned long yst_wince_ctime[2];
|
||||
#else
|
||||
unsigned long yst_atime; /* time of last access */
|
||||
unsigned long yst_mtime; /* time of last modification */
|
||||
unsigned long yst_ctime; /* time of last change */
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct yaffs_utimbuf {
|
||||
unsigned long actime;
|
||||
unsigned long modtime;
|
||||
};
|
||||
|
||||
/* Normal POSIX-style API functions */
|
||||
|
||||
int yaffs_open(const YCHAR *path, int oflag, int mode) ;
|
||||
|
||||
int yaffs_close(int fd) ;
|
||||
int yaffs_fsync(int fd) ;
|
||||
int yaffs_fdatasync(int fd) ;
|
||||
int yaffs_flush(int fd) ; /* same as yaffs_fsync() */
|
||||
|
||||
int yaffs_access(const YCHAR *path, int amode);
|
||||
|
||||
int yaffs_dup(int fd);
|
||||
|
||||
int yaffs_read(int fd, void *buf, unsigned int nbyte) ;
|
||||
int yaffs_write(int fd, const void *buf, unsigned int nbyte) ;
|
||||
|
||||
int yaffs_pread(int fd, void *buf, unsigned int nbyte, Y_LOFF_T offset);
|
||||
int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, Y_LOFF_T offset);
|
||||
|
||||
Y_LOFF_T yaffs_lseek(int fd, Y_LOFF_T offset, int whence) ;
|
||||
|
||||
int yaffs_truncate(const YCHAR *path, Y_LOFF_T new_size);
|
||||
int yaffs_ftruncate(int fd, Y_LOFF_T new_size);
|
||||
|
||||
int yaffs_unlink(const YCHAR *path) ;
|
||||
int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath) ;
|
||||
|
||||
int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf) ;
|
||||
int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf) ;
|
||||
int yaffs_fstat(int fd, struct yaffs_stat *buf) ;
|
||||
|
||||
int yaffs_utime(const YCHAR *path, const struct yaffs_utimbuf *buf);
|
||||
int yaffs_futime(int fd, const struct yaffs_utimbuf *buf);
|
||||
|
||||
|
||||
int yaffs_setxattr(const char *path, const char *name,
|
||||
const void *data, int size, int flags);
|
||||
int yaffs_lsetxattr(const char *path, const char *name,
|
||||
const void *data, int size, int flags);
|
||||
int yaffs_fsetxattr(int fd, const char *name,
|
||||
const void *data, int size, int flags);
|
||||
|
||||
int yaffs_getxattr(const char *path, const char *name,
|
||||
void *data, int size);
|
||||
int yaffs_lgetxattr(const char *path, const char *name,
|
||||
void *data, int size);
|
||||
int yaffs_fgetxattr(int fd, const char *name,
|
||||
void *data, int size);
|
||||
|
||||
int yaffs_removexattr(const char *path, const char *name);
|
||||
int yaffs_lremovexattr(const char *path, const char *name);
|
||||
int yaffs_fremovexattr(int fd, const char *name);
|
||||
|
||||
int yaffs_listxattr(const char *path, char *list, int size);
|
||||
int yaffs_llistxattr(const char *path, char *list, int size);
|
||||
int yaffs_flistxattr(int fd, char *list, int size);
|
||||
|
||||
int yaffs_chmod(const YCHAR *path, mode_t mode);
|
||||
int yaffs_fchmod(int fd, mode_t mode);
|
||||
|
||||
int yaffs_mkdir(const YCHAR *path, mode_t mode) ;
|
||||
int yaffs_rmdir(const YCHAR *path) ;
|
||||
|
||||
yaffs_DIR *yaffs_opendir(const YCHAR *dirname) ;
|
||||
struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp) ;
|
||||
void yaffs_rewinddir(yaffs_DIR *dirp) ;
|
||||
int yaffs_closedir(yaffs_DIR *dirp) ;
|
||||
|
||||
int yaffs_mount(const YCHAR *path) ;
|
||||
int yaffs_mount2(const YCHAR *path, int read_only);
|
||||
int yaffs_mount3(const YCHAR *path, int read_only, int skip_checkpt);
|
||||
|
||||
int yaffs_unmount(const YCHAR *path) ;
|
||||
int yaffs_unmount2(const YCHAR *path, int force);
|
||||
int yaffs_remount(const YCHAR *path, int force, int read_only);
|
||||
|
||||
int yaffs_format(const YCHAR *path,
|
||||
int unmount_flag,
|
||||
int force_unmount_flag,
|
||||
int remount_flag);
|
||||
|
||||
int yaffs_sync(const YCHAR *path) ;
|
||||
|
||||
int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath);
|
||||
int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz);
|
||||
|
||||
int yaffs_link(const YCHAR *oldpath, const YCHAR *newpath);
|
||||
int yaffs_mknod(const YCHAR *pathname, mode_t mode, dev_t dev);
|
||||
|
||||
Y_LOFF_T yaffs_freespace(const YCHAR *path);
|
||||
Y_LOFF_T yaffs_totalspace(const YCHAR *path);
|
||||
|
||||
/* Function variants that use a relative directory */
|
||||
struct yaffs_obj;
|
||||
int yaffs_open_sharing_reldir(struct yaffs_obj *reldir, const YCHAR *path, int oflag, int mode, int sharing);
|
||||
int yaffs_open_reldir(struct yaffs_obj *reldir,const YCHAR *path, int oflag, int mode);
|
||||
int yaffs_truncate_reldir(struct yaffs_obj *reldir, const YCHAR *path, Y_LOFF_T new_size);
|
||||
int yaffs_unlink_reldir(struct yaffs_obj *reldir, const YCHAR *path);
|
||||
int yaffs_rename_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldPath, const YCHAR *newPath);
|
||||
int yaffs_stat_reldir(struct yaffs_obj *reldir, const YCHAR *path, struct yaffs_stat *buf);
|
||||
int yaffs_lstat_reldir(struct yaffs_obj *reldir, const YCHAR *path, struct yaffs_stat *buf);
|
||||
int yaffs_utime_reldir(struct yaffs_obj *reldir, const YCHAR *path, const struct yaffs_utimbuf *buf);
|
||||
int yaffs_setxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags);
|
||||
int yaffs_lsetxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags);
|
||||
int yaffs_getxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size);
|
||||
int yaffs_lgetxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size);
|
||||
int yaffs_listxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size);
|
||||
int yaffs_llistxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size);
|
||||
int yaffs_removexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name);
|
||||
int yaffs_lremovexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name);
|
||||
int yaffs_access_reldir(struct yaffs_obj *reldir, const YCHAR *path, int amode);
|
||||
int yaffs_chmod_reldir(struct yaffs_obj *reldir, const YCHAR *path, mode_t mode);
|
||||
int yaffs_mkdir_reldir(struct yaffs_obj *reldir, const YCHAR *path, mode_t mode);
|
||||
int yaffs_rmdir_reldir(struct yaffs_obj *reldir, const YCHAR *path);
|
||||
yaffs_DIR *yaffs_opendir_reldir(struct yaffs_obj *reldir, const YCHAR *dirname);
|
||||
int yaffs_symlink_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldpath, const YCHAR *newpath);
|
||||
int yaffs_readlink_reldir(struct yaffs_obj *reldir,const YCHAR *path,
|
||||
YCHAR *buf, int bufsiz);
|
||||
int yaffs_link_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldpath, const YCHAR *linkpath);
|
||||
int yaffs_mknod_reldir(struct yaffs_obj *reldir, const YCHAR *pathname,
|
||||
mode_t mode, dev_t dev);
|
||||
|
||||
/* Function variants that use a relative device */
|
||||
struct yaffs_dev;
|
||||
int yaffs_open_sharing_reldev(struct yaffs_dev *dev, const YCHAR *path, int oflag, int mode, int sharing);
|
||||
int yaffs_open_reldev(struct yaffs_dev *dev,const YCHAR *path, int oflag, int mode);
|
||||
int yaffs_truncate_reldev(struct yaffs_dev *dev, const YCHAR *path, Y_LOFF_T new_size);
|
||||
int yaffs_unlink_reldev(struct yaffs_dev *dev, const YCHAR *path);
|
||||
int yaffs_rename_reldev(struct yaffs_dev *dev,
|
||||
const YCHAR *oldPath, const YCHAR *newPath);
|
||||
int yaffs_stat_reldev(struct yaffs_dev *dev, const YCHAR *path, struct yaffs_stat *buf);
|
||||
int yaffs_lstat_reldev(struct yaffs_dev *dev, const YCHAR *path, struct yaffs_stat *buf);
|
||||
int yaffs_utime_reldev(struct yaffs_dev *dev, const YCHAR *path, const struct yaffs_utimbuf *buf);
|
||||
int yaffs_setxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags);
|
||||
int yaffs_lsetxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags);
|
||||
int yaffs_getxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, void *data, int size);
|
||||
int yaffs_lgetxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, void *data, int size);
|
||||
int yaffs_listxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
char *data, int size);
|
||||
int yaffs_llistxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
char *data, int size);
|
||||
int yaffs_removexattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name);
|
||||
int yaffs_lremovexattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name);
|
||||
int yaffs_access_reldev(struct yaffs_dev *dev, const YCHAR *path, int amode);
|
||||
int yaffs_chmod_reldev(struct yaffs_dev *dev, const YCHAR *path, mode_t mode);
|
||||
int yaffs_mkdir_reldev(struct yaffs_dev *dev, const YCHAR *path, mode_t mode);
|
||||
int yaffs_rmdir_reldev(struct yaffs_dev *dev, const YCHAR *path);
|
||||
yaffs_DIR *yaffs_opendir_reldev(struct yaffs_dev *dev, const YCHAR *dirname);
|
||||
int yaffs_symlink_reldev(struct yaffs_dev *dev,
|
||||
const YCHAR *oldpath, const YCHAR *newpath);
|
||||
int yaffs_readlink_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
YCHAR *buf, int bufsiz);
|
||||
int yaffs_link_reldev(struct yaffs_dev *dev,
|
||||
const YCHAR *oldpath, const YCHAR *linkpath);
|
||||
int yaffs_mknod_reldev(struct yaffs_dev *dev, const YCHAR *pathname,
|
||||
mode_t mode, dev_t dev_val);
|
||||
Y_LOFF_T yaffs_freespace_reldev(struct yaffs_dev *dev);
|
||||
Y_LOFF_T yaffs_totalspace_reldev(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_sync_reldev(struct yaffs_dev *dev);
|
||||
int yaffs_unmount_reldev(struct yaffs_dev *dev);
|
||||
int yaffs_unmount2_reldev(struct yaffs_dev *dev, int force);
|
||||
int yaffs_remount_reldev(struct yaffs_dev *dev, int force, int read_only);
|
||||
|
||||
|
||||
/* Some non-standard functions to use fds to access directories */
|
||||
struct yaffs_dirent *yaffs_readdir_fd(int fd);
|
||||
void yaffs_rewinddir_fd(int fd);
|
||||
|
||||
/* Non-standard functions to pump garbage collection. */
|
||||
int yaffs_do_background_gc(const YCHAR *path, int urgency);
|
||||
int yaffs_do_background_gc_reldev(struct yaffs_dev *dev, int urgency);
|
||||
|
||||
/* Non-standard functions to get usage info */
|
||||
int yaffs_inodecount(const YCHAR *path);
|
||||
|
||||
int yaffs_n_handles(const YCHAR *path);
|
||||
|
||||
int yaffs_n_handles_reldir(struct yaffs_obj *reldir, const YCHAR *path);
|
||||
int yaffs_dump_dev_reldir(struct yaffs_obj *reldir, const YCHAR *path);
|
||||
int yaffs_n_handles_reldev(struct yaffs_dev *dev, const YCHAR *path);
|
||||
int yaffs_dump_dev_reldev(struct yaffs_dev *dev, const YCHAR *path);
|
||||
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
int yaffs_set_wince_times(int fd,
|
||||
const unsigned *wctime,
|
||||
const unsigned *watime,
|
||||
const unsigned *wmtime);
|
||||
int yaffs_get_wince_times(int fd,
|
||||
unsigned *wctime,
|
||||
unsigned *watime,
|
||||
unsigned *wmtime);
|
||||
#endif
|
||||
|
||||
|
||||
#define YAFFS_SHARE_READ 1
|
||||
#define YAFFS_SHARE_WRITE 2
|
||||
int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int shareMode);
|
||||
|
||||
struct yaffs_dev;
|
||||
void yaffs_add_device(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_start_up(void);
|
||||
int yaffsfs_GetLastError(void);
|
||||
|
||||
/* Functions to iterate through devices. NB Use with extreme care! */
|
||||
void yaffs_dev_rewind(void);
|
||||
struct yaffs_dev *yaffs_next_dev(void);
|
||||
|
||||
/* Function to get the last error */
|
||||
int yaffs_get_error(void);
|
||||
const char *yaffs_error_to_str(int err);
|
||||
|
||||
/* Function only for debugging */
|
||||
void *yaffs_getdev(const YCHAR *path);
|
||||
int yaffs_dump_dev(const YCHAR *path);
|
||||
int yaffs_set_error(int error);
|
||||
|
||||
/* Trace control functions */
|
||||
unsigned yaffs_set_trace(unsigned tm);
|
||||
unsigned yaffs_get_trace(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
101
flight/pios/common/libraries/yaffs2/inc/ydirectenv.h
Normal file
101
flight/pios/common/libraries/yaffs2/inc/ydirectenv.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ydirectenv.h: Environment wrappers for YAFFS direct.
|
||||
*/
|
||||
|
||||
#ifndef __YDIRECTENV_H__
|
||||
#define __YDIRECTENV_H__
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stdlib.h"
|
||||
#include "stdio.h"
|
||||
#include "string.h"
|
||||
#include "yaffs_osglue.h"
|
||||
#include "yaffs_hweight.h"
|
||||
|
||||
void yaffs_bug_fn(const char *file_name, int line_no);
|
||||
|
||||
#define BUG() do { yaffs_bug_fn(__FILE__, __LINE__); } while (0)
|
||||
|
||||
|
||||
#define YCHAR char
|
||||
#define YUCHAR unsigned char
|
||||
#define _Y(x) x
|
||||
|
||||
#ifndef Y_LOFF_T
|
||||
#define Y_LOFF_T int32_t // note CONFIG_YAFFS_OP changes dependent on type for max file size
|
||||
#endif
|
||||
|
||||
#define yaffs_strcat(a, b) strcat(a, b)
|
||||
#define yaffs_strcpy(a, b) strcpy(a, b)
|
||||
#define yaffs_strncpy(a, b, c) strncpy(a, b, c)
|
||||
#define yaffs_strnlen(s, m) strnlen(s, m)
|
||||
#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
|
||||
#define yaffs_strcmp(a, b) strcasecmp(a, b)
|
||||
#define yaffs_strncmp(a, b, c) strncasecmp(a, b, c)
|
||||
#else
|
||||
#define yaffs_strcmp(a, b) strcmp(a, b)
|
||||
#define yaffs_strncmp(a, b, c) strncmp(a, b, c)
|
||||
#endif
|
||||
|
||||
#define hweight8(x) yaffs_hweight8(x)
|
||||
#define hweight32(x) yaffs_hweight32(x)
|
||||
|
||||
void yaffs_qsort(void *aa, size_t n, size_t es,
|
||||
int (*cmp)(const void *, const void *));
|
||||
|
||||
#define sort(base, n, sz, cmp_fn, swp) yaffs_qsort(base, n, sz, cmp_fn)
|
||||
|
||||
#define YAFFS_PATH_DIVIDERS "/"
|
||||
|
||||
#ifdef NO_inline
|
||||
#define inline
|
||||
#else
|
||||
#define inline __inline__
|
||||
#endif
|
||||
|
||||
#define kmalloc(x, flags) yaffsfs_malloc(x)
|
||||
#define kfree(x) yaffsfs_free(x)
|
||||
#define vmalloc(x) yaffsfs_malloc(x)
|
||||
#define vfree(x) yaffsfs_free(x)
|
||||
|
||||
#define cond_resched() do {} while (0)
|
||||
|
||||
#define yaffs_trace(msk, fmt, ...) do { \
|
||||
if (yaffs_trace_mask & (msk)) \
|
||||
printf("yaffs: " fmt "\n", ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define YAFFS_LOSTNFOUND_NAME "lost+found"
|
||||
#define YAFFS_LOSTNFOUND_PREFIX "obj"
|
||||
|
||||
#include "yaffscfg.h"
|
||||
|
||||
#define Y_CURRENT_TIME yaffsfs_CurrentTime()
|
||||
#define Y_TIME_CONVERT(x) x
|
||||
|
||||
#define YAFFS_ROOT_MODE 0666
|
||||
#define YAFFS_LOSTNFOUND_MODE 0666
|
||||
|
||||
#include "yaffs_list.h"
|
||||
|
||||
#include "yaffsfs.h"
|
||||
|
||||
#endif
|
||||
|
||||
|
324
flight/pios/common/libraries/yaffs2/inc/yportenv.h
Normal file
324
flight/pios/common/libraries/yaffs2/inc/yportenv.h
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YPORTENV_H__
|
||||
#define __YPORTENV_H__
|
||||
|
||||
|
||||
/* Definition of types */
|
||||
#ifdef CONFIG_YAFFS_DEFINES_TYPES
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int s32;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_YAFFS_PROVIDE_DEFS
|
||||
/* File types */
|
||||
|
||||
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_FIFO 1
|
||||
#define DT_CHR 2
|
||||
#define DT_DIR 4
|
||||
#define DT_BLK 6
|
||||
#define DT_REG 8
|
||||
#define DT_LNK 10
|
||||
#define DT_SOCK 12
|
||||
#define DT_WHT 14
|
||||
|
||||
|
||||
/*
|
||||
* Attribute flags.
|
||||
* These are or-ed together to select what has been changed.
|
||||
*/
|
||||
#define ATTR_MODE 1
|
||||
#define ATTR_UID 2
|
||||
#define ATTR_GID 4
|
||||
#define ATTR_SIZE 8
|
||||
#define ATTR_ATIME 16
|
||||
#define ATTR_MTIME 32
|
||||
#define ATTR_CTIME 64
|
||||
|
||||
struct iattr {
|
||||
unsigned int ia_valid;
|
||||
unsigned ia_mode;
|
||||
unsigned ia_uid;
|
||||
unsigned ia_gid;
|
||||
unsigned ia_size;
|
||||
unsigned ia_atime;
|
||||
unsigned ia_mtime;
|
||||
unsigned ia_ctime;
|
||||
unsigned int ia_attr_flags;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined CONFIG_YAFFS_WINCE
|
||||
|
||||
#include "ywinceenv.h"
|
||||
|
||||
|
||||
#elif defined CONFIG_YAFFS_DIRECT
|
||||
|
||||
/* Direct interface */
|
||||
#include "ydirectenv.h"
|
||||
|
||||
#elif defined CONFIG_YAFFS_UTIL
|
||||
|
||||
#include "yutilsenv.h"
|
||||
|
||||
#else
|
||||
/* Should have specified a configuration type */
|
||||
#error Unknown configuration
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_YAFFS_DIRECT) || defined(CONFIG_YAFFS_WINCE)
|
||||
|
||||
#ifdef CONFIG_YAFFSFS_PROVIDE_VALUES
|
||||
|
||||
#ifndef O_RDONLY
|
||||
#define O_RDONLY 00
|
||||
#endif
|
||||
|
||||
#ifndef O_WRONLY
|
||||
#define O_WRONLY 01
|
||||
#endif
|
||||
|
||||
#ifndef O_RDWR
|
||||
#define O_RDWR 02
|
||||
#endif
|
||||
|
||||
#ifndef O_CREAT
|
||||
#define O_CREAT 0100
|
||||
#endif
|
||||
|
||||
#ifndef O_EXCL
|
||||
#define O_EXCL 0200
|
||||
#endif
|
||||
|
||||
#ifndef O_TRUNC
|
||||
#define O_TRUNC 01000
|
||||
#endif
|
||||
|
||||
#ifndef O_APPEND
|
||||
#define O_APPEND 02000
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_CUR
|
||||
#define SEEK_CUR 1
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_END
|
||||
#define SEEK_END 2
|
||||
#endif
|
||||
|
||||
#ifndef EBUSY
|
||||
#define EBUSY 16
|
||||
#endif
|
||||
|
||||
#ifndef ENODEV
|
||||
#define ENODEV 19
|
||||
#endif
|
||||
|
||||
#ifndef EINVAL
|
||||
#define EINVAL 22
|
||||
#endif
|
||||
|
||||
#ifndef ENFILE
|
||||
#define ENFILE 23
|
||||
#endif
|
||||
|
||||
#ifndef EBADF
|
||||
#define EBADF 9
|
||||
#endif
|
||||
|
||||
#ifndef EACCES
|
||||
#define EACCES 13
|
||||
#endif
|
||||
|
||||
#ifndef EXDEV
|
||||
#define EXDEV 18
|
||||
#endif
|
||||
|
||||
#ifndef ENOENT
|
||||
#define ENOENT 2
|
||||
#endif
|
||||
|
||||
#ifndef ENOSPC
|
||||
#define ENOSPC 28
|
||||
#endif
|
||||
|
||||
#ifndef EROFS
|
||||
#define EROFS 30
|
||||
#endif
|
||||
|
||||
#ifndef ERANGE
|
||||
#define ERANGE 34
|
||||
#endif
|
||||
|
||||
#ifndef ENODATA
|
||||
#define ENODATA 61
|
||||
#endif
|
||||
|
||||
#ifndef ENOTEMPTY
|
||||
#define ENOTEMPTY 39
|
||||
#endif
|
||||
|
||||
#ifndef ENAMETOOLONG
|
||||
#define ENAMETOOLONG 36
|
||||
#endif
|
||||
|
||||
#ifndef ENOMEM
|
||||
#define ENOMEM 12
|
||||
#endif
|
||||
|
||||
#ifndef EFAULT
|
||||
#define EFAULT 14
|
||||
#endif
|
||||
|
||||
#ifndef EEXIST
|
||||
#define EEXIST 17
|
||||
#endif
|
||||
|
||||
#ifndef ENOTDIR
|
||||
#define ENOTDIR 20
|
||||
#endif
|
||||
|
||||
#ifndef EISDIR
|
||||
#define EISDIR 21
|
||||
#endif
|
||||
|
||||
#ifndef ELOOP
|
||||
#define ELOOP 40
|
||||
#endif
|
||||
|
||||
|
||||
/* Mode flags */
|
||||
|
||||
#ifndef S_IFMT
|
||||
#define S_IFMT 0170000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFSOCK
|
||||
#define S_IFSOCK 0140000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFIFO
|
||||
#define S_IFIFO 0010000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFCHR
|
||||
#define S_IFCHR 0020000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFBLK
|
||||
#define S_IFBLK 0060000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFLNK
|
||||
#define S_IFLNK 0120000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFDIR
|
||||
#define S_IFDIR 0040000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFREG
|
||||
#define S_IFREG 0100000
|
||||
#endif
|
||||
|
||||
#ifndef S_ISSOCK
|
||||
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||
#endif
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||
#endif
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
#ifndef S_ISBLK
|
||||
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
|
||||
#endif
|
||||
#ifndef S_ISCHR
|
||||
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
||||
#endif
|
||||
#ifndef S_ISFIFO
|
||||
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef S_IREAD
|
||||
#define S_IREAD 0000400
|
||||
#endif
|
||||
|
||||
#ifndef S_IWRITE
|
||||
#define S_IWRITE 0000200
|
||||
#endif
|
||||
|
||||
#ifndef S_IEXEC
|
||||
#define S_IEXEC 0000100
|
||||
#endif
|
||||
|
||||
#ifndef XATTR_CREATE
|
||||
#define XATTR_CREATE 1
|
||||
#endif
|
||||
|
||||
#ifndef XATTR_REPLACE
|
||||
#define XATTR_REPLACE 2
|
||||
#endif
|
||||
|
||||
#ifndef R_OK
|
||||
#define R_OK 4
|
||||
#define W_OK 2
|
||||
#define X_OK 1
|
||||
#define F_OK 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef Y_DUMP_STACK
|
||||
#define Y_DUMP_STACK() do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef BUG
|
||||
#define BUG() do {\
|
||||
yaffs_trace(YAFFS_TRACE_BUG,\
|
||||
"==>> yaffs bug: " __FILE__ " %d",\
|
||||
__LINE__);\
|
||||
Y_DUMP_STACK();\
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
29
flight/pios/common/libraries/yaffs2/library.mk
Normal file
29
flight/pios/common/libraries/yaffs2/library.mk
Normal file
@ -0,0 +1,29 @@
|
||||
#
|
||||
# Rules to add yaffs2 to the PiOS target
|
||||
#
|
||||
#
|
||||
# Note that the PIOS target-specific makefile will detect that YAFFS2_DIR
|
||||
# has been defined and add in the target-specific pieces separately.
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Directory containing this makefile
|
||||
#
|
||||
YAFFS2_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
|
||||
# Compiler options
|
||||
#
|
||||
CDEFS += -DCONFIG_YAFFS_DIRECT
|
||||
CDEFS += -DCONFIG_YAFFS_YAFFS2
|
||||
CDEFS += -DCONFIG_YAFFS_DEFINES_TYPES
|
||||
CDEFS += -DCONFIG_YAFFS_PROVIDE_DEFS
|
||||
CDEFS += -DCONFIG_YAFFSFS_PROVIDE_VALUES
|
||||
CDEFS += -DCONFIG_YAFFS_OP
|
||||
#ARCHFLAGS += -DARCH_POSIX
|
||||
|
||||
#
|
||||
# Yaffs2 device library source and includes
|
||||
#
|
||||
SRC += $(sort $(wildcard $(YAFFS2_DIR)*.c))
|
||||
EXTRAINCDIRS += $(YAFFS2_DIR)/inc
|
357
flight/pios/common/libraries/yaffs2/yaffs_allocator.c
Normal file
357
flight/pios/common/libraries/yaffs2/yaffs_allocator.c
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_allocator.h"
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yportenv.h"
|
||||
|
||||
/*
|
||||
* Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks
|
||||
* of approx 100 objects that are themn allocated singly.
|
||||
* This is basically a simplified slab allocator.
|
||||
*
|
||||
* We don't use the Linux slab allocator because slab does not allow
|
||||
* us to dump all the objects in one hit when we do a umount and tear
|
||||
* down all the tnodes and objects. slab requires that we first free
|
||||
* the individual objects.
|
||||
*
|
||||
* Once yaffs has been mainlined I shall try to motivate for a change
|
||||
* to slab to provide the extra features we need here.
|
||||
*/
|
||||
|
||||
struct yaffs_tnode_list {
|
||||
struct yaffs_tnode_list *next;
|
||||
struct yaffs_tnode *tnodes;
|
||||
};
|
||||
|
||||
struct yaffs_obj_list {
|
||||
struct yaffs_obj_list *next;
|
||||
struct yaffs_obj *objects;
|
||||
};
|
||||
|
||||
struct yaffs_allocator {
|
||||
int n_tnodes_created;
|
||||
struct yaffs_tnode *free_tnodes;
|
||||
int n_free_tnodes;
|
||||
struct yaffs_tnode_list *alloc_tnode_list;
|
||||
|
||||
int n_obj_created;
|
||||
struct list_head free_objs;
|
||||
int n_free_objects;
|
||||
|
||||
struct yaffs_obj_list *allocated_obj_list;
|
||||
};
|
||||
|
||||
static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator =
|
||||
(struct yaffs_allocator *)dev->allocator;
|
||||
struct yaffs_tnode_list *tmp;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
while (allocator->alloc_tnode_list) {
|
||||
tmp = allocator->alloc_tnode_list->next;
|
||||
|
||||
kfree(allocator->alloc_tnode_list->tnodes);
|
||||
kfree(allocator->alloc_tnode_list);
|
||||
allocator->alloc_tnode_list = tmp;
|
||||
}
|
||||
|
||||
allocator->free_tnodes = NULL;
|
||||
allocator->n_free_tnodes = 0;
|
||||
allocator->n_tnodes_created = 0;
|
||||
}
|
||||
|
||||
static void yaffs_init_raw_tnodes(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
allocator->alloc_tnode_list = NULL;
|
||||
allocator->free_tnodes = NULL;
|
||||
allocator->n_free_tnodes = 0;
|
||||
allocator->n_tnodes_created = 0;
|
||||
}
|
||||
|
||||
static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes)
|
||||
{
|
||||
struct yaffs_allocator *allocator =
|
||||
(struct yaffs_allocator *)dev->allocator;
|
||||
int i;
|
||||
struct yaffs_tnode *new_tnodes;
|
||||
u8 *mem;
|
||||
struct yaffs_tnode *curr;
|
||||
struct yaffs_tnode *next;
|
||||
struct yaffs_tnode_list *tnl;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (n_tnodes < 1)
|
||||
return YAFFS_OK;
|
||||
|
||||
/* make these things */
|
||||
new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS);
|
||||
mem = (u8 *) new_tnodes;
|
||||
|
||||
if (!new_tnodes) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs: Could not allocate Tnodes");
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* New hookup for wide tnodes */
|
||||
for (i = 0; i < n_tnodes - 1; i++) {
|
||||
curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size];
|
||||
next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size];
|
||||
curr->internal[0] = next;
|
||||
}
|
||||
|
||||
curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size];
|
||||
curr->internal[0] = allocator->free_tnodes;
|
||||
allocator->free_tnodes = (struct yaffs_tnode *)mem;
|
||||
|
||||
allocator->n_free_tnodes += n_tnodes;
|
||||
allocator->n_tnodes_created += n_tnodes;
|
||||
|
||||
/* Now add this bunch of tnodes to a list for freeing up.
|
||||
* NB If we can't add this to the management list it isn't fatal
|
||||
* but it just means we can't free this bunch of tnodes later.
|
||||
*/
|
||||
tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS);
|
||||
if (!tnl) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Could not add tnodes to management list");
|
||||
return YAFFS_FAIL;
|
||||
} else {
|
||||
tnl->tnodes = new_tnodes;
|
||||
tnl->next = allocator->alloc_tnode_list;
|
||||
allocator->alloc_tnode_list = tnl;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added");
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator =
|
||||
(struct yaffs_allocator *)dev->allocator;
|
||||
struct yaffs_tnode *tn = NULL;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If there are none left make more */
|
||||
if (!allocator->free_tnodes)
|
||||
yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES);
|
||||
|
||||
if (allocator->free_tnodes) {
|
||||
tn = allocator->free_tnodes;
|
||||
allocator->free_tnodes = allocator->free_tnodes->internal[0];
|
||||
allocator->n_free_tnodes--;
|
||||
}
|
||||
|
||||
return tn;
|
||||
}
|
||||
|
||||
/* FreeTnode frees up a tnode and puts it back on the free list */
|
||||
void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
if (tn) {
|
||||
tn->internal[0] = allocator->free_tnodes;
|
||||
allocator->free_tnodes = tn;
|
||||
allocator->n_free_tnodes++;
|
||||
}
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
}
|
||||
|
||||
/*--------------- yaffs_obj alloaction ------------------------
|
||||
*
|
||||
* Free yaffs_objs are stored in a list using obj->siblings.
|
||||
* The blocks of allocated objects are stored in a linked list.
|
||||
*/
|
||||
|
||||
static void yaffs_init_raw_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
allocator->allocated_obj_list = NULL;
|
||||
INIT_LIST_HEAD(&allocator->free_objs);
|
||||
allocator->n_free_objects = 0;
|
||||
}
|
||||
|
||||
static void yaffs_deinit_raw_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
struct yaffs_obj_list *tmp;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
while (allocator->allocated_obj_list) {
|
||||
tmp = allocator->allocated_obj_list->next;
|
||||
kfree(allocator->allocated_obj_list->objects);
|
||||
kfree(allocator->allocated_obj_list);
|
||||
allocator->allocated_obj_list = tmp;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&allocator->free_objs);
|
||||
allocator->n_free_objects = 0;
|
||||
allocator->n_obj_created = 0;
|
||||
}
|
||||
|
||||
static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
int i;
|
||||
struct yaffs_obj *new_objs;
|
||||
struct yaffs_obj_list *list;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (n_obj < 1)
|
||||
return YAFFS_OK;
|
||||
|
||||
/* make these things */
|
||||
new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS);
|
||||
list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS);
|
||||
|
||||
if (!new_objs || !list) {
|
||||
kfree(new_objs);
|
||||
new_objs = NULL;
|
||||
kfree(list);
|
||||
list = NULL;
|
||||
yaffs_trace(YAFFS_TRACE_ALLOCATE,
|
||||
"Could not allocate more objects");
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Hook them into the free list */
|
||||
for (i = 0; i < n_obj; i++)
|
||||
list_add(&new_objs[i].siblings, &allocator->free_objs);
|
||||
|
||||
allocator->n_free_objects += n_obj;
|
||||
allocator->n_obj_created += n_obj;
|
||||
|
||||
/* Now add this bunch of Objects to a list for freeing up. */
|
||||
|
||||
list->objects = new_objs;
|
||||
list->next = allocator->allocated_obj_list;
|
||||
allocator->allocated_obj_list = list;
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct list_head *lh;
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* If there are none left make more */
|
||||
if (list_empty(&allocator->free_objs))
|
||||
yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS);
|
||||
|
||||
if (!list_empty(&allocator->free_objs)) {
|
||||
lh = allocator->free_objs.next;
|
||||
obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
list_del_init(lh);
|
||||
allocator->n_free_objects--;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj)
|
||||
{
|
||||
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Link into the free list. */
|
||||
list_add(&obj->siblings, &allocator->free_objs);
|
||||
allocator->n_free_objects++;
|
||||
}
|
||||
|
||||
void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
|
||||
if (!dev->allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
yaffs_deinit_raw_tnodes(dev);
|
||||
yaffs_deinit_raw_objs(dev);
|
||||
kfree(dev->allocator);
|
||||
dev->allocator = NULL;
|
||||
}
|
||||
|
||||
void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator;
|
||||
|
||||
if (dev->allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS);
|
||||
if (allocator) {
|
||||
dev->allocator = allocator;
|
||||
yaffs_init_raw_tnodes(dev);
|
||||
yaffs_init_raw_objs(dev);
|
||||
}
|
||||
}
|
||||
|
110
flight/pios/common/libraries/yaffs2/yaffs_attribs.c
Normal file
110
flight/pios/common/libraries/yaffs2/yaffs_attribs.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_attribs.h"
|
||||
|
||||
|
||||
void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
obj->win_atime[0] = oh->win_atime[0];
|
||||
obj->win_ctime[0] = oh->win_ctime[0];
|
||||
obj->win_mtime[0] = oh->win_mtime[0];
|
||||
obj->win_atime[1] = oh->win_atime[1];
|
||||
obj->win_ctime[1] = oh->win_ctime[1];
|
||||
obj->win_mtime[1] = oh->win_mtime[1];
|
||||
#else
|
||||
obj->yst_uid = oh->yst_uid;
|
||||
obj->yst_gid = oh->yst_gid;
|
||||
obj->yst_atime = oh->yst_atime;
|
||||
obj->yst_mtime = oh->yst_mtime;
|
||||
obj->yst_ctime = oh->yst_ctime;
|
||||
obj->yst_rdev = oh->yst_rdev;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
oh->win_atime[0] = obj->win_atime[0];
|
||||
oh->win_ctime[0] = obj->win_ctime[0];
|
||||
oh->win_mtime[0] = obj->win_mtime[0];
|
||||
oh->win_atime[1] = obj->win_atime[1];
|
||||
oh->win_ctime[1] = obj->win_ctime[1];
|
||||
oh->win_mtime[1] = obj->win_mtime[1];
|
||||
#else
|
||||
oh->yst_uid = obj->yst_uid;
|
||||
oh->yst_gid = obj->yst_gid;
|
||||
oh->yst_atime = obj->yst_atime;
|
||||
oh->yst_mtime = obj->yst_mtime;
|
||||
oh->yst_ctime = obj->yst_ctime;
|
||||
oh->yst_rdev = obj->yst_rdev;
|
||||
#endif
|
||||
}
|
||||
|
||||
void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev)
|
||||
{
|
||||
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
yfsd_win_file_time_now(obj->win_atime);
|
||||
obj->win_ctime[0] = obj->win_mtime[0] = obj->win_atime[0];
|
||||
obj->win_ctime[1] = obj->win_mtime[1] = obj->win_atime[1];
|
||||
|
||||
#else
|
||||
yaffs_load_current_time(obj, 1, 1);
|
||||
obj->yst_rdev = rdev;
|
||||
obj->yst_uid = uid;
|
||||
obj->yst_gid = gid;
|
||||
#endif
|
||||
}
|
||||
|
||||
void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
yfsd_win_file_time_now(obj->win_atime);
|
||||
obj->win_ctime[0] = obj->win_mtime[0] =
|
||||
obj->win_atime[0];
|
||||
obj->win_ctime[1] = obj->win_mtime[1] =
|
||||
obj->win_atime[1];
|
||||
|
||||
#else
|
||||
|
||||
obj->yst_mtime = Y_CURRENT_TIME;
|
||||
if (do_a)
|
||||
obj->yst_atime = obj->yst_atime;
|
||||
if (do_c)
|
||||
obj->yst_ctime = obj->yst_atime;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef CONFIG_YAFFS_OP // unused yaffs code
|
||||
static Y_LOFF_T yaffs_get_file_size(struct yaffs_obj *obj)
|
||||
{
|
||||
YCHAR *alias = NULL;
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
return obj->variant.file_variant.file_size;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
alias = obj->variant.symlink_variant.alias;
|
||||
if (!alias)
|
||||
return 0;
|
||||
return yaffs_strnlen(alias, YAFFS_MAX_ALIAS_LENGTH);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
97
flight/pios/common/libraries/yaffs2/yaffs_bitmap.c
Normal file
97
flight/pios/common/libraries/yaffs2/yaffs_bitmap.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_trace.h"
|
||||
/*
|
||||
* Chunk bitmap manipulations
|
||||
*/
|
||||
|
||||
static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"BlockBits block %d is not valid",
|
||||
blk);
|
||||
BUG();
|
||||
}
|
||||
return dev->chunk_bits +
|
||||
(dev->chunk_bit_stride * (blk - dev->internal_start_block));
|
||||
}
|
||||
|
||||
void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk)
|
||||
{
|
||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block ||
|
||||
chunk < 0 || chunk >= dev->param.chunks_per_block) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Chunk Id (%d:%d) invalid",
|
||||
blk, chunk);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
|
||||
memset(blk_bits, 0, dev->chunk_bit_stride);
|
||||
}
|
||||
|
||||
void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
|
||||
yaffs_verify_chunk_bit_id(dev, blk, chunk);
|
||||
blk_bits[chunk / 8] &= ~(1 << (chunk & 7));
|
||||
}
|
||||
|
||||
void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
|
||||
yaffs_verify_chunk_bit_id(dev, blk, chunk);
|
||||
blk_bits[chunk / 8] |= (1 << (chunk & 7));
|
||||
}
|
||||
|
||||
int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
|
||||
yaffs_verify_chunk_bit_id(dev, blk, chunk);
|
||||
return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;
|
||||
}
|
||||
|
||||
int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->chunk_bit_stride; i++) {
|
||||
if (*blk_bits)
|
||||
return 1;
|
||||
blk_bits++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
int i;
|
||||
int n = 0;
|
||||
|
||||
for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++)
|
||||
n += hweight8(*blk_bits);
|
||||
|
||||
return n;
|
||||
}
|
477
flight/pios/common/libraries/yaffs2/yaffs_checkptrw.c
Normal file
477
flight/pios/common/libraries/yaffs2/yaffs_checkptrw.c
Normal file
@ -0,0 +1,477 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_checkptrw.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
|
||||
struct yaffs_checkpt_chunk_hdr {
|
||||
int version;
|
||||
int seq;
|
||||
u32 sum;
|
||||
u32 xor;
|
||||
} ;
|
||||
|
||||
|
||||
static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)
|
||||
{
|
||||
return chunk - dev->chunk_offset;
|
||||
}
|
||||
|
||||
static int apply_block_offset(struct yaffs_dev *dev, int block)
|
||||
{
|
||||
return block - dev->block_offset;
|
||||
}
|
||||
|
||||
static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_checkpt_chunk_hdr hdr;
|
||||
|
||||
hdr.version = YAFFS_CHECKPOINT_VERSION;
|
||||
hdr.seq = dev->checkpt_page_seq;
|
||||
hdr.sum = dev->checkpt_sum;
|
||||
hdr.xor = dev->checkpt_xor;
|
||||
|
||||
dev->checkpt_byte_offs = sizeof(hdr);
|
||||
|
||||
memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_checkpt_chunk_hdr hdr;
|
||||
|
||||
memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr));
|
||||
|
||||
dev->checkpt_byte_offs = sizeof(hdr);
|
||||
|
||||
return hdr.version == YAFFS_CHECKPOINT_VERSION &&
|
||||
hdr.seq == dev->checkpt_page_seq &&
|
||||
hdr.sum == dev->checkpt_sum &&
|
||||
hdr.xor == dev->checkpt_xor;
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev)
|
||||
{
|
||||
int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"checkpt blocks_avail = %d", blocks_avail);
|
||||
|
||||
return (blocks_avail <= 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int yaffs_checkpt_erase(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!dev->drv.drv_erase_fn)
|
||||
return 0;
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"checking blocks %d to %d",
|
||||
dev->internal_start_block, dev->internal_end_block);
|
||||
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
|
||||
int offset_i = apply_block_offset(dev, i);
|
||||
int result;
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"erasing checkpt block %d", i);
|
||||
|
||||
dev->n_erasures++;
|
||||
|
||||
result = dev->drv.drv_erase_fn(dev, offset_i);
|
||||
if(result) {
|
||||
bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
dev->n_erased_blocks++;
|
||||
dev->n_free_chunks +=
|
||||
dev->param.chunks_per_block;
|
||||
} else {
|
||||
dev->drv.drv_mark_bad_fn(dev, offset_i);
|
||||
bi->block_state = YAFFS_BLOCK_STATE_DEAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev->blocks_in_checkpt = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"allocating checkpt block: erased %d reserved %d avail %d next %d ",
|
||||
dev->n_erased_blocks, dev->param.n_reserved_blocks,
|
||||
blocks_avail, dev->checkpt_next_block);
|
||||
|
||||
if (dev->checkpt_next_block >= 0 &&
|
||||
dev->checkpt_next_block <= dev->internal_end_block &&
|
||||
blocks_avail > 0) {
|
||||
|
||||
for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
|
||||
i++) {
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
bi = yaffs_get_block_info(dev, i);
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
|
||||
dev->checkpt_next_block = i + 1;
|
||||
dev->checkpt_cur_block = i;
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"allocating checkpt block %d", i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks");
|
||||
|
||||
dev->checkpt_next_block = -1;
|
||||
dev->checkpt_cur_block = -1;
|
||||
}
|
||||
|
||||
static void yaffs2_checkpt_find_block(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_ext_tags tags;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"find next checkpt block: start: blocks %d next %d",
|
||||
dev->blocks_in_checkpt, dev->checkpt_next_block);
|
||||
|
||||
if (dev->blocks_in_checkpt < dev->checkpt_max_blocks)
|
||||
for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
|
||||
i++) {
|
||||
int chunk = i * dev->param.chunks_per_block;
|
||||
enum yaffs_block_state state;
|
||||
u32 seq;
|
||||
|
||||
dev->tagger.read_chunk_tags_fn(dev,
|
||||
apply_chunk_offset(dev, chunk),
|
||||
NULL, &tags);
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"find next checkpt block: search: block %d state %d oid %d seq %d eccr %d",
|
||||
i, (int) state,
|
||||
tags.obj_id, tags.seq_number,
|
||||
tags.ecc_result);
|
||||
|
||||
if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA)
|
||||
continue;
|
||||
|
||||
dev->tagger.query_block_fn(dev,
|
||||
apply_block_offset(dev, i),
|
||||
&state, &seq);
|
||||
if (state == YAFFS_BLOCK_STATE_DEAD)
|
||||
continue;
|
||||
|
||||
/* Right kind of block */
|
||||
dev->checkpt_next_block = tags.obj_id;
|
||||
dev->checkpt_cur_block = i;
|
||||
dev->checkpt_block_list[dev->blocks_in_checkpt] = i;
|
||||
dev->blocks_in_checkpt++;
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"found checkpt block %d", i);
|
||||
return;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks");
|
||||
|
||||
dev->checkpt_next_block = -1;
|
||||
dev->checkpt_cur_block = -1;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->checkpt_open_write = writing;
|
||||
|
||||
/* Got the functions we need? */
|
||||
if (!dev->tagger.write_chunk_tags_fn ||
|
||||
!dev->tagger.read_chunk_tags_fn ||
|
||||
!dev->drv.drv_erase_fn ||
|
||||
!dev->drv.drv_mark_bad_fn)
|
||||
return 0;
|
||||
|
||||
if (writing && !yaffs2_checkpt_space_ok(dev))
|
||||
return 0;
|
||||
|
||||
if (!dev->checkpt_buffer)
|
||||
dev->checkpt_buffer =
|
||||
kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
|
||||
if (!dev->checkpt_buffer)
|
||||
return 0;
|
||||
|
||||
dev->checkpt_page_seq = 0;
|
||||
dev->checkpt_byte_count = 0;
|
||||
dev->checkpt_sum = 0;
|
||||
dev->checkpt_xor = 0;
|
||||
dev->checkpt_cur_block = -1;
|
||||
dev->checkpt_cur_chunk = -1;
|
||||
dev->checkpt_next_block = dev->internal_start_block;
|
||||
|
||||
if (writing) {
|
||||
memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
|
||||
yaffs2_checkpt_init_chunk_hdr(dev);
|
||||
return yaffs_checkpt_erase(dev);
|
||||
}
|
||||
|
||||
/* Opening for a read */
|
||||
/* Set to a value that will kick off a read */
|
||||
dev->checkpt_byte_offs = dev->data_bytes_per_chunk;
|
||||
/* A checkpoint block list of 1 checkpoint block per 16 block is
|
||||
* (hopefully) going to be way more than we need */
|
||||
dev->blocks_in_checkpt = 0;
|
||||
dev->checkpt_max_blocks =
|
||||
(dev->internal_end_block - dev->internal_start_block) / 16 + 2;
|
||||
dev->checkpt_block_list =
|
||||
kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);
|
||||
|
||||
if (!dev->checkpt_block_list)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < dev->checkpt_max_blocks; i++)
|
||||
dev->checkpt_block_list[i] = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
|
||||
{
|
||||
u32 composite_sum;
|
||||
|
||||
composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
|
||||
*sum = composite_sum;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
|
||||
{
|
||||
int chunk;
|
||||
int offset_chunk;
|
||||
struct yaffs_ext_tags tags;
|
||||
|
||||
if (dev->checkpt_cur_block < 0) {
|
||||
yaffs2_checkpt_find_erased_block(dev);
|
||||
dev->checkpt_cur_chunk = 0;
|
||||
}
|
||||
|
||||
if (dev->checkpt_cur_block < 0)
|
||||
return 0;
|
||||
|
||||
tags.is_deleted = 0;
|
||||
tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */
|
||||
tags.chunk_id = dev->checkpt_page_seq + 1;
|
||||
tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;
|
||||
tags.n_bytes = dev->data_bytes_per_chunk;
|
||||
if (dev->checkpt_cur_chunk == 0) {
|
||||
/* First chunk we write for the block? Set block state to
|
||||
checkpoint */
|
||||
struct yaffs_block_info *bi =
|
||||
yaffs_get_block_info(dev, dev->checkpt_cur_block);
|
||||
bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
|
||||
dev->blocks_in_checkpt++;
|
||||
}
|
||||
|
||||
chunk =
|
||||
dev->checkpt_cur_block * dev->param.chunks_per_block +
|
||||
dev->checkpt_cur_chunk;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
|
||||
chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,
|
||||
tags.obj_id, tags.chunk_id);
|
||||
|
||||
offset_chunk = apply_chunk_offset(dev, chunk);
|
||||
|
||||
dev->n_page_writes++;
|
||||
|
||||
dev->tagger.write_chunk_tags_fn(dev, offset_chunk,
|
||||
dev->checkpt_buffer, &tags);
|
||||
dev->checkpt_page_seq++;
|
||||
dev->checkpt_cur_chunk++;
|
||||
if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) {
|
||||
dev->checkpt_cur_chunk = 0;
|
||||
dev->checkpt_cur_block = -1;
|
||||
}
|
||||
memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
|
||||
|
||||
yaffs2_checkpt_init_chunk_hdr(dev);
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
|
||||
{
|
||||
int i = 0;
|
||||
int ok = 1;
|
||||
u8 *data_bytes = (u8 *) data;
|
||||
|
||||
if (!dev->checkpt_buffer)
|
||||
return 0;
|
||||
|
||||
if (!dev->checkpt_open_write)
|
||||
return -1;
|
||||
|
||||
while (i < n_bytes && ok) {
|
||||
dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;
|
||||
dev->checkpt_sum += *data_bytes;
|
||||
dev->checkpt_xor ^= *data_bytes;
|
||||
|
||||
dev->checkpt_byte_offs++;
|
||||
i++;
|
||||
data_bytes++;
|
||||
dev->checkpt_byte_count++;
|
||||
|
||||
if (dev->checkpt_byte_offs < 0 ||
|
||||
dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)
|
||||
ok = yaffs2_checkpt_flush_buffer(dev);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
|
||||
{
|
||||
int i = 0;
|
||||
int ok = 1;
|
||||
struct yaffs_ext_tags tags;
|
||||
int chunk;
|
||||
int offset_chunk;
|
||||
u8 *data_bytes = (u8 *) data;
|
||||
|
||||
if (!dev->checkpt_buffer)
|
||||
return 0;
|
||||
|
||||
if (dev->checkpt_open_write)
|
||||
return -1;
|
||||
|
||||
while (i < n_bytes && ok) {
|
||||
|
||||
if (dev->checkpt_byte_offs < 0 ||
|
||||
dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) {
|
||||
|
||||
if (dev->checkpt_cur_block < 0) {
|
||||
yaffs2_checkpt_find_block(dev);
|
||||
dev->checkpt_cur_chunk = 0;
|
||||
}
|
||||
|
||||
if (dev->checkpt_cur_block < 0) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
chunk = dev->checkpt_cur_block *
|
||||
dev->param.chunks_per_block +
|
||||
dev->checkpt_cur_chunk;
|
||||
|
||||
offset_chunk = apply_chunk_offset(dev, chunk);
|
||||
dev->n_page_reads++;
|
||||
|
||||
/* read in the next chunk */
|
||||
dev->tagger.read_chunk_tags_fn(dev,
|
||||
offset_chunk,
|
||||
dev->checkpt_buffer,
|
||||
&tags);
|
||||
#ifdef CONFIG_YAFFS_OP
|
||||
if (tags.chunk_id != (u32)(dev->checkpt_page_seq + 1) ||
|
||||
#else
|
||||
if (tags.chunk_id != (dev->checkpt_page_seq + 1) ||
|
||||
#endif
|
||||
tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
|
||||
tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
if(!yaffs2_checkpt_check_chunk_hdr(dev)) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
dev->checkpt_page_seq++;
|
||||
dev->checkpt_cur_chunk++;
|
||||
|
||||
if (dev->checkpt_cur_chunk >=
|
||||
dev->param.chunks_per_block)
|
||||
dev->checkpt_cur_block = -1;
|
||||
|
||||
}
|
||||
|
||||
*data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];
|
||||
dev->checkpt_sum += *data_bytes;
|
||||
dev->checkpt_xor ^= *data_bytes;
|
||||
dev->checkpt_byte_offs++;
|
||||
i++;
|
||||
data_bytes++;
|
||||
dev->checkpt_byte_count++;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int yaffs_checkpt_close(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dev->checkpt_open_write) {
|
||||
if (dev->checkpt_byte_offs !=
|
||||
sizeof(sizeof(struct yaffs_checkpt_chunk_hdr)))
|
||||
yaffs2_checkpt_flush_buffer(dev);
|
||||
} else if (dev->checkpt_block_list) {
|
||||
for (i = 0;
|
||||
i < dev->blocks_in_checkpt &&
|
||||
dev->checkpt_block_list[i] >= 0; i++) {
|
||||
int blk = dev->checkpt_block_list[i];
|
||||
struct yaffs_block_info *bi = NULL;
|
||||
|
||||
if (dev->internal_start_block <= blk &&
|
||||
blk <= dev->internal_end_block)
|
||||
bi = yaffs_get_block_info(dev, blk);
|
||||
if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)
|
||||
bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
|
||||
}
|
||||
kfree(dev->checkpt_block_list);
|
||||
dev->checkpt_block_list = NULL;
|
||||
}
|
||||
|
||||
dev->n_free_chunks -=
|
||||
dev->blocks_in_checkpt * dev->param.chunks_per_block;
|
||||
dev->n_erased_blocks -= dev->blocks_in_checkpt;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
|
||||
dev->checkpt_byte_count);
|
||||
|
||||
if (dev->checkpt_buffer) {
|
||||
/* free the buffer */
|
||||
kfree(dev->checkpt_buffer);
|
||||
dev->checkpt_buffer = NULL;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
|
||||
{
|
||||
/* Erase the checkpoint data */
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"checkpoint invalidate of %d blocks",
|
||||
dev->blocks_in_checkpt);
|
||||
|
||||
return yaffs_checkpt_erase(dev);
|
||||
}
|
281
flight/pios/common/libraries/yaffs2/yaffs_ecc.c
Normal file
281
flight/pios/common/libraries/yaffs2/yaffs_ecc.c
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code implements the ECC algorithm used in SmartMedia.
|
||||
*
|
||||
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
||||
* The two unused bit are set to 1.
|
||||
* The ECC can correct single bit errors in a 256-byte page of data. Thus, two
|
||||
* such ECC blocks are used on a 512-byte NAND page.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
#include "yaffs_ecc.h"
|
||||
|
||||
/* Table generated by gen-ecc.c
|
||||
* Using a table means we do not have to calculate p1..p4 and p1'..p4'
|
||||
* for each byte of data. These are instead provided in a table in bits7..2.
|
||||
* Bit 0 of each entry indicates whether the entry has an odd or even parity,
|
||||
* and therefore this bytes influence on the line parity.
|
||||
*/
|
||||
|
||||
static const unsigned char column_parity_table[] = {
|
||||
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
|
||||
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
||||
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
|
||||
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
||||
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
|
||||
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
||||
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
|
||||
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
||||
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
|
||||
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
||||
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
|
||||
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
||||
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
|
||||
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
||||
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
|
||||
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
||||
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
|
||||
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
||||
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
|
||||
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
||||
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
|
||||
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
||||
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
|
||||
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
||||
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
|
||||
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
||||
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
|
||||
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
||||
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
|
||||
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
||||
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
|
||||
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
||||
};
|
||||
|
||||
|
||||
/* Calculate the ECC for a 256-byte block of data */
|
||||
void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned char col_parity = 0;
|
||||
unsigned char line_parity = 0;
|
||||
unsigned char line_parity_prime = 0;
|
||||
unsigned char t;
|
||||
unsigned char b;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
b = column_parity_table[*data++];
|
||||
col_parity ^= b;
|
||||
|
||||
if (b & 0x01) { /* odd number of bits in the byte */
|
||||
line_parity ^= i;
|
||||
line_parity_prime ^= ~i;
|
||||
}
|
||||
}
|
||||
|
||||
ecc[2] = (~col_parity) | 0x03;
|
||||
|
||||
t = 0;
|
||||
if (line_parity & 0x80)
|
||||
t |= 0x80;
|
||||
if (line_parity_prime & 0x80)
|
||||
t |= 0x40;
|
||||
if (line_parity & 0x40)
|
||||
t |= 0x20;
|
||||
if (line_parity_prime & 0x40)
|
||||
t |= 0x10;
|
||||
if (line_parity & 0x20)
|
||||
t |= 0x08;
|
||||
if (line_parity_prime & 0x20)
|
||||
t |= 0x04;
|
||||
if (line_parity & 0x10)
|
||||
t |= 0x02;
|
||||
if (line_parity_prime & 0x10)
|
||||
t |= 0x01;
|
||||
ecc[1] = ~t;
|
||||
|
||||
t = 0;
|
||||
if (line_parity & 0x08)
|
||||
t |= 0x80;
|
||||
if (line_parity_prime & 0x08)
|
||||
t |= 0x40;
|
||||
if (line_parity & 0x04)
|
||||
t |= 0x20;
|
||||
if (line_parity_prime & 0x04)
|
||||
t |= 0x10;
|
||||
if (line_parity & 0x02)
|
||||
t |= 0x08;
|
||||
if (line_parity_prime & 0x02)
|
||||
t |= 0x04;
|
||||
if (line_parity & 0x01)
|
||||
t |= 0x02;
|
||||
if (line_parity_prime & 0x01)
|
||||
t |= 0x01;
|
||||
ecc[0] = ~t;
|
||||
|
||||
}
|
||||
|
||||
/* Correct the ECC on a 256 byte block of data */
|
||||
|
||||
int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
|
||||
const unsigned char *test_ecc)
|
||||
{
|
||||
unsigned char d0, d1, d2; /* deltas */
|
||||
|
||||
d0 = read_ecc[0] ^ test_ecc[0];
|
||||
d1 = read_ecc[1] ^ test_ecc[1];
|
||||
d2 = read_ecc[2] ^ test_ecc[2];
|
||||
|
||||
if ((d0 | d1 | d2) == 0)
|
||||
return 0; /* no error */
|
||||
|
||||
if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
|
||||
((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
|
||||
((d2 ^ (d2 >> 1)) & 0x54) == 0x54) {
|
||||
/* Single bit (recoverable) error in data */
|
||||
|
||||
unsigned byte;
|
||||
unsigned bit;
|
||||
|
||||
bit = byte = 0;
|
||||
|
||||
if (d1 & 0x80)
|
||||
byte |= 0x80;
|
||||
if (d1 & 0x20)
|
||||
byte |= 0x40;
|
||||
if (d1 & 0x08)
|
||||
byte |= 0x20;
|
||||
if (d1 & 0x02)
|
||||
byte |= 0x10;
|
||||
if (d0 & 0x80)
|
||||
byte |= 0x08;
|
||||
if (d0 & 0x20)
|
||||
byte |= 0x04;
|
||||
if (d0 & 0x08)
|
||||
byte |= 0x02;
|
||||
if (d0 & 0x02)
|
||||
byte |= 0x01;
|
||||
|
||||
if (d2 & 0x80)
|
||||
bit |= 0x04;
|
||||
if (d2 & 0x20)
|
||||
bit |= 0x02;
|
||||
if (d2 & 0x08)
|
||||
bit |= 0x01;
|
||||
|
||||
data[byte] ^= (1 << bit);
|
||||
|
||||
return 1; /* Corrected the error */
|
||||
}
|
||||
|
||||
if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) {
|
||||
/* Reccoverable error in ecc */
|
||||
|
||||
read_ecc[0] = test_ecc[0];
|
||||
read_ecc[1] = test_ecc[1];
|
||||
read_ecc[2] = test_ecc[2];
|
||||
|
||||
return 1; /* Corrected the error */
|
||||
}
|
||||
|
||||
/* Unrecoverable error */
|
||||
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* ECCxxxOther does ECC calcs on arbitrary n bytes of data
|
||||
*/
|
||||
void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
|
||||
struct yaffs_ecc_other *ecc_other)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned char col_parity = 0;
|
||||
unsigned line_parity = 0;
|
||||
unsigned line_parity_prime = 0;
|
||||
unsigned char b;
|
||||
|
||||
for (i = 0; i < n_bytes; i++) {
|
||||
b = column_parity_table[*data++];
|
||||
col_parity ^= b;
|
||||
|
||||
if (b & 0x01) {
|
||||
/* odd number of bits in the byte */
|
||||
line_parity ^= i;
|
||||
line_parity_prime ^= ~i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ecc_other->col_parity = (col_parity >> 2) & 0x3f;
|
||||
ecc_other->line_parity = line_parity;
|
||||
ecc_other->line_parity_prime = line_parity_prime;
|
||||
}
|
||||
|
||||
int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
|
||||
struct yaffs_ecc_other *read_ecc,
|
||||
const struct yaffs_ecc_other *test_ecc)
|
||||
{
|
||||
unsigned char delta_col; /* column parity delta */
|
||||
unsigned delta_line; /* line parity delta */
|
||||
unsigned delta_line_prime; /* line parity delta */
|
||||
unsigned bit;
|
||||
|
||||
delta_col = read_ecc->col_parity ^ test_ecc->col_parity;
|
||||
delta_line = read_ecc->line_parity ^ test_ecc->line_parity;
|
||||
delta_line_prime =
|
||||
read_ecc->line_parity_prime ^ test_ecc->line_parity_prime;
|
||||
|
||||
if ((delta_col | delta_line | delta_line_prime) == 0)
|
||||
return 0; /* no error */
|
||||
|
||||
if (delta_line == ~delta_line_prime &&
|
||||
(((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) {
|
||||
/* Single bit (recoverable) error in data */
|
||||
|
||||
bit = 0;
|
||||
|
||||
if (delta_col & 0x20)
|
||||
bit |= 0x04;
|
||||
if (delta_col & 0x08)
|
||||
bit |= 0x02;
|
||||
if (delta_col & 0x02)
|
||||
bit |= 0x01;
|
||||
|
||||
if (delta_line >= n_bytes)
|
||||
return -1;
|
||||
|
||||
data[delta_line] ^= (1 << bit);
|
||||
|
||||
return 1; /* corrected */
|
||||
}
|
||||
|
||||
if ((hweight32(delta_line) +
|
||||
hweight32(delta_line_prime) +
|
||||
hweight8(delta_col)) == 1) {
|
||||
/* Reccoverable error in ecc */
|
||||
|
||||
*read_ecc = *test_ecc;
|
||||
return 1; /* corrected */
|
||||
}
|
||||
|
||||
/* Unrecoverable error */
|
||||
|
||||
return -1;
|
||||
}
|
58
flight/pios/common/libraries/yaffs2/yaffs_error.c
Normal file
58
flight/pios/common/libraries/yaffs2/yaffs_error.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* YAFFS: Yet another FFS. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Timothy Manning <timothy@yaffs.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffsfs.h"
|
||||
|
||||
struct error_entry {
|
||||
int code;
|
||||
const char *text;
|
||||
};
|
||||
|
||||
static const struct error_entry error_list[] = {
|
||||
{ ENOMEM , "ENOMEM" },
|
||||
{ EBUSY , "EBUSY"},
|
||||
{ ENODEV , "ENODEV"},
|
||||
{ EINVAL , "EINVAL"},
|
||||
{ EBADF , "EBADF"},
|
||||
{ EACCES , "EACCES"},
|
||||
{ EXDEV , "EXDEV" },
|
||||
{ ENOENT , "ENOENT"},
|
||||
{ ENOSPC , "ENOSPC"},
|
||||
{ ERANGE , "ERANGE"},
|
||||
{ ENODATA, "ENODATA"},
|
||||
{ ENOTEMPTY, "ENOTEMPTY"},
|
||||
{ ENAMETOOLONG, "ENAMETOOLONG"},
|
||||
{ ENOMEM , "ENOMEM"},
|
||||
{ EEXIST , "EEXIST"},
|
||||
{ ENOTDIR , "ENOTDIR"},
|
||||
{ EISDIR , "EISDIR"},
|
||||
{ ENFILE, "ENFILE"},
|
||||
{ EROFS, "EROFS"},
|
||||
{ EFAULT, "EFAULT"},
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *yaffs_error_to_str(int err)
|
||||
{
|
||||
const struct error_entry *e = error_list;
|
||||
|
||||
if (err < 0)
|
||||
err = -err;
|
||||
|
||||
while (e->code && e->text) {
|
||||
if (err == e->code)
|
||||
return e->text;
|
||||
e++;
|
||||
}
|
||||
return "Unknown error code";
|
||||
}
|
5175
flight/pios/common/libraries/yaffs2/yaffs_guts.c
Normal file
5175
flight/pios/common/libraries/yaffs2/yaffs_guts.c
Normal file
@ -0,0 +1,5175 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_tagscompat.h"
|
||||
#include "yaffs_tagsmarshall.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_yaffs1.h"
|
||||
#include "yaffs_yaffs2.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_verify.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_packedtags2.h"
|
||||
#include "yaffs_nameval.h"
|
||||
#include "yaffs_allocator.h"
|
||||
#include "yaffs_attribs.h"
|
||||
#include "yaffs_summary.h"
|
||||
|
||||
/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */
|
||||
#define YAFFS_GC_GOOD_ENOUGH 2
|
||||
#define YAFFS_GC_PASSIVE_THRESHOLD 4
|
||||
|
||||
#include "yaffs_ecc.h"
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk,
|
||||
const u8 *buffer, int n_bytes, int use_reserve);
|
||||
|
||||
static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name,
|
||||
int buffer_size);
|
||||
|
||||
/* Function to calculate chunk and offset */
|
||||
|
||||
void yaffs_addr_to_chunk(struct yaffs_dev *dev, Y_LOFF_T addr,
|
||||
int *chunk_out, u32 *offset_out)
|
||||
{
|
||||
int chunk;
|
||||
u32 offset;
|
||||
|
||||
chunk = (u32) (addr >> dev->chunk_shift);
|
||||
|
||||
if (dev->chunk_div == 1) {
|
||||
/* easy power of 2 case */
|
||||
offset = (u32) (addr & dev->chunk_mask);
|
||||
} else {
|
||||
/* Non power-of-2 case */
|
||||
|
||||
Y_LOFF_T chunk_base;
|
||||
|
||||
chunk /= dev->chunk_div;
|
||||
|
||||
chunk_base = ((Y_LOFF_T) chunk) * dev->data_bytes_per_chunk;
|
||||
offset = (u32) (addr - chunk_base);
|
||||
}
|
||||
|
||||
*chunk_out = chunk;
|
||||
*offset_out = offset;
|
||||
}
|
||||
|
||||
/* Function to return the number of shifts for a power of 2 greater than or
|
||||
* equal to the given number
|
||||
* Note we don't try to cater for all possible numbers and this does not have to
|
||||
* be hellishly efficient.
|
||||
*/
|
||||
|
||||
static inline u32 calc_shifts_ceiling(u32 x)
|
||||
{
|
||||
int extra_bits;
|
||||
int shifts;
|
||||
|
||||
shifts = extra_bits = 0;
|
||||
|
||||
while (x > 1) {
|
||||
if (x & 1)
|
||||
extra_bits++;
|
||||
x >>= 1;
|
||||
shifts++;
|
||||
}
|
||||
|
||||
if (extra_bits)
|
||||
shifts++;
|
||||
|
||||
return shifts;
|
||||
}
|
||||
|
||||
/* Function to return the number of shifts to get a 1 in bit 0
|
||||
*/
|
||||
|
||||
static inline u32 calc_shifts(u32 x)
|
||||
{
|
||||
u32 shifts;
|
||||
|
||||
shifts = 0;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
|
||||
while (!(x & 1)) {
|
||||
x >>= 1;
|
||||
shifts++;
|
||||
}
|
||||
|
||||
return shifts;
|
||||
}
|
||||
|
||||
/*
|
||||
* Temporary buffer manipulations.
|
||||
*/
|
||||
|
||||
static int yaffs_init_tmp_buffers(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
u8 *buf = (u8 *) 1;
|
||||
|
||||
memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer));
|
||||
|
||||
for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) {
|
||||
dev->temp_buffer[i].in_use = 0;
|
||||
buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
|
||||
dev->temp_buffer[i].buffer = buf;
|
||||
}
|
||||
|
||||
return buf ? YAFFS_OK : YAFFS_FAIL;
|
||||
}
|
||||
|
||||
u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->temp_in_use++;
|
||||
if (dev->temp_in_use > dev->max_temp)
|
||||
dev->max_temp = dev->temp_in_use;
|
||||
|
||||
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
|
||||
if (dev->temp_buffer[i].in_use == 0) {
|
||||
dev->temp_buffer[i].in_use = 1;
|
||||
return dev->temp_buffer[i].buffer;
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers");
|
||||
/*
|
||||
* If we got here then we have to allocate an unmanaged one
|
||||
* This is not good.
|
||||
*/
|
||||
|
||||
dev->unmanaged_buffer_allocs++;
|
||||
return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS);
|
||||
|
||||
}
|
||||
|
||||
void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->temp_in_use--;
|
||||
|
||||
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
|
||||
if (dev->temp_buffer[i].buffer == buffer) {
|
||||
dev->temp_buffer[i].in_use = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
/* assume it is an unmanaged one. */
|
||||
yaffs_trace(YAFFS_TRACE_BUFFERS,
|
||||
"Releasing unmanaged temp buffer");
|
||||
kfree(buffer);
|
||||
dev->unmanaged_buffer_deallocs++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for robustisizing TODO
|
||||
*
|
||||
*/
|
||||
|
||||
static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data,
|
||||
const struct yaffs_ext_tags *tags)
|
||||
{
|
||||
(void) dev;
|
||||
(void) nand_chunk;
|
||||
(void) data;
|
||||
(void) tags;
|
||||
}
|
||||
|
||||
static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk,
|
||||
const struct yaffs_ext_tags *tags)
|
||||
{
|
||||
(void) dev;
|
||||
(void) nand_chunk;
|
||||
(void) tags;
|
||||
}
|
||||
|
||||
void yaffs_handle_chunk_error(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi)
|
||||
{
|
||||
if (!bi->gc_prioritise) {
|
||||
bi->gc_prioritise = 1;
|
||||
dev->has_pending_prioritised_gc = 1;
|
||||
bi->chunk_error_strikes++;
|
||||
|
||||
if (bi->chunk_error_strikes > 3) {
|
||||
bi->needs_retiring = 1; /* Too many stikes, so retire */
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs: Block struck out");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk,
|
||||
int erased_ok)
|
||||
{
|
||||
int flash_block = nand_chunk / dev->param.chunks_per_block;
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);
|
||||
|
||||
yaffs_handle_chunk_error(dev, bi);
|
||||
|
||||
if (erased_ok) {
|
||||
/* Was an actual write failure,
|
||||
* so mark the block for retirement.*/
|
||||
bi->needs_retiring = 1;
|
||||
yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
||||
"**>> Block %d needs retiring", flash_block);
|
||||
}
|
||||
|
||||
/* Delete the chunk */
|
||||
yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
|
||||
yaffs_skip_rest_of_block(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verification code
|
||||
*/
|
||||
|
||||
/*
|
||||
* Simple hash function. Needs to have a reasonable spread
|
||||
*/
|
||||
|
||||
static inline int yaffs_hash_fn(int n)
|
||||
{
|
||||
if (n < 0)
|
||||
n = -n;
|
||||
return n % YAFFS_NOBJECT_BUCKETS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Access functions to useful fake objects.
|
||||
* Note that root might have a presence in NAND if permissions are set.
|
||||
*/
|
||||
|
||||
struct yaffs_obj *yaffs_root(struct yaffs_dev *dev)
|
||||
{
|
||||
return dev->root_dir;
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev)
|
||||
{
|
||||
return dev->lost_n_found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Erased NAND checking functions
|
||||
*/
|
||||
|
||||
int yaffs_check_ff(u8 *buffer, int n_bytes)
|
||||
{
|
||||
/* Horrible, slow implementation */
|
||||
while (n_bytes--) {
|
||||
if (*buffer != 0xff)
|
||||
return 0;
|
||||
buffer++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk)
|
||||
{
|
||||
int retval = YAFFS_OK;
|
||||
u8 *data = yaffs_get_temp_buffer(dev);
|
||||
struct yaffs_ext_tags tags;
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_check_chunk_erased: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
|
||||
if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR)
|
||||
retval = YAFFS_FAIL;
|
||||
|
||||
if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) ||
|
||||
tags.chunk_used) {
|
||||
yaffs_trace(YAFFS_TRACE_NANDACCESS,
|
||||
"Chunk %d not erased", nand_chunk);
|
||||
retval = YAFFS_FAIL;
|
||||
}
|
||||
|
||||
|
||||
yaffs_release_temp_buffer(dev, data);
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
static int yaffs_verify_chunk_written(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *data,
|
||||
struct yaffs_ext_tags *tags)
|
||||
{
|
||||
int retval = YAFFS_OK;
|
||||
struct yaffs_ext_tags temp_tags;
|
||||
u8 *buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_verify_chunk_written: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
if (memcmp(buffer, data, dev->data_bytes_per_chunk) ||
|
||||
temp_tags.obj_id != tags->obj_id ||
|
||||
temp_tags.chunk_id != tags->chunk_id ||
|
||||
temp_tags.n_bytes != tags->n_bytes)
|
||||
retval = YAFFS_FAIL;
|
||||
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks)
|
||||
{
|
||||
int reserved_chunks;
|
||||
int reserved_blocks = dev->param.n_reserved_blocks;
|
||||
int checkpt_blocks;
|
||||
|
||||
checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev);
|
||||
|
||||
reserved_chunks =
|
||||
(reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block;
|
||||
|
||||
return (dev->n_free_chunks > (reserved_chunks + n_chunks));
|
||||
}
|
||||
|
||||
static int yaffs_find_alloc_block(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (dev->n_erased_blocks < 1) {
|
||||
/* Hoosterman we've got a problem.
|
||||
* Can't get space to gc
|
||||
*/
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: no more erased blocks");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find an empty block. */
|
||||
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
dev->alloc_block_finder++;
|
||||
if (dev->alloc_block_finder < dev->internal_start_block
|
||||
|| dev->alloc_block_finder > dev->internal_end_block) {
|
||||
dev->alloc_block_finder = dev->internal_start_block;
|
||||
}
|
||||
|
||||
bi = yaffs_get_block_info(dev, dev->alloc_block_finder);
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
|
||||
bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING;
|
||||
dev->seq_number++;
|
||||
bi->seq_number = dev->seq_number;
|
||||
dev->n_erased_blocks--;
|
||||
yaffs_trace(YAFFS_TRACE_ALLOCATE,
|
||||
"Allocated block %d, seq %d, %d left" ,
|
||||
dev->alloc_block_finder, dev->seq_number,
|
||||
dev->n_erased_blocks);
|
||||
return dev->alloc_block_finder;
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs tragedy: no more erased blocks, but there should have been %d",
|
||||
dev->n_erased_blocks);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver,
|
||||
struct yaffs_block_info **block_ptr)
|
||||
{
|
||||
int ret_val;
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (dev->alloc_block < 0) {
|
||||
/* Get next block to allocate off */
|
||||
dev->alloc_block = yaffs_find_alloc_block(dev);
|
||||
dev->alloc_page = 0;
|
||||
}
|
||||
|
||||
if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) {
|
||||
/* No space unless we're allowed to use the reserve. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dev->n_erased_blocks < dev->param.n_reserved_blocks
|
||||
&& dev->alloc_page == 0)
|
||||
yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve");
|
||||
|
||||
/* Next page please.... */
|
||||
if (dev->alloc_block >= 0) {
|
||||
bi = yaffs_get_block_info(dev, dev->alloc_block);
|
||||
|
||||
ret_val = (dev->alloc_block * dev->param.chunks_per_block) +
|
||||
dev->alloc_page;
|
||||
bi->pages_in_use++;
|
||||
yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page);
|
||||
|
||||
dev->alloc_page++;
|
||||
|
||||
dev->n_free_chunks--;
|
||||
|
||||
/* If the block is full set the state to full */
|
||||
if (dev->alloc_page >= (u32)dev->param.chunks_per_block) {
|
||||
bi->block_state = YAFFS_BLOCK_STATE_FULL;
|
||||
dev->alloc_block = -1;
|
||||
}
|
||||
|
||||
if (block_ptr)
|
||||
*block_ptr = bi;
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int yaffs_get_erased_chunks(struct yaffs_dev *dev)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = dev->n_erased_blocks * dev->param.chunks_per_block;
|
||||
|
||||
if (dev->alloc_block > 0)
|
||||
n += (dev->param.chunks_per_block - dev->alloc_page);
|
||||
|
||||
return n;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_skip_rest_of_block() skips over the rest of the allocation block
|
||||
* if we don't want to write to it.
|
||||
*/
|
||||
void yaffs_skip_rest_of_block(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (dev->alloc_block > 0) {
|
||||
bi = yaffs_get_block_info(dev, dev->alloc_block);
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {
|
||||
bi->block_state = YAFFS_BLOCK_STATE_FULL;
|
||||
dev->alloc_block = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffs_write_new_chunk(struct yaffs_dev *dev,
|
||||
const u8 *data,
|
||||
struct yaffs_ext_tags *tags, int use_reserver)
|
||||
{
|
||||
u32 attempts = 0;
|
||||
int write_ok = 0;
|
||||
int chunk;
|
||||
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
|
||||
do {
|
||||
struct yaffs_block_info *bi = 0;
|
||||
int erased_ok = 0;
|
||||
|
||||
chunk = yaffs_alloc_chunk(dev, use_reserver, &bi);
|
||||
if (chunk < 0) {
|
||||
/* no space */
|
||||
break;
|
||||
}
|
||||
|
||||
/* First check this chunk is erased, if it needs
|
||||
* checking. The checking policy (unless forced
|
||||
* always on) is as follows:
|
||||
*
|
||||
* Check the first page we try to write in a block.
|
||||
* If the check passes then we don't need to check any
|
||||
* more. If the check fails, we check again...
|
||||
* If the block has been erased, we don't need to check.
|
||||
*
|
||||
* However, if the block has been prioritised for gc,
|
||||
* then we think there might be something odd about
|
||||
* this block and stop using it.
|
||||
*
|
||||
* Rationale: We should only ever see chunks that have
|
||||
* not been erased if there was a partially written
|
||||
* chunk due to power loss. This checking policy should
|
||||
* catch that case with very few checks and thus save a
|
||||
* lot of checks that are most likely not needed.
|
||||
*
|
||||
* Mods to the above
|
||||
* If an erase check fails or the write fails we skip the
|
||||
* rest of the block.
|
||||
*/
|
||||
|
||||
/* let's give it a try */
|
||||
attempts++;
|
||||
|
||||
if (dev->param.always_check_erased)
|
||||
bi->skip_erased_check = 0;
|
||||
|
||||
if (!bi->skip_erased_check) {
|
||||
erased_ok = yaffs_check_chunk_erased(dev, chunk);
|
||||
if (erased_ok != YAFFS_OK) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>> yaffs chunk %d was not erased",
|
||||
chunk);
|
||||
|
||||
/* If not erased, delete this one,
|
||||
* skip rest of block and
|
||||
* try another chunk */
|
||||
yaffs_chunk_del(dev, chunk, 1, __LINE__);
|
||||
yaffs_skip_rest_of_block(dev);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags);
|
||||
|
||||
if (!bi->skip_erased_check)
|
||||
write_ok =
|
||||
yaffs_verify_chunk_written(dev, chunk, data, tags);
|
||||
|
||||
if (write_ok != YAFFS_OK) {
|
||||
/* Clean up aborted write, skip to next block and
|
||||
* try another chunk */
|
||||
yaffs_handle_chunk_wr_error(dev, chunk, erased_ok);
|
||||
continue;
|
||||
}
|
||||
|
||||
bi->skip_erased_check = 1;
|
||||
|
||||
/* Copy the data into the robustification buffer */
|
||||
yaffs_handle_chunk_wr_ok(dev, chunk, data, tags);
|
||||
|
||||
} while (write_ok != YAFFS_OK &&
|
||||
(yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts));
|
||||
|
||||
if (!write_ok)
|
||||
chunk = -1;
|
||||
|
||||
if (attempts > 1) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>> yaffs write required %d attempts",
|
||||
attempts);
|
||||
dev->n_retried_writes += (attempts - 1);
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Block retiring for handling a broken block.
|
||||
*/
|
||||
|
||||
static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block)
|
||||
{
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);
|
||||
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
|
||||
yaffs2_clear_oldest_dirty_seq(dev, bi);
|
||||
|
||||
if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) {
|
||||
if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs: Failed to mark bad and erase block %d",
|
||||
flash_block);
|
||||
} else {
|
||||
struct yaffs_ext_tags tags;
|
||||
int chunk_id =
|
||||
flash_block * dev->param.chunks_per_block;
|
||||
|
||||
u8 *buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
memset(buffer, 0xff, dev->data_bytes_per_chunk);
|
||||
memset(&tags, 0, sizeof(tags));
|
||||
tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK;
|
||||
if (dev->tagger.write_chunk_tags_fn(dev, chunk_id -
|
||||
dev->chunk_offset,
|
||||
buffer,
|
||||
&tags) != YAFFS_OK)
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs: Failed to write bad block marker to block %d",
|
||||
flash_block);
|
||||
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bi->block_state = YAFFS_BLOCK_STATE_DEAD;
|
||||
bi->gc_prioritise = 0;
|
||||
bi->needs_retiring = 0;
|
||||
|
||||
dev->n_retired_blocks++;
|
||||
}
|
||||
|
||||
/*---------------- Name handling functions ------------*/
|
||||
|
||||
static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name,
|
||||
const YCHAR *oh_name, int buff_size)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_AUTO_UNICODE
|
||||
if (dev->param.auto_unicode) {
|
||||
if (*oh_name) {
|
||||
/* It is an ASCII name, do an ASCII to
|
||||
* unicode conversion */
|
||||
const char *ascii_oh_name = (const char *)oh_name;
|
||||
int n = buff_size - 1;
|
||||
while (n > 0 && *ascii_oh_name) {
|
||||
*name = *ascii_oh_name;
|
||||
name++;
|
||||
ascii_oh_name++;
|
||||
n--;
|
||||
}
|
||||
} else {
|
||||
yaffs_strncpy(name, oh_name + 1, buff_size - 1);
|
||||
}
|
||||
} else {
|
||||
#else
|
||||
(void) dev;
|
||||
{
|
||||
#endif
|
||||
yaffs_strncpy(name, oh_name, buff_size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name,
|
||||
const YCHAR *name)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_AUTO_UNICODE
|
||||
|
||||
int is_ascii;
|
||||
const YCHAR *w;
|
||||
|
||||
if (dev->param.auto_unicode) {
|
||||
|
||||
is_ascii = 1;
|
||||
w = name;
|
||||
|
||||
/* Figure out if the name will fit in ascii character set */
|
||||
while (is_ascii && *w) {
|
||||
if ((*w) & 0xff00)
|
||||
is_ascii = 0;
|
||||
w++;
|
||||
}
|
||||
|
||||
if (is_ascii) {
|
||||
/* It is an ASCII name, so convert unicode to ascii */
|
||||
char *ascii_oh_name = (char *)oh_name;
|
||||
int n = YAFFS_MAX_NAME_LENGTH - 1;
|
||||
while (n > 0 && *name) {
|
||||
*ascii_oh_name = *name;
|
||||
name++;
|
||||
ascii_oh_name++;
|
||||
n--;
|
||||
}
|
||||
} else {
|
||||
/* Unicode name, so save starting at the second YCHAR */
|
||||
*oh_name = 0;
|
||||
yaffs_strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2);
|
||||
}
|
||||
} else {
|
||||
#else
|
||||
dev = dev;
|
||||
{
|
||||
#endif
|
||||
yaffs_strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static u16 yaffs_calc_name_sum(const YCHAR *name)
|
||||
{
|
||||
u16 sum = 0;
|
||||
u16 i = 1;
|
||||
|
||||
if (!name)
|
||||
return 0;
|
||||
|
||||
while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) {
|
||||
|
||||
/* 0x1f mask is case insensitive */
|
||||
sum += ((*name) & 0x1f) * i;
|
||||
i++;
|
||||
name++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name)
|
||||
{
|
||||
memset(obj->short_name, 0, sizeof(obj->short_name));
|
||||
|
||||
if (name && !name[0]) {
|
||||
yaffs_fix_null_name(obj, obj->short_name,
|
||||
YAFFS_SHORT_NAME_LENGTH);
|
||||
name = obj->short_name;
|
||||
} else if (name &&
|
||||
yaffs_strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <=
|
||||
YAFFS_SHORT_NAME_LENGTH) {
|
||||
yaffs_strcpy(obj->short_name, name);
|
||||
}
|
||||
|
||||
obj->sum = yaffs_calc_name_sum(name);
|
||||
}
|
||||
|
||||
void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj,
|
||||
const struct yaffs_obj_hdr *oh)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_AUTO_UNICODE
|
||||
YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
memset(tmp_name, 0, sizeof(tmp_name));
|
||||
yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name,
|
||||
YAFFS_MAX_NAME_LENGTH + 1);
|
||||
yaffs_set_obj_name(obj, tmp_name);
|
||||
#else
|
||||
yaffs_set_obj_name(obj, oh->name);
|
||||
#endif
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_max_file_size(struct yaffs_dev *dev)
|
||||
{
|
||||
if(sizeof(Y_LOFF_T) < 8)
|
||||
return YAFFS_MAX_FILE_SIZE_32;
|
||||
else
|
||||
return ((Y_LOFF_T) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk;
|
||||
}
|
||||
|
||||
/*-------------------- TNODES -------------------
|
||||
|
||||
* List of spare tnodes
|
||||
* The list is hooked together using the first pointer
|
||||
* in the tnode.
|
||||
*/
|
||||
|
||||
struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev);
|
||||
|
||||
if (tn) {
|
||||
memset(tn, 0, dev->tnode_size);
|
||||
dev->n_tnodes++;
|
||||
}
|
||||
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
|
||||
return tn;
|
||||
}
|
||||
|
||||
/* FreeTnode frees up a tnode and puts it back on the free list */
|
||||
static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)
|
||||
{
|
||||
yaffs_free_raw_tnode(dev, tn);
|
||||
dev->n_tnodes--;
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
}
|
||||
|
||||
static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
yaffs_deinit_raw_tnodes_and_objs(dev);
|
||||
dev->n_obj = 0;
|
||||
dev->n_tnodes = 0;
|
||||
}
|
||||
|
||||
static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn,
|
||||
unsigned pos, unsigned val)
|
||||
{
|
||||
u32 *map = (u32 *) tn;
|
||||
u32 bit_in_map;
|
||||
u32 bit_in_word;
|
||||
u32 word_in_map;
|
||||
u32 mask;
|
||||
|
||||
pos &= YAFFS_TNODES_LEVEL0_MASK;
|
||||
val >>= dev->chunk_grp_bits;
|
||||
|
||||
bit_in_map = pos * dev->tnode_width;
|
||||
word_in_map = bit_in_map / 32;
|
||||
bit_in_word = bit_in_map & (32 - 1);
|
||||
|
||||
mask = dev->tnode_mask << bit_in_word;
|
||||
|
||||
map[word_in_map] &= ~mask;
|
||||
map[word_in_map] |= (mask & (val << bit_in_word));
|
||||
|
||||
if (dev->tnode_width > (32 - bit_in_word)) {
|
||||
bit_in_word = (32 - bit_in_word);
|
||||
word_in_map++;
|
||||
mask =
|
||||
dev->tnode_mask >> bit_in_word;
|
||||
map[word_in_map] &= ~mask;
|
||||
map[word_in_map] |= (mask & (val >> bit_in_word));
|
||||
}
|
||||
}
|
||||
|
||||
u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn,
|
||||
unsigned pos)
|
||||
{
|
||||
u32 *map = (u32 *) tn;
|
||||
u32 bit_in_map;
|
||||
u32 bit_in_word;
|
||||
u32 word_in_map;
|
||||
u32 val;
|
||||
|
||||
pos &= YAFFS_TNODES_LEVEL0_MASK;
|
||||
|
||||
bit_in_map = pos * dev->tnode_width;
|
||||
word_in_map = bit_in_map / 32;
|
||||
bit_in_word = bit_in_map & (32 - 1);
|
||||
|
||||
val = map[word_in_map] >> bit_in_word;
|
||||
|
||||
if (dev->tnode_width > (32 - bit_in_word)) {
|
||||
bit_in_word = (32 - bit_in_word);
|
||||
word_in_map++;
|
||||
val |= (map[word_in_map] << bit_in_word);
|
||||
}
|
||||
|
||||
val &= dev->tnode_mask;
|
||||
val <<= dev->chunk_grp_bits;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/* ------------------- End of individual tnode manipulation -----------------*/
|
||||
|
||||
/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------
|
||||
* The look up tree is represented by the top tnode and the number of top_level
|
||||
* in the tree. 0 means only the level 0 tnode is in the tree.
|
||||
*/
|
||||
|
||||
/* FindLevel0Tnode finds the level 0 tnode, if one exists. */
|
||||
struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct,
|
||||
u32 chunk_id)
|
||||
{
|
||||
struct yaffs_tnode *tn = file_struct->top;
|
||||
u32 i;
|
||||
int required_depth;
|
||||
int level = file_struct->top_level;
|
||||
|
||||
(void) dev;
|
||||
|
||||
/* Check sane level and chunk Id */
|
||||
if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
|
||||
return NULL;
|
||||
|
||||
if (chunk_id > YAFFS_MAX_CHUNK_ID)
|
||||
return NULL;
|
||||
|
||||
/* First check we're tall enough (ie enough top_level) */
|
||||
|
||||
i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS;
|
||||
required_depth = 0;
|
||||
while (i) {
|
||||
i >>= YAFFS_TNODES_INTERNAL_BITS;
|
||||
required_depth++;
|
||||
}
|
||||
|
||||
if (required_depth > file_struct->top_level)
|
||||
return NULL; /* Not tall enough, so we can't find it */
|
||||
|
||||
/* Traverse down to level 0 */
|
||||
while (level > 0 && tn) {
|
||||
tn = tn->internal[(chunk_id >>
|
||||
(YAFFS_TNODES_LEVEL0_BITS +
|
||||
(level - 1) *
|
||||
YAFFS_TNODES_INTERNAL_BITS)) &
|
||||
YAFFS_TNODES_INTERNAL_MASK];
|
||||
level--;
|
||||
}
|
||||
|
||||
return tn;
|
||||
}
|
||||
|
||||
/* add_find_tnode_0 finds the level 0 tnode if it exists,
|
||||
* otherwise first expands the tree.
|
||||
* This happens in two steps:
|
||||
* 1. If the tree isn't tall enough, then make it taller.
|
||||
* 2. Scan down the tree towards the level 0 tnode adding tnodes if required.
|
||||
*
|
||||
* Used when modifying the tree.
|
||||
*
|
||||
* If the tn argument is NULL, then a fresh tnode will be added otherwise the
|
||||
* specified tn will be plugged into the ttree.
|
||||
*/
|
||||
|
||||
struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct,
|
||||
u32 chunk_id,
|
||||
struct yaffs_tnode *passed_tn)
|
||||
{
|
||||
int required_depth;
|
||||
int i;
|
||||
int l;
|
||||
struct yaffs_tnode *tn;
|
||||
u32 x;
|
||||
|
||||
/* Check sane level and page Id */
|
||||
if (file_struct->top_level < 0 ||
|
||||
file_struct->top_level > YAFFS_TNODES_MAX_LEVEL)
|
||||
return NULL;
|
||||
|
||||
if (chunk_id > YAFFS_MAX_CHUNK_ID)
|
||||
return NULL;
|
||||
|
||||
/* First check we're tall enough (ie enough top_level) */
|
||||
|
||||
x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS;
|
||||
required_depth = 0;
|
||||
while (x) {
|
||||
x >>= YAFFS_TNODES_INTERNAL_BITS;
|
||||
required_depth++;
|
||||
}
|
||||
|
||||
if (required_depth > file_struct->top_level) {
|
||||
/* Not tall enough, gotta make the tree taller */
|
||||
for (i = file_struct->top_level; i < required_depth; i++) {
|
||||
|
||||
tn = yaffs_get_tnode(dev);
|
||||
|
||||
if (tn) {
|
||||
tn->internal[0] = file_struct->top;
|
||||
file_struct->top = tn;
|
||||
file_struct->top_level++;
|
||||
} else {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs: no more tnodes");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Traverse down to level 0, adding anything we need */
|
||||
|
||||
l = file_struct->top_level;
|
||||
tn = file_struct->top;
|
||||
|
||||
if (l > 0) {
|
||||
while (l > 0 && tn) {
|
||||
x = (chunk_id >>
|
||||
(YAFFS_TNODES_LEVEL0_BITS +
|
||||
(l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &
|
||||
YAFFS_TNODES_INTERNAL_MASK;
|
||||
|
||||
if ((l > 1) && !tn->internal[x]) {
|
||||
/* Add missing non-level-zero tnode */
|
||||
tn->internal[x] = yaffs_get_tnode(dev);
|
||||
if (!tn->internal[x])
|
||||
return NULL;
|
||||
} else if (l == 1) {
|
||||
/* Looking from level 1 at level 0 */
|
||||
if (passed_tn) {
|
||||
/* If we already have one, release it */
|
||||
if (tn->internal[x])
|
||||
yaffs_free_tnode(dev,
|
||||
tn->internal[x]);
|
||||
tn->internal[x] = passed_tn;
|
||||
|
||||
} else if (!tn->internal[x]) {
|
||||
/* Don't have one, none passed in */
|
||||
tn->internal[x] = yaffs_get_tnode(dev);
|
||||
if (!tn->internal[x])
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
tn = tn->internal[x];
|
||||
l--;
|
||||
}
|
||||
} else {
|
||||
/* top is level 0 */
|
||||
if (passed_tn) {
|
||||
memcpy(tn, passed_tn,
|
||||
(dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8);
|
||||
yaffs_free_tnode(dev, passed_tn);
|
||||
}
|
||||
}
|
||||
|
||||
return tn;
|
||||
}
|
||||
|
||||
static int yaffs_tags_match(const struct yaffs_ext_tags *tags, unsigned int obj_id,
|
||||
unsigned int chunk_obj)
|
||||
{
|
||||
return (tags->chunk_id == chunk_obj &&
|
||||
tags->obj_id == obj_id &&
|
||||
!tags->is_deleted) ? 1 : 0;
|
||||
|
||||
}
|
||||
|
||||
static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk,
|
||||
struct yaffs_ext_tags *tags, int obj_id,
|
||||
int inode_chunk)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) {
|
||||
if (yaffs_check_chunk_bit
|
||||
(dev, the_chunk / dev->param.chunks_per_block,
|
||||
the_chunk % dev->param.chunks_per_block)) {
|
||||
|
||||
if (dev->chunk_grp_size == 1)
|
||||
return the_chunk;
|
||||
else {
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs_find_chunk_in_group: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
if (yaffs_tags_match(tags,
|
||||
obj_id, inode_chunk)) {
|
||||
/* found it; */
|
||||
return the_chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
the_chunk++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
|
||||
struct yaffs_ext_tags *tags)
|
||||
{
|
||||
/*Get the Tnode, then get the level 0 offset chunk offset */
|
||||
struct yaffs_tnode *tn;
|
||||
int the_chunk = -1;
|
||||
struct yaffs_ext_tags local_tags;
|
||||
int ret_val = -1;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (!tags) {
|
||||
/* Passed a NULL, so use our own tags space */
|
||||
tags = &local_tags;
|
||||
}
|
||||
|
||||
tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk);
|
||||
|
||||
if (!tn)
|
||||
return ret_val;
|
||||
|
||||
the_chunk = yaffs_get_group_base(dev, tn, inode_chunk);
|
||||
|
||||
ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id,
|
||||
inode_chunk);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk,
|
||||
struct yaffs_ext_tags *tags)
|
||||
{
|
||||
/* Get the Tnode, then get the level 0 offset chunk offset */
|
||||
struct yaffs_tnode *tn;
|
||||
int the_chunk = -1;
|
||||
struct yaffs_ext_tags local_tags;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
int ret_val = -1;
|
||||
|
||||
if (!tags) {
|
||||
/* Passed a NULL, so use our own tags space */
|
||||
tags = &local_tags;
|
||||
}
|
||||
|
||||
tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk);
|
||||
|
||||
if (!tn)
|
||||
return ret_val;
|
||||
|
||||
the_chunk = yaffs_get_group_base(dev, tn, inode_chunk);
|
||||
|
||||
ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id,
|
||||
inode_chunk);
|
||||
|
||||
/* Delete the entry in the filestructure (if found) */
|
||||
if (ret_val != -1)
|
||||
yaffs_load_tnode_0(dev, tn, inode_chunk, 0);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
|
||||
int nand_chunk, int in_scan)
|
||||
{
|
||||
/* NB in_scan is zero unless scanning.
|
||||
* For forward scanning, in_scan is > 0;
|
||||
* for backward scanning in_scan is < 0
|
||||
*
|
||||
* nand_chunk = 0 is a dummy insert to make sure the tnodes are there.
|
||||
*/
|
||||
|
||||
struct yaffs_tnode *tn;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
int existing_cunk;
|
||||
struct yaffs_ext_tags existing_tags;
|
||||
struct yaffs_ext_tags new_tags;
|
||||
unsigned existing_serial, new_serial;
|
||||
|
||||
if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) {
|
||||
/* Just ignore an attempt at putting a chunk into a non-file
|
||||
* during scanning.
|
||||
* If it is not during Scanning then something went wrong!
|
||||
*/
|
||||
if (!in_scan) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy:attempt to put data chunk into a non-file"
|
||||
);
|
||||
BUG();
|
||||
}
|
||||
|
||||
yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
tn = yaffs_add_find_tnode_0(dev,
|
||||
&in->variant.file_variant,
|
||||
inode_chunk, NULL);
|
||||
if (!tn)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (!nand_chunk)
|
||||
/* Dummy insert, bail now */
|
||||
return YAFFS_OK;
|
||||
|
||||
existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk);
|
||||
|
||||
if (in_scan != 0) {
|
||||
/* If we're scanning then we need to test for duplicates
|
||||
* NB This does not need to be efficient since it should only
|
||||
* happen when the power fails during a write, then only one
|
||||
* chunk should ever be affected.
|
||||
*
|
||||
* Correction for YAFFS2: This could happen quite a lot and we
|
||||
* need to think about efficiency! TODO
|
||||
* Update: For backward scanning we don't need to re-read tags
|
||||
* so this is quite cheap.
|
||||
*/
|
||||
|
||||
if (existing_cunk > 0) {
|
||||
/* NB Right now existing chunk will not be real
|
||||
* chunk_id if the chunk group size > 1
|
||||
* thus we have to do a FindChunkInFile to get the
|
||||
* real chunk id.
|
||||
*
|
||||
* We have a duplicate now we need to decide which
|
||||
* one to use:
|
||||
*
|
||||
* Backwards scanning YAFFS2: The old one is what
|
||||
* we use, dump the new one.
|
||||
* YAFFS1: Get both sets of tags and compare serial
|
||||
* numbers.
|
||||
*/
|
||||
|
||||
if (in_scan > 0) {
|
||||
/* Only do this for forward scanning */
|
||||
if (!yaffs_rd_chunk_tags_nand(dev,
|
||||
nand_chunk,
|
||||
NULL, &new_tags))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_put_chunk_in_file: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
/* Do a proper find */
|
||||
existing_cunk =
|
||||
yaffs_find_chunk_in_file(in, inode_chunk,
|
||||
&existing_tags);
|
||||
}
|
||||
|
||||
if (existing_cunk <= 0) {
|
||||
/*Hoosterman - how did this happen? */
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: existing chunk < 0 in scan"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/* NB The deleted flags should be false, otherwise
|
||||
* the chunks will not be loaded during a scan
|
||||
*/
|
||||
|
||||
if (in_scan > 0) {
|
||||
new_serial = new_tags.serial_number;
|
||||
existing_serial = existing_tags.serial_number;
|
||||
}
|
||||
|
||||
if ((in_scan > 0) &&
|
||||
(existing_cunk <= 0 ||
|
||||
((existing_serial + 1) & 3) == new_serial)) {
|
||||
/* Forward scanning.
|
||||
* Use new
|
||||
* Delete the old one and drop through to
|
||||
* update the tnode
|
||||
*/
|
||||
yaffs_chunk_del(dev, existing_cunk, 1,
|
||||
__LINE__);
|
||||
} else {
|
||||
/* Backward scanning or we want to use the
|
||||
* existing one
|
||||
* Delete the new one and return early so that
|
||||
* the tnode isn't changed
|
||||
*/
|
||||
yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (existing_cunk == 0)
|
||||
in->n_data_chunks++;
|
||||
|
||||
yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk)
|
||||
{
|
||||
struct yaffs_block_info *the_block;
|
||||
unsigned block_no;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk);
|
||||
|
||||
block_no = chunk / dev->param.chunks_per_block;
|
||||
the_block = yaffs_get_block_info(dev, block_no);
|
||||
if (the_block) {
|
||||
the_block->soft_del_pages++;
|
||||
dev->n_free_chunks++;
|
||||
yaffs2_update_oldest_dirty_seq(dev, block_no, the_block);
|
||||
}
|
||||
}
|
||||
|
||||
/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all
|
||||
* the chunks in the file.
|
||||
* All soft deleting does is increment the block's softdelete count and pulls
|
||||
* the chunk out of the tnode.
|
||||
* Thus, essentially this is the same as DeleteWorker except that the chunks
|
||||
* are soft deleted.
|
||||
*/
|
||||
|
||||
static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn,
|
||||
u32 level, int chunk_offset)
|
||||
{
|
||||
int i;
|
||||
int the_chunk;
|
||||
int all_done = 1;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (!tn)
|
||||
return 1;
|
||||
|
||||
if (level > 0) {
|
||||
for (i = YAFFS_NTNODES_INTERNAL - 1;
|
||||
all_done && i >= 0;
|
||||
i--) {
|
||||
if (tn->internal[i]) {
|
||||
all_done =
|
||||
yaffs_soft_del_worker(in,
|
||||
tn->internal[i],
|
||||
level - 1,
|
||||
(chunk_offset <<
|
||||
YAFFS_TNODES_INTERNAL_BITS)
|
||||
+ i);
|
||||
if (all_done) {
|
||||
yaffs_free_tnode(dev,
|
||||
tn->internal[i]);
|
||||
tn->internal[i] = NULL;
|
||||
} else {
|
||||
/* Can this happen? */
|
||||
}
|
||||
}
|
||||
}
|
||||
return (all_done) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* level 0 */
|
||||
for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) {
|
||||
the_chunk = yaffs_get_group_base(dev, tn, i);
|
||||
if (the_chunk) {
|
||||
yaffs_soft_del_chunk(dev, the_chunk);
|
||||
yaffs_load_tnode_0(dev, tn, i, 0);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
struct yaffs_obj *parent;
|
||||
|
||||
yaffs_verify_obj_in_dir(obj);
|
||||
parent = obj->parent;
|
||||
|
||||
yaffs_verify_dir(parent);
|
||||
|
||||
if (dev && dev->param.remove_obj_fn)
|
||||
dev->param.remove_obj_fn(obj);
|
||||
|
||||
list_del_init(&obj->siblings);
|
||||
obj->parent = NULL;
|
||||
|
||||
yaffs_verify_dir(parent);
|
||||
}
|
||||
|
||||
void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj)
|
||||
{
|
||||
if (!directory) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: Trying to add an object to a null pointer directory"
|
||||
);
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: Trying to add an object to a non-directory"
|
||||
);
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (obj->siblings.prev == NULL) {
|
||||
/* Not initialised */
|
||||
BUG();
|
||||
}
|
||||
|
||||
yaffs_verify_dir(directory);
|
||||
|
||||
yaffs_remove_obj_from_dir(obj);
|
||||
|
||||
/* Now add it */
|
||||
list_add(&obj->siblings, &directory->variant.dir_variant.children);
|
||||
obj->parent = directory;
|
||||
|
||||
if (directory == obj->my_dev->unlinked_dir
|
||||
|| directory == obj->my_dev->del_dir) {
|
||||
obj->unlinked = 1;
|
||||
obj->my_dev->n_unlinked_files++;
|
||||
obj->rename_allowed = 0;
|
||||
}
|
||||
|
||||
yaffs_verify_dir(directory);
|
||||
yaffs_verify_obj_in_dir(obj);
|
||||
}
|
||||
|
||||
static int yaffs_change_obj_name(struct yaffs_obj *obj,
|
||||
struct yaffs_obj *new_dir,
|
||||
const YCHAR *new_name, int force, int shadows)
|
||||
{
|
||||
int unlink_op;
|
||||
int del_op;
|
||||
struct yaffs_obj *existing_target;
|
||||
|
||||
if (new_dir == NULL)
|
||||
new_dir = obj->parent; /* use the old directory */
|
||||
|
||||
if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: yaffs_change_obj_name: new_dir is not a directory"
|
||||
);
|
||||
BUG();
|
||||
}
|
||||
|
||||
unlink_op = (new_dir == obj->my_dev->unlinked_dir);
|
||||
del_op = (new_dir == obj->my_dev->del_dir);
|
||||
|
||||
existing_target = yaffs_find_by_name(new_dir, new_name);
|
||||
|
||||
/* If the object is a file going into the unlinked directory,
|
||||
* then it is OK to just stuff it in since duplicate names are OK.
|
||||
* else only proceed if the new name does not exist and we're putting
|
||||
* it into a directory.
|
||||
*/
|
||||
if (!(unlink_op || del_op || force ||
|
||||
shadows > 0 || !existing_target) ||
|
||||
new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
yaffs_set_obj_name(obj, new_name);
|
||||
obj->dirty = 1;
|
||||
yaffs_add_obj_to_dir(new_dir, obj);
|
||||
|
||||
if (unlink_op)
|
||||
obj->unlinked = 1;
|
||||
|
||||
/* If it is a deletion then we mark it as a shrink for gc */
|
||||
if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0)
|
||||
return YAFFS_OK;
|
||||
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/*------------------------ Short Operations Cache ------------------------------
|
||||
* In many situations where there is no high level buffering a lot of
|
||||
* reads might be short sequential reads, and a lot of writes may be short
|
||||
* sequential writes. eg. scanning/writing a jpeg file.
|
||||
* In these cases, a short read/write cache can provide a huge perfomance
|
||||
* benefit with dumb-as-a-rock code.
|
||||
* In Linux, the page cache provides read buffering and the short op cache
|
||||
* provides write buffering.
|
||||
*
|
||||
* There are a small number (~10) of cache chunks per device so that we don't
|
||||
* need a very intelligent search.
|
||||
*/
|
||||
|
||||
static int yaffs_obj_cache_dirty(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int i;
|
||||
struct yaffs_cache *cache;
|
||||
int n_caches = obj->my_dev->param.n_caches;
|
||||
|
||||
for (i = 0; i < n_caches; i++) {
|
||||
cache = &dev->cache[i];
|
||||
if (cache->object == obj && cache->dirty)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void yaffs_flush_single_cache(struct yaffs_cache *cache, int discard)
|
||||
{
|
||||
|
||||
if (!cache || cache->locked)
|
||||
return;
|
||||
|
||||
/* Write it out and free it up if need be.*/
|
||||
if (cache->dirty) {
|
||||
yaffs_wr_data_obj(cache->object,
|
||||
cache->chunk_id,
|
||||
cache->data,
|
||||
cache->n_bytes,
|
||||
1);
|
||||
|
||||
cache->dirty = 0;
|
||||
}
|
||||
|
||||
if (discard)
|
||||
cache->object = NULL;
|
||||
}
|
||||
|
||||
static void yaffs_flush_file_cache(struct yaffs_obj *obj, int discard)
|
||||
{
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int i;
|
||||
struct yaffs_cache *cache;
|
||||
int n_caches = obj->my_dev->param.n_caches;
|
||||
|
||||
if (n_caches < 1)
|
||||
return;
|
||||
|
||||
|
||||
/* Find the chunks for this object and flush them. */
|
||||
for (i = 0; i < n_caches; i++) {
|
||||
cache = &dev->cache[i];
|
||||
if (cache->object == obj)
|
||||
yaffs_flush_single_cache(cache, discard);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
int n_caches = dev->param.n_caches;
|
||||
int i;
|
||||
|
||||
/* Find a dirty object in the cache and flush it...
|
||||
* until there are no further dirty objects.
|
||||
*/
|
||||
do {
|
||||
obj = NULL;
|
||||
for (i = 0; i < n_caches && !obj; i++) {
|
||||
if (dev->cache[i].object && dev->cache[i].dirty)
|
||||
obj = dev->cache[i].object;
|
||||
}
|
||||
if (obj)
|
||||
yaffs_flush_file_cache(obj, discard);
|
||||
} while (obj);
|
||||
|
||||
}
|
||||
|
||||
/* Grab us an unused cache chunk for use.
|
||||
* First look for an empty one.
|
||||
* Then look for the least recently used non-dirty one.
|
||||
* Then look for the least recently used dirty one...., flush and look again.
|
||||
*/
|
||||
static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dev->param.n_caches > 0) {
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
if (!dev->cache[i].object)
|
||||
return &dev->cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_cache *cache;
|
||||
int usage;
|
||||
int i;
|
||||
|
||||
if (dev->param.n_caches < 1)
|
||||
return NULL;
|
||||
|
||||
/* First look for an unused cache */
|
||||
|
||||
cache = yaffs_grab_chunk_worker(dev);
|
||||
|
||||
if (cache)
|
||||
return cache;
|
||||
|
||||
/*
|
||||
* Thery were all in use.
|
||||
* Find the LRU cache and flush it if it is dirty.
|
||||
*/
|
||||
|
||||
usage = -1;
|
||||
cache = NULL;
|
||||
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
if (dev->cache[i].object &&
|
||||
!dev->cache[i].locked &&
|
||||
(dev->cache[i].last_use < usage || !cache)) {
|
||||
usage = dev->cache[i].last_use;
|
||||
cache = &dev->cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
yaffs_flush_single_cache(cache, 1);
|
||||
#else
|
||||
yaffs_flush_file_cache(cache->object, 1);
|
||||
cache = yaffs_grab_chunk_worker(dev);
|
||||
#endif
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/* Find a cached chunk */
|
||||
static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj,
|
||||
int chunk_id)
|
||||
{
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int i;
|
||||
|
||||
if (dev->param.n_caches < 1)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
if (dev->cache[i].object == obj &&
|
||||
dev->cache[i].chunk_id == chunk_id) {
|
||||
dev->cache_hits++;
|
||||
|
||||
return &dev->cache[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Mark the chunk for the least recently used algorithym */
|
||||
static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache,
|
||||
int is_write)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dev->param.n_caches < 1)
|
||||
return;
|
||||
|
||||
if (dev->cache_last_use < 0 ||
|
||||
dev->cache_last_use > 100000000) {
|
||||
/* Reset the cache usages */
|
||||
for (i = 1; i < dev->param.n_caches; i++)
|
||||
dev->cache[i].last_use = 0;
|
||||
|
||||
dev->cache_last_use = 0;
|
||||
}
|
||||
dev->cache_last_use++;
|
||||
cache->last_use = dev->cache_last_use;
|
||||
|
||||
if (is_write)
|
||||
cache->dirty = 1;
|
||||
}
|
||||
|
||||
/* Invalidate a single cache page.
|
||||
* Do this when a whole page gets written,
|
||||
* ie the short cache for this page is no longer valid.
|
||||
*/
|
||||
static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id)
|
||||
{
|
||||
struct yaffs_cache *cache;
|
||||
|
||||
if (object->my_dev->param.n_caches > 0) {
|
||||
cache = yaffs_find_chunk_cache(object, chunk_id);
|
||||
|
||||
if (cache)
|
||||
cache->object = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Invalidate all the cache pages associated with this object
|
||||
* Do this whenever ther file is deleted or resized.
|
||||
*/
|
||||
static void yaffs_invalidate_whole_cache(struct yaffs_obj *in)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (dev->param.n_caches > 0) {
|
||||
/* Invalidate it. */
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
if (dev->cache[i].object == in)
|
||||
dev->cache[i].object = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_unhash_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
int bucket;
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
|
||||
/* If it is still linked into the bucket list, free from the list */
|
||||
if (!list_empty(&obj->hash_link)) {
|
||||
list_del_init(&obj->hash_link);
|
||||
bucket = yaffs_hash_fn(obj->obj_id);
|
||||
dev->obj_bucket[bucket].count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* FreeObject frees up a Object and puts it back on the free list */
|
||||
static void yaffs_free_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
if (!obj) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
dev = obj->my_dev;
|
||||
yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p",
|
||||
obj, obj->my_inode);
|
||||
if (obj->parent)
|
||||
BUG();
|
||||
if (!list_empty(&obj->siblings))
|
||||
BUG();
|
||||
|
||||
if (obj->my_inode) {
|
||||
/* We're still hooked up to a cached inode.
|
||||
* Don't delete now, but mark for later deletion
|
||||
*/
|
||||
obj->defered_free = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
yaffs_unhash_obj(obj);
|
||||
|
||||
yaffs_free_raw_obj(dev, obj);
|
||||
dev->n_obj--;
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
}
|
||||
|
||||
void yaffs_handle_defered_free(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj->defered_free)
|
||||
yaffs_free_obj(obj);
|
||||
}
|
||||
|
||||
static int yaffs_generic_obj_del(struct yaffs_obj *in)
|
||||
{
|
||||
/* Iinvalidate the file's data in the cache, without flushing. */
|
||||
yaffs_invalidate_whole_cache(in);
|
||||
|
||||
if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) {
|
||||
/* Move to unlinked directory so we have a deletion record */
|
||||
yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0,
|
||||
0);
|
||||
}
|
||||
|
||||
yaffs_remove_obj_from_dir(in);
|
||||
yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__);
|
||||
in->hdr_chunk = 0;
|
||||
|
||||
yaffs_free_obj(in);
|
||||
return YAFFS_OK;
|
||||
|
||||
}
|
||||
|
||||
static void yaffs_soft_del_file(struct yaffs_obj *obj)
|
||||
{
|
||||
if (!obj->deleted ||
|
||||
obj->variant_type != YAFFS_OBJECT_TYPE_FILE ||
|
||||
obj->soft_del)
|
||||
return;
|
||||
|
||||
if (obj->n_data_chunks <= 0) {
|
||||
/* Empty file with no duplicate object headers,
|
||||
* just delete it immediately */
|
||||
yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top);
|
||||
obj->variant.file_variant.top = NULL;
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"yaffs: Deleting empty file %d",
|
||||
obj->obj_id);
|
||||
yaffs_generic_obj_del(obj);
|
||||
} else {
|
||||
yaffs_soft_del_worker(obj,
|
||||
obj->variant.file_variant.top,
|
||||
obj->variant.
|
||||
file_variant.top_level, 0);
|
||||
obj->soft_del = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pruning removes any part of the file structure tree that is beyond the
|
||||
* bounds of the file (ie that does not point to chunks).
|
||||
*
|
||||
* A file should only get pruned when its size is reduced.
|
||||
*
|
||||
* Before pruning, the chunks must be pulled from the tree and the
|
||||
* level 0 tnode entries must be zeroed out.
|
||||
* Could also use this for file deletion, but that's probably better handled
|
||||
* by a special case.
|
||||
*
|
||||
* This function is recursive. For levels > 0 the function is called again on
|
||||
* any sub-tree. For level == 0 we just check if the sub-tree has data.
|
||||
* If there is no data in a subtree then it is pruned.
|
||||
*/
|
||||
|
||||
static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev,
|
||||
struct yaffs_tnode *tn, u32 level,
|
||||
int del0)
|
||||
{
|
||||
int i;
|
||||
int has_data;
|
||||
|
||||
if (!tn)
|
||||
return tn;
|
||||
|
||||
has_data = 0;
|
||||
|
||||
if (level > 0) {
|
||||
for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) {
|
||||
if (tn->internal[i]) {
|
||||
tn->internal[i] =
|
||||
yaffs_prune_worker(dev,
|
||||
tn->internal[i],
|
||||
level - 1,
|
||||
(i == 0) ? del0 : 1);
|
||||
}
|
||||
|
||||
if (tn->internal[i])
|
||||
has_data++;
|
||||
}
|
||||
} else {
|
||||
int tnode_size_u32 = dev->tnode_size / sizeof(u32);
|
||||
u32 *map = (u32 *) tn;
|
||||
|
||||
for (i = 0; !has_data && i < tnode_size_u32; i++) {
|
||||
if (map[i])
|
||||
has_data++;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_data == 0 && del0) {
|
||||
/* Free and return NULL */
|
||||
yaffs_free_tnode(dev, tn);
|
||||
tn = NULL;
|
||||
}
|
||||
return tn;
|
||||
}
|
||||
|
||||
static int yaffs_prune_tree(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct)
|
||||
{
|
||||
int i;
|
||||
int has_data;
|
||||
int done = 0;
|
||||
struct yaffs_tnode *tn;
|
||||
|
||||
if (file_struct->top_level < 1)
|
||||
return YAFFS_OK;
|
||||
|
||||
file_struct->top =
|
||||
yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0);
|
||||
|
||||
/* Now we have a tree with all the non-zero branches NULL but
|
||||
* the height is the same as it was.
|
||||
* Let's see if we can trim internal tnodes to shorten the tree.
|
||||
* We can do this if only the 0th element in the tnode is in use
|
||||
* (ie all the non-zero are NULL)
|
||||
*/
|
||||
|
||||
while (file_struct->top_level && !done) {
|
||||
tn = file_struct->top;
|
||||
|
||||
has_data = 0;
|
||||
for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) {
|
||||
if (tn->internal[i])
|
||||
has_data++;
|
||||
}
|
||||
|
||||
if (!has_data) {
|
||||
file_struct->top = tn->internal[0];
|
||||
file_struct->top_level--;
|
||||
yaffs_free_tnode(dev, tn);
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
/*-------------------- End of File Structure functions.-------------------*/
|
||||
|
||||
/* alloc_empty_obj gets us a clean Object.*/
|
||||
static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev);
|
||||
|
||||
if (!obj)
|
||||
return obj;
|
||||
|
||||
dev->n_obj++;
|
||||
|
||||
/* Now sweeten it up... */
|
||||
|
||||
memset(obj, 0, sizeof(struct yaffs_obj));
|
||||
obj->being_created = 1;
|
||||
|
||||
obj->my_dev = dev;
|
||||
obj->hdr_chunk = 0;
|
||||
obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN;
|
||||
INIT_LIST_HEAD(&(obj->hard_links));
|
||||
INIT_LIST_HEAD(&(obj->hash_link));
|
||||
INIT_LIST_HEAD(&obj->siblings);
|
||||
|
||||
/* Now make the directory sane */
|
||||
if (dev->root_dir) {
|
||||
obj->parent = dev->root_dir;
|
||||
list_add(&(obj->siblings),
|
||||
&dev->root_dir->variant.dir_variant.children);
|
||||
}
|
||||
|
||||
/* Add it to the lost and found directory.
|
||||
* NB Can't put root or lost-n-found in lost-n-found so
|
||||
* check if lost-n-found exists first
|
||||
*/
|
||||
if (dev->lost_n_found)
|
||||
yaffs_add_obj_to_dir(dev->lost_n_found, obj);
|
||||
|
||||
obj->being_created = 0;
|
||||
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static int yaffs_find_nice_bucket(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
int l = 999;
|
||||
int lowest = 999999;
|
||||
|
||||
/* Search for the shortest list or one that
|
||||
* isn't too long.
|
||||
*/
|
||||
|
||||
for (i = 0; i < 10 && lowest > 4; i++) {
|
||||
dev->bucket_finder++;
|
||||
dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS;
|
||||
if (dev->obj_bucket[dev->bucket_finder].count < lowest) {
|
||||
lowest = dev->obj_bucket[dev->bucket_finder].count;
|
||||
l = dev->bucket_finder;
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static int yaffs_new_obj_id(struct yaffs_dev *dev)
|
||||
{
|
||||
int bucket = yaffs_find_nice_bucket(dev);
|
||||
int found = 0;
|
||||
struct list_head *i;
|
||||
u32 n = (u32) bucket;
|
||||
|
||||
/* Now find an object value that has not already been taken
|
||||
* by scanning the list.
|
||||
*/
|
||||
|
||||
while (!found) {
|
||||
found = 1;
|
||||
n += YAFFS_NOBJECT_BUCKETS;
|
||||
if (1 || dev->obj_bucket[bucket].count > 0) {
|
||||
list_for_each(i, &dev->obj_bucket[bucket].list) {
|
||||
/* If there is already one in the list */
|
||||
if (i && list_entry(i, struct yaffs_obj,
|
||||
hash_link)->obj_id == n) {
|
||||
found = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static void yaffs_hash_obj(struct yaffs_obj *in)
|
||||
{
|
||||
int bucket = yaffs_hash_fn(in->obj_id);
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
list_add(&in->hash_link, &dev->obj_bucket[bucket].list);
|
||||
dev->obj_bucket[bucket].count++;
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number)
|
||||
{
|
||||
int bucket = yaffs_hash_fn(number);
|
||||
struct list_head *i;
|
||||
struct yaffs_obj *in;
|
||||
|
||||
list_for_each(i, &dev->obj_bucket[bucket].list) {
|
||||
/* Look if it is in the list */
|
||||
in = list_entry(i, struct yaffs_obj, hash_link);
|
||||
if (in->obj_id == number) {
|
||||
/* Don't show if it is defered free */
|
||||
if (in->defered_free)
|
||||
return NULL;
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number,
|
||||
enum yaffs_obj_type type)
|
||||
{
|
||||
struct yaffs_obj *the_obj = NULL;
|
||||
struct yaffs_tnode *tn = NULL;
|
||||
|
||||
if (number < 0)
|
||||
number = yaffs_new_obj_id(dev);
|
||||
|
||||
if (type == YAFFS_OBJECT_TYPE_FILE) {
|
||||
tn = yaffs_get_tnode(dev);
|
||||
if (!tn)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
the_obj = yaffs_alloc_empty_obj(dev);
|
||||
if (!the_obj) {
|
||||
if (tn)
|
||||
yaffs_free_tnode(dev, tn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
the_obj->fake = 0;
|
||||
the_obj->rename_allowed = 1;
|
||||
the_obj->unlink_allowed = 1;
|
||||
the_obj->obj_id = number;
|
||||
yaffs_hash_obj(the_obj);
|
||||
the_obj->variant_type = type;
|
||||
yaffs_load_current_time(the_obj, 1, 1);
|
||||
|
||||
switch (type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
the_obj->variant.file_variant.file_size = 0;
|
||||
the_obj->variant.file_variant.scanned_size = 0;
|
||||
the_obj->variant.file_variant.shrink_size =
|
||||
yaffs_max_file_size(dev);
|
||||
the_obj->variant.file_variant.top_level = 0;
|
||||
the_obj->variant.file_variant.top = tn;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
INIT_LIST_HEAD(&the_obj->variant.dir_variant.children);
|
||||
INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
/* No action required */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* todo this should not happen */
|
||||
break;
|
||||
}
|
||||
return the_obj;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev,
|
||||
int number, u32 mode)
|
||||
{
|
||||
|
||||
struct yaffs_obj *obj =
|
||||
yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
obj->fake = 1; /* it is fake so it might not use NAND */
|
||||
obj->rename_allowed = 0;
|
||||
obj->unlink_allowed = 0;
|
||||
obj->deleted = 0;
|
||||
obj->unlinked = 0;
|
||||
obj->yst_mode = mode;
|
||||
obj->my_dev = dev;
|
||||
obj->hdr_chunk = 0; /* Not a valid chunk. */
|
||||
return obj;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->n_obj = 0;
|
||||
dev->n_tnodes = 0;
|
||||
yaffs_init_raw_tnodes_and_objs(dev);
|
||||
|
||||
for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
|
||||
INIT_LIST_HEAD(&dev->obj_bucket[i].list);
|
||||
dev->obj_bucket[i].count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev,
|
||||
int number,
|
||||
enum yaffs_obj_type type)
|
||||
{
|
||||
struct yaffs_obj *the_obj = NULL;
|
||||
|
||||
if (number > 0)
|
||||
the_obj = yaffs_find_by_number(dev, number);
|
||||
|
||||
if (!the_obj)
|
||||
the_obj = yaffs_new_obj(dev, number, type);
|
||||
|
||||
return the_obj;
|
||||
|
||||
}
|
||||
|
||||
YCHAR *yaffs_clone_str(const YCHAR *str)
|
||||
{
|
||||
YCHAR *new_str = NULL;
|
||||
int len;
|
||||
|
||||
if (!str)
|
||||
str = _Y("");
|
||||
|
||||
len = yaffs_strnlen(str, YAFFS_MAX_ALIAS_LENGTH);
|
||||
new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS);
|
||||
if (new_str) {
|
||||
yaffs_strncpy(new_str, str, len);
|
||||
new_str[len] = 0;
|
||||
}
|
||||
return new_str;
|
||||
|
||||
}
|
||||
/*
|
||||
*yaffs_update_parent() handles fixing a directories mtime and ctime when a new
|
||||
* link (ie. name) is created or deleted in the directory.
|
||||
*
|
||||
* ie.
|
||||
* create dir/a : update dir's mtime/ctime
|
||||
* rm dir/a: update dir's mtime/ctime
|
||||
* modify dir/a: don't update dir's mtimme/ctime
|
||||
*
|
||||
* This can be handled immediately or defered. Defering helps reduce the number
|
||||
* of updates when many files in a directory are changed within a brief period.
|
||||
*
|
||||
* If the directory updating is defered then yaffs_update_dirty_dirs must be
|
||||
* called periodically.
|
||||
*/
|
||||
|
||||
static void yaffs_update_parent(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
dev = obj->my_dev;
|
||||
obj->dirty = 1;
|
||||
yaffs_load_current_time(obj, 0, 1);
|
||||
if (dev->param.defered_dir_update) {
|
||||
struct list_head *link = &obj->variant.dir_variant.dirty;
|
||||
|
||||
if (list_empty(link)) {
|
||||
list_add(link, &dev->dirty_dirs);
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND,
|
||||
"Added object %d to dirty directories",
|
||||
obj->obj_id);
|
||||
}
|
||||
|
||||
} else {
|
||||
yaffs_update_oh(obj, NULL, 0, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_update_dirty_dirs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct list_head *link;
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_dir_var *d_s;
|
||||
union yaffs_obj_var *o_v;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories");
|
||||
|
||||
while (!list_empty(&dev->dirty_dirs)) {
|
||||
link = dev->dirty_dirs.next;
|
||||
list_del_init(link);
|
||||
|
||||
d_s = list_entry(link, struct yaffs_dir_var, dirty);
|
||||
o_v = list_entry(d_s, union yaffs_obj_var, dir_variant);
|
||||
obj = list_entry(o_v, struct yaffs_obj, variant);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d",
|
||||
obj->obj_id);
|
||||
|
||||
if (obj->dirty)
|
||||
yaffs_update_oh(obj, NULL, 0, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Mknod (create) a new object.
|
||||
* equiv_obj only has meaning for a hard link;
|
||||
* alias_str only has meaning for a symlink.
|
||||
* rdev only has meaning for devices (a subset of special objects)
|
||||
*/
|
||||
|
||||
static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type,
|
||||
struct yaffs_obj *parent,
|
||||
const YCHAR *name,
|
||||
u32 mode,
|
||||
u32 uid,
|
||||
u32 gid,
|
||||
struct yaffs_obj *equiv_obj,
|
||||
const YCHAR *alias_str, u32 rdev)
|
||||
{
|
||||
struct yaffs_obj *in;
|
||||
YCHAR *str = NULL;
|
||||
struct yaffs_dev *dev = parent->my_dev;
|
||||
|
||||
/* Check if the entry exists.
|
||||
* If it does then fail the call since we don't want a dup. */
|
||||
if (yaffs_find_by_name(parent, name))
|
||||
return NULL;
|
||||
|
||||
if (type == YAFFS_OBJECT_TYPE_SYMLINK) {
|
||||
str = yaffs_clone_str(alias_str);
|
||||
if (!str)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
in = yaffs_new_obj(dev, -1, type);
|
||||
|
||||
if (!in) {
|
||||
kfree(str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
in->hdr_chunk = 0;
|
||||
in->valid = 1;
|
||||
in->variant_type = type;
|
||||
|
||||
in->yst_mode = mode;
|
||||
|
||||
yaffs_attribs_init(in, gid, uid, rdev);
|
||||
|
||||
in->n_data_chunks = 0;
|
||||
|
||||
yaffs_set_obj_name(in, name);
|
||||
in->dirty = 1;
|
||||
|
||||
yaffs_add_obj_to_dir(parent, in);
|
||||
|
||||
in->my_dev = parent->my_dev;
|
||||
|
||||
switch (type) {
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
in->variant.symlink_variant.alias = str;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
in->variant.hardlink_variant.equiv_obj = equiv_obj;
|
||||
in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id;
|
||||
list_add(&in->hard_links, &equiv_obj->hard_links);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) {
|
||||
/* Could not create the object header, fail */
|
||||
yaffs_del_obj(in);
|
||||
in = NULL;
|
||||
}
|
||||
|
||||
if (in)
|
||||
yaffs_update_parent(parent);
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid)
|
||||
{
|
||||
return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode,
|
||||
uid, gid, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name,
|
||||
u32 mode, u32 uid, u32 gid)
|
||||
{
|
||||
return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name,
|
||||
mode, uid, gid, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid, u32 rdev)
|
||||
{
|
||||
return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode,
|
||||
uid, gid, NULL, NULL, rdev);
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid, const YCHAR *alias)
|
||||
{
|
||||
return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode,
|
||||
uid, gid, NULL, alias, 0);
|
||||
}
|
||||
|
||||
/* yaffs_link_obj returns the object id of the equivalent object.*/
|
||||
struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name,
|
||||
struct yaffs_obj *equiv_obj)
|
||||
{
|
||||
/* Get the real object in case we were fed a hard link obj */
|
||||
equiv_obj = yaffs_get_equivalent_obj(equiv_obj);
|
||||
|
||||
if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK,
|
||||
parent, name, 0, 0, 0,
|
||||
equiv_obj, NULL, 0))
|
||||
return equiv_obj;
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---------------------- Block Management and Page Allocation -------------*/
|
||||
|
||||
static void yaffs_deinit_blocks(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->block_info_alt && dev->block_info)
|
||||
vfree(dev->block_info);
|
||||
else
|
||||
kfree(dev->block_info);
|
||||
|
||||
dev->block_info_alt = 0;
|
||||
|
||||
dev->block_info = NULL;
|
||||
|
||||
if (dev->chunk_bits_alt && dev->chunk_bits)
|
||||
vfree(dev->chunk_bits);
|
||||
else
|
||||
kfree(dev->chunk_bits);
|
||||
dev->chunk_bits_alt = 0;
|
||||
dev->chunk_bits = NULL;
|
||||
}
|
||||
|
||||
static int yaffs_init_blocks(struct yaffs_dev *dev)
|
||||
{
|
||||
int n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
|
||||
|
||||
dev->block_info = NULL;
|
||||
dev->chunk_bits = NULL;
|
||||
dev->alloc_block = -1; /* force it to get a new one */
|
||||
|
||||
/* If the first allocation strategy fails, thry the alternate one */
|
||||
dev->block_info =
|
||||
kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS);
|
||||
if (!dev->block_info) {
|
||||
dev->block_info =
|
||||
vmalloc(n_blocks * sizeof(struct yaffs_block_info));
|
||||
dev->block_info_alt = 1;
|
||||
} else {
|
||||
dev->block_info_alt = 0;
|
||||
}
|
||||
|
||||
if (!dev->block_info)
|
||||
goto alloc_error;
|
||||
|
||||
/* Set up dynamic blockinfo stuff. Round up bytes. */
|
||||
dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8;
|
||||
dev->chunk_bits =
|
||||
kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS);
|
||||
if (!dev->chunk_bits) {
|
||||
dev->chunk_bits =
|
||||
vmalloc(dev->chunk_bit_stride * n_blocks);
|
||||
dev->chunk_bits_alt = 1;
|
||||
} else {
|
||||
dev->chunk_bits_alt = 0;
|
||||
}
|
||||
if (!dev->chunk_bits)
|
||||
goto alloc_error;
|
||||
|
||||
|
||||
memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info));
|
||||
memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks);
|
||||
return YAFFS_OK;
|
||||
|
||||
alloc_error:
|
||||
yaffs_deinit_blocks(dev);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
|
||||
void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no)
|
||||
{
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no);
|
||||
int erased_ok = 0;
|
||||
int i;
|
||||
|
||||
/* If the block is still healthy erase it and mark as clean.
|
||||
* If the block has had a data failure, then retire it.
|
||||
*/
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,
|
||||
"yaffs_block_became_dirty block %d state %d %s",
|
||||
block_no, bi->block_state,
|
||||
(bi->needs_retiring) ? "needs retiring" : "");
|
||||
|
||||
yaffs2_clear_oldest_dirty_seq(dev, bi);
|
||||
|
||||
bi->block_state = YAFFS_BLOCK_STATE_DIRTY;
|
||||
|
||||
/* If this is the block being garbage collected then stop gc'ing */
|
||||
if ((u32)block_no == dev->gc_block)
|
||||
dev->gc_block = 0;
|
||||
|
||||
/* If this block is currently the best candidate for gc
|
||||
* then drop as a candidate */
|
||||
if ((u32)block_no == dev->gc_dirtiest) {
|
||||
dev->gc_dirtiest = 0;
|
||||
dev->gc_pages_in_use = 0;
|
||||
}
|
||||
|
||||
if (!bi->needs_retiring) {
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
erased_ok = yaffs_erase_block(dev, block_no);
|
||||
if (!erased_ok) {
|
||||
dev->n_erase_failures++;
|
||||
yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
||||
"**>> Erasure failed %d", block_no);
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify erasure if needed */
|
||||
if (erased_ok &&
|
||||
((yaffs_trace_mask & YAFFS_TRACE_ERASE) ||
|
||||
!yaffs_skip_verification(dev))) {
|
||||
for (i = 0; i < dev->param.chunks_per_block; i++) {
|
||||
if (!yaffs_check_chunk_erased(dev,
|
||||
block_no * dev->param.chunks_per_block + i)) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
">>Block %d erasure supposedly OK, but chunk %d not erased",
|
||||
block_no, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!erased_ok) {
|
||||
/* We lost a block of free space */
|
||||
dev->n_free_chunks -= dev->param.chunks_per_block;
|
||||
yaffs_retire_block(dev, block_no);
|
||||
yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
||||
"**>> Block %d retired", block_no);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clean it up... */
|
||||
bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
bi->seq_number = 0;
|
||||
dev->n_erased_blocks++;
|
||||
bi->pages_in_use = 0;
|
||||
bi->soft_del_pages = 0;
|
||||
bi->has_shrink_hdr = 0;
|
||||
bi->skip_erased_check = 1; /* Clean, so no need to check */
|
||||
bi->gc_prioritise = 0;
|
||||
bi->has_summary = 0;
|
||||
|
||||
yaffs_clear_chunk_bits(dev, block_no);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no);
|
||||
}
|
||||
|
||||
static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi,
|
||||
int old_chunk, u8 *buffer)
|
||||
{
|
||||
int new_chunk;
|
||||
int mark_flash = 1;
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_obj *object;
|
||||
int matching_chunk;
|
||||
int ret_val = YAFFS_OK;
|
||||
|
||||
memset(&tags, 0, sizeof(tags));
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, old_chunk,
|
||||
buffer, &tags))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_gc_process_chunk: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
object = yaffs_find_by_number(dev, tags.obj_id);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_GC_DETAIL,
|
||||
"Collecting chunk in block %d, %d %d %d ",
|
||||
dev->gc_chunk, tags.obj_id,
|
||||
tags.chunk_id, tags.n_bytes);
|
||||
|
||||
if (object && !yaffs_skip_verification(dev)) {
|
||||
if (tags.chunk_id == 0)
|
||||
matching_chunk =
|
||||
object->hdr_chunk;
|
||||
else if (object->soft_del)
|
||||
/* Defeat the test */
|
||||
matching_chunk = old_chunk;
|
||||
else
|
||||
matching_chunk =
|
||||
yaffs_find_chunk_in_file
|
||||
(object, tags.chunk_id,
|
||||
NULL);
|
||||
|
||||
if (old_chunk != matching_chunk)
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"gc: page in gc mismatch: %d %d %d %d",
|
||||
old_chunk,
|
||||
matching_chunk,
|
||||
tags.obj_id,
|
||||
tags.chunk_id);
|
||||
}
|
||||
|
||||
if (!object) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"page %d in gc has no object: %d %d %d ",
|
||||
old_chunk,
|
||||
tags.obj_id, tags.chunk_id,
|
||||
tags.n_bytes);
|
||||
}
|
||||
|
||||
if (object &&
|
||||
object->deleted &&
|
||||
object->soft_del && tags.chunk_id != 0) {
|
||||
/* Data chunk in a soft deleted file,
|
||||
* throw it away.
|
||||
* It's a soft deleted data chunk,
|
||||
* No need to copy this, just forget
|
||||
* about it and fix up the object.
|
||||
*/
|
||||
|
||||
/* Free chunks already includes
|
||||
* softdeleted chunks, how ever this
|
||||
* chunk is going to soon be really
|
||||
* deleted which will increment free
|
||||
* chunks. We have to decrement free
|
||||
* chunks so this works out properly.
|
||||
*/
|
||||
dev->n_free_chunks--;
|
||||
bi->soft_del_pages--;
|
||||
|
||||
object->n_data_chunks--;
|
||||
if (object->n_data_chunks <= 0) {
|
||||
/* remeber to clean up obj */
|
||||
dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id;
|
||||
dev->n_clean_ups++;
|
||||
}
|
||||
mark_flash = 0;
|
||||
} else if (object) {
|
||||
/* It's either a data chunk in a live
|
||||
* file or an ObjectHeader, so we're
|
||||
* interested in it.
|
||||
* NB Need to keep the ObjectHeaders of
|
||||
* deleted files until the whole file
|
||||
* has been deleted off
|
||||
*/
|
||||
tags.serial_number++;
|
||||
dev->n_gc_copies++;
|
||||
|
||||
if (tags.chunk_id == 0) {
|
||||
/* It is an object Id,
|
||||
* We need to nuke the
|
||||
* shrinkheader flags since its
|
||||
* work is done.
|
||||
* Also need to clean up
|
||||
* shadowing.
|
||||
*/
|
||||
struct yaffs_obj_hdr *oh;
|
||||
oh = (struct yaffs_obj_hdr *) buffer;
|
||||
|
||||
oh->is_shrink = 0;
|
||||
tags.extra_is_shrink = 0;
|
||||
oh->shadows_obj = 0;
|
||||
oh->inband_shadowed_obj_id = 0;
|
||||
tags.extra_shadows = 0;
|
||||
|
||||
/* Update file size */
|
||||
if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) {
|
||||
yaffs_oh_size_load(oh,
|
||||
object->variant.file_variant.file_size);
|
||||
tags.extra_file_size =
|
||||
object->variant.file_variant.file_size;
|
||||
}
|
||||
|
||||
yaffs_verify_oh(object, oh, &tags, 1);
|
||||
new_chunk =
|
||||
yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1);
|
||||
} else {
|
||||
new_chunk =
|
||||
yaffs_write_new_chunk(dev, buffer, &tags, 1);
|
||||
}
|
||||
|
||||
if (new_chunk < 0) {
|
||||
ret_val = YAFFS_FAIL;
|
||||
} else {
|
||||
|
||||
/* Now fix up the Tnodes etc. */
|
||||
|
||||
if (tags.chunk_id == 0) {
|
||||
/* It's a header */
|
||||
object->hdr_chunk = new_chunk;
|
||||
object->serial = tags.serial_number;
|
||||
} else {
|
||||
/* It's a data chunk */
|
||||
yaffs_put_chunk_in_file(object, tags.chunk_id,
|
||||
new_chunk, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret_val == YAFFS_OK)
|
||||
yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block)
|
||||
{
|
||||
int old_chunk;
|
||||
int ret_val = YAFFS_OK;
|
||||
u32 i;
|
||||
int is_checkpt_block;
|
||||
int max_copies;
|
||||
int chunks_before = yaffs_get_erased_chunks(dev);
|
||||
int chunks_after;
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, block);
|
||||
|
||||
is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"Collecting block %d, in use %d, shrink %d, whole_block %d",
|
||||
block, bi->pages_in_use, bi->has_shrink_hdr,
|
||||
whole_block);
|
||||
|
||||
/*yaffs_verify_free_chunks(dev); */
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_FULL)
|
||||
bi->block_state = YAFFS_BLOCK_STATE_COLLECTING;
|
||||
|
||||
bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */
|
||||
|
||||
dev->gc_disable = 1;
|
||||
|
||||
yaffs_summary_gc(dev, block);
|
||||
|
||||
if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) {
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"Collecting block %d that has no chunks in use",
|
||||
block);
|
||||
yaffs_block_became_dirty(dev, block);
|
||||
} else {
|
||||
|
||||
u8 *buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
yaffs_verify_blk(dev, bi, block);
|
||||
|
||||
max_copies = (whole_block) ? dev->param.chunks_per_block : 5;
|
||||
old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk;
|
||||
|
||||
for (/* init already done */ ;
|
||||
ret_val == YAFFS_OK &&
|
||||
dev->gc_chunk < (u32)dev->param.chunks_per_block &&
|
||||
(bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) &&
|
||||
max_copies > 0;
|
||||
dev->gc_chunk++, old_chunk++) {
|
||||
if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) {
|
||||
/* Page is in use and might need to be copied */
|
||||
max_copies--;
|
||||
ret_val = yaffs_gc_process_chunk(dev, bi,
|
||||
old_chunk, buffer);
|
||||
}
|
||||
}
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
}
|
||||
|
||||
yaffs_verify_collected_blk(dev, bi, block);
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {
|
||||
/*
|
||||
* The gc did not complete. Set block state back to FULL
|
||||
* because checkpointing does not restore gc.
|
||||
*/
|
||||
bi->block_state = YAFFS_BLOCK_STATE_FULL;
|
||||
} else {
|
||||
/* The gc completed. */
|
||||
/* Do any required cleanups */
|
||||
for (i = 0; i < dev->n_clean_ups; i++) {
|
||||
/* Time to delete the file too */
|
||||
struct yaffs_obj *object =
|
||||
yaffs_find_by_number(dev, dev->gc_cleanup_list[i]);
|
||||
if (object) {
|
||||
yaffs_free_tnode(dev,
|
||||
object->variant.file_variant.top);
|
||||
object->variant.file_variant.top = NULL;
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"yaffs: About to finally delete object %d",
|
||||
object->obj_id);
|
||||
yaffs_generic_obj_del(object);
|
||||
object->my_dev->n_deleted_files--;
|
||||
}
|
||||
|
||||
}
|
||||
chunks_after = yaffs_get_erased_chunks(dev);
|
||||
if (chunks_before >= chunks_after)
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"gc did not increase free chunks before %d after %d",
|
||||
chunks_before, chunks_after);
|
||||
dev->gc_block = 0;
|
||||
dev->gc_chunk = 0;
|
||||
dev->n_clean_ups = 0;
|
||||
}
|
||||
|
||||
dev->gc_disable = 0;
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/*
|
||||
* find_gc_block() selects the dirtiest block (or close enough)
|
||||
* for garbage collection.
|
||||
*/
|
||||
|
||||
static unsigned yaffs_find_gc_block(struct yaffs_dev *dev,
|
||||
int aggressive, int background)
|
||||
{
|
||||
int i;
|
||||
int iterations;
|
||||
unsigned selected = 0;
|
||||
int prioritised = 0;
|
||||
int prioritised_exist = 0;
|
||||
struct yaffs_block_info *bi;
|
||||
int threshold;
|
||||
|
||||
/* First let's see if we need to grab a prioritised block */
|
||||
if (dev->has_pending_prioritised_gc && !aggressive) {
|
||||
dev->gc_dirtiest = 0;
|
||||
bi = dev->block_info;
|
||||
for (i = dev->internal_start_block;
|
||||
i <= dev->internal_end_block && !selected; i++) {
|
||||
|
||||
if (bi->gc_prioritise) {
|
||||
prioritised_exist = 1;
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_FULL &&
|
||||
yaffs_block_ok_for_gc(dev, bi)) {
|
||||
selected = i;
|
||||
prioritised = 1;
|
||||
}
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a prioritised block and none was selected then
|
||||
* this happened because there is at least one old dirty block
|
||||
* gumming up the works. Let's gc the oldest dirty block.
|
||||
*/
|
||||
|
||||
if (prioritised_exist &&
|
||||
!selected && dev->oldest_dirty_block > 0)
|
||||
selected = dev->oldest_dirty_block;
|
||||
|
||||
if (!prioritised_exist) /* None found, so we can clear this */
|
||||
dev->has_pending_prioritised_gc = 0;
|
||||
}
|
||||
|
||||
/* If we're doing aggressive GC then we are happy to take a less-dirty
|
||||
* block, and search harder.
|
||||
* else (leasurely gc), then we only bother to do this if the
|
||||
* block has only a few pages in use.
|
||||
*/
|
||||
|
||||
if (!selected) {
|
||||
int pages_used;
|
||||
int n_blocks =
|
||||
dev->internal_end_block - dev->internal_start_block + 1;
|
||||
if (aggressive) {
|
||||
threshold = dev->param.chunks_per_block;
|
||||
iterations = n_blocks;
|
||||
} else {
|
||||
int max_threshold;
|
||||
|
||||
if (background)
|
||||
max_threshold = dev->param.chunks_per_block / 2;
|
||||
else
|
||||
max_threshold = dev->param.chunks_per_block / 8;
|
||||
|
||||
if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD)
|
||||
max_threshold = YAFFS_GC_PASSIVE_THRESHOLD;
|
||||
|
||||
threshold = background ? (dev->gc_not_done + 2) * 2 : 0;
|
||||
if (threshold < YAFFS_GC_PASSIVE_THRESHOLD)
|
||||
threshold = YAFFS_GC_PASSIVE_THRESHOLD;
|
||||
if (threshold > max_threshold)
|
||||
threshold = max_threshold;
|
||||
|
||||
iterations = n_blocks / 16 + 1;
|
||||
if (iterations > 100)
|
||||
iterations = 100;
|
||||
}
|
||||
|
||||
for (i = 0;
|
||||
i < iterations &&
|
||||
(dev->gc_dirtiest < 1 ||
|
||||
dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH);
|
||||
i++) {
|
||||
dev->gc_block_finder++;
|
||||
if (dev->gc_block_finder < (u32)dev->internal_start_block ||
|
||||
(int)dev->gc_block_finder > dev->internal_end_block)
|
||||
dev->gc_block_finder =
|
||||
dev->internal_start_block;
|
||||
|
||||
bi = yaffs_get_block_info(dev, dev->gc_block_finder);
|
||||
|
||||
pages_used = bi->pages_in_use - bi->soft_del_pages;
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_FULL &&
|
||||
pages_used < dev->param.chunks_per_block &&
|
||||
(dev->gc_dirtiest < 1 ||
|
||||
pages_used < (int)dev->gc_pages_in_use) &&
|
||||
yaffs_block_ok_for_gc(dev, bi)) {
|
||||
dev->gc_dirtiest = dev->gc_block_finder;
|
||||
dev->gc_pages_in_use = pages_used;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= (u32)threshold)
|
||||
selected = dev->gc_dirtiest;
|
||||
}
|
||||
|
||||
/*
|
||||
* If nothing has been selected for a while, try the oldest dirty
|
||||
* because that's gumming up the works.
|
||||
*/
|
||||
|
||||
if (!selected && dev->param.is_yaffs2 &&
|
||||
dev->gc_not_done >= (background ? 10 : 20)) {
|
||||
yaffs2_find_oldest_dirty_seq(dev);
|
||||
if (dev->oldest_dirty_block > 0) {
|
||||
selected = dev->oldest_dirty_block;
|
||||
dev->gc_dirtiest = selected;
|
||||
dev->oldest_dirty_gc_count++;
|
||||
bi = yaffs_get_block_info(dev, selected);
|
||||
dev->gc_pages_in_use =
|
||||
bi->pages_in_use - bi->soft_del_pages;
|
||||
} else {
|
||||
dev->gc_not_done = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"GC Selected block %d with %d free, prioritised:%d",
|
||||
selected,
|
||||
dev->param.chunks_per_block - dev->gc_pages_in_use,
|
||||
prioritised);
|
||||
|
||||
dev->n_gc_blocks++;
|
||||
if (background)
|
||||
dev->bg_gcs++;
|
||||
|
||||
dev->gc_dirtiest = 0;
|
||||
dev->gc_pages_in_use = 0;
|
||||
dev->gc_not_done = 0;
|
||||
if (dev->refresh_skip > 0)
|
||||
dev->refresh_skip--;
|
||||
} else {
|
||||
dev->gc_not_done++;
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s",
|
||||
dev->gc_block_finder, dev->gc_not_done, threshold,
|
||||
dev->gc_dirtiest, dev->gc_pages_in_use,
|
||||
dev->oldest_dirty_block, background ? " bg" : "");
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
/* New garbage collector
|
||||
* If we're very low on erased blocks then we do aggressive garbage collection
|
||||
* otherwise we do "leasurely" garbage collection.
|
||||
* Aggressive gc looks further (whole array) and will accept less dirty blocks.
|
||||
* Passive gc only inspects smaller areas and only accepts more dirty blocks.
|
||||
*
|
||||
* The idea is to help clear out space in a more spread-out manner.
|
||||
* Dunno if it really does anything useful.
|
||||
*/
|
||||
static int yaffs_check_gc(struct yaffs_dev *dev, int background)
|
||||
{
|
||||
int aggressive = 0;
|
||||
int gc_ok = YAFFS_OK;
|
||||
int max_tries = 0;
|
||||
int min_erased;
|
||||
int erased_chunks;
|
||||
int checkpt_block_adjust;
|
||||
|
||||
if (dev->param.gc_control_fn &&
|
||||
(dev->param.gc_control_fn(dev) & 1) == 0)
|
||||
return YAFFS_OK;
|
||||
|
||||
if (dev->gc_disable)
|
||||
/* Bail out so we don't get recursive gc */
|
||||
return YAFFS_OK;
|
||||
|
||||
/* This loop should pass the first time.
|
||||
* Only loops here if the collection does not increase space.
|
||||
*/
|
||||
|
||||
do {
|
||||
max_tries++;
|
||||
|
||||
checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev);
|
||||
|
||||
min_erased =
|
||||
dev->param.n_reserved_blocks + checkpt_block_adjust + 1;
|
||||
erased_chunks =
|
||||
dev->n_erased_blocks * dev->param.chunks_per_block;
|
||||
|
||||
/* If we need a block soon then do aggressive gc. */
|
||||
if (dev->n_erased_blocks < min_erased)
|
||||
aggressive = 1;
|
||||
else {
|
||||
if (!background
|
||||
&& erased_chunks > (dev->n_free_chunks / 4))
|
||||
break;
|
||||
|
||||
if (dev->gc_skip > 20)
|
||||
dev->gc_skip = 20;
|
||||
if (erased_chunks < dev->n_free_chunks / 2 ||
|
||||
dev->gc_skip < 1 || background)
|
||||
aggressive = 0;
|
||||
else {
|
||||
dev->gc_skip--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev->gc_skip = 5;
|
||||
|
||||
/* If we don't already have a block being gc'd then see if we
|
||||
* should start another */
|
||||
|
||||
if (dev->gc_block < 1 && !aggressive) {
|
||||
dev->gc_block = yaffs2_find_refresh_block(dev);
|
||||
dev->gc_chunk = 0;
|
||||
dev->n_clean_ups = 0;
|
||||
}
|
||||
if (dev->gc_block < 1) {
|
||||
dev->gc_block =
|
||||
yaffs_find_gc_block(dev, aggressive, background);
|
||||
dev->gc_chunk = 0;
|
||||
dev->n_clean_ups = 0;
|
||||
}
|
||||
|
||||
if (dev->gc_block > 0) {
|
||||
dev->all_gcs++;
|
||||
if (!aggressive)
|
||||
dev->passive_gc_count++;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"yaffs: GC n_erased_blocks %d aggressive %d",
|
||||
dev->n_erased_blocks, aggressive);
|
||||
|
||||
gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive);
|
||||
}
|
||||
|
||||
if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) &&
|
||||
dev->gc_block > 0) {
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d",
|
||||
dev->n_erased_blocks, max_tries,
|
||||
dev->gc_block);
|
||||
}
|
||||
} while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) &&
|
||||
(dev->gc_block > 0) && (max_tries < 2));
|
||||
|
||||
return aggressive ? gc_ok : YAFFS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_bg_gc()
|
||||
* Garbage collects. Intended to be called from a background thread.
|
||||
* Returns non-zero if at least half the free chunks are erased.
|
||||
*/
|
||||
int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency)
|
||||
{
|
||||
int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency);
|
||||
|
||||
yaffs_check_gc(dev, 1);
|
||||
return erased_chunks > dev->n_free_chunks / 2;
|
||||
}
|
||||
|
||||
/*-------------------- Data file manipulation -----------------*/
|
||||
|
||||
static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer)
|
||||
{
|
||||
int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL);
|
||||
|
||||
if (nand_chunk >= 0)
|
||||
return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk,
|
||||
buffer, NULL);
|
||||
else {
|
||||
yaffs_trace(YAFFS_TRACE_NANDACCESS,
|
||||
"Chunk %d not found zero instead",
|
||||
nand_chunk);
|
||||
/* get sane (zero) data if you read a hole */
|
||||
memset(buffer, 0, in->my_dev->data_bytes_per_chunk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,
|
||||
int lyn)
|
||||
{
|
||||
int block;
|
||||
int page;
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (chunk_id <= 0)
|
||||
return;
|
||||
|
||||
dev->n_deletions++;
|
||||
block = chunk_id / dev->param.chunks_per_block;
|
||||
page = chunk_id % dev->param.chunks_per_block;
|
||||
|
||||
if (!yaffs_check_chunk_bit(dev, block, page))
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Deleting invalid chunk %d", chunk_id);
|
||||
|
||||
bi = yaffs_get_block_info(dev, block);
|
||||
|
||||
yaffs2_update_oldest_dirty_seq(dev, block, bi);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_DELETION,
|
||||
"line %d delete of chunk %d",
|
||||
lyn, chunk_id);
|
||||
|
||||
if (!dev->param.is_yaffs2 && mark_flash &&
|
||||
bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) {
|
||||
|
||||
memset(&tags, 0, sizeof(tags));
|
||||
tags.is_deleted = 1;
|
||||
yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags);
|
||||
yaffs_handle_chunk_update(dev, chunk_id, &tags);
|
||||
} else {
|
||||
dev->n_unmarked_deletions++;
|
||||
}
|
||||
|
||||
/* Pull out of the management area.
|
||||
* If the whole block became dirty, this will kick off an erasure.
|
||||
*/
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_FULL ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {
|
||||
dev->n_free_chunks++;
|
||||
yaffs_clear_chunk_bit(dev, block, page);
|
||||
bi->pages_in_use--;
|
||||
|
||||
if (bi->pages_in_use == 0 &&
|
||||
!bi->has_shrink_hdr &&
|
||||
bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING &&
|
||||
bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) {
|
||||
yaffs_block_became_dirty(dev, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk,
|
||||
const u8 *buffer, int n_bytes, int use_reserve)
|
||||
{
|
||||
/* Find old chunk Need to do this to get serial number
|
||||
* Write new one and patch into tree.
|
||||
* Invalidate old tags.
|
||||
*/
|
||||
|
||||
int prev_chunk_id;
|
||||
struct yaffs_ext_tags prev_tags;
|
||||
int new_chunk_id;
|
||||
struct yaffs_ext_tags new_tags;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
yaffs_check_gc(dev, 0);
|
||||
|
||||
/* Get the previous chunk at this location in the file if it exists.
|
||||
* If it does not exist then put a zero into the tree. This creates
|
||||
* the tnode now, rather than later when it is harder to clean up.
|
||||
*/
|
||||
prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags);
|
||||
if (prev_chunk_id < 1 &&
|
||||
!yaffs_put_chunk_in_file(in, inode_chunk, 0, 0))
|
||||
return 0;
|
||||
|
||||
/* Set up new tags */
|
||||
memset(&new_tags, 0, sizeof(new_tags));
|
||||
|
||||
new_tags.chunk_id = inode_chunk;
|
||||
new_tags.obj_id = in->obj_id;
|
||||
new_tags.serial_number =
|
||||
(prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1;
|
||||
new_tags.n_bytes = n_bytes;
|
||||
|
||||
if (n_bytes < 1 || (u32)n_bytes > dev->param.total_bytes_per_chunk) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Writing %d bytes to chunk!!!!!!!!!",
|
||||
n_bytes);
|
||||
BUG();
|
||||
}
|
||||
|
||||
new_chunk_id =
|
||||
yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve);
|
||||
|
||||
if (new_chunk_id > 0) {
|
||||
yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0);
|
||||
|
||||
if (prev_chunk_id > 0)
|
||||
yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__);
|
||||
|
||||
yaffs_verify_file_sane(in);
|
||||
}
|
||||
return new_chunk_id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set,
|
||||
const YCHAR *name, const void *value, int size,
|
||||
int flags)
|
||||
{
|
||||
struct yaffs_xattr_mod xmod;
|
||||
int result;
|
||||
|
||||
xmod.set = set;
|
||||
xmod.name = name;
|
||||
xmod.data = value;
|
||||
xmod.size = size;
|
||||
xmod.flags = flags;
|
||||
xmod.result = -ENOSPC;
|
||||
|
||||
result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod);
|
||||
|
||||
if (result > 0)
|
||||
return xmod.result;
|
||||
else
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer,
|
||||
struct yaffs_xattr_mod *xmod)
|
||||
{
|
||||
int retval = 0;
|
||||
int x_offs = sizeof(struct yaffs_obj_hdr);
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr);
|
||||
char *x_buffer = buffer + x_offs;
|
||||
|
||||
if (xmod->set)
|
||||
retval =
|
||||
nval_set(x_buffer, x_size, xmod->name, xmod->data,
|
||||
xmod->size, xmod->flags);
|
||||
else
|
||||
retval = nval_del(x_buffer, x_size, xmod->name);
|
||||
|
||||
obj->has_xattr = nval_hasvalues(x_buffer, x_size);
|
||||
obj->xattr_known = 1;
|
||||
xmod->result = retval;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name,
|
||||
void *value, int size)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
int result;
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int x_offs = sizeof(struct yaffs_obj_hdr);
|
||||
int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr);
|
||||
char *x_buffer;
|
||||
int retval = 0;
|
||||
|
||||
if (obj->hdr_chunk < 1)
|
||||
return -ENODATA;
|
||||
|
||||
/* If we know that the object has no xattribs then don't do all the
|
||||
* reading and parsing.
|
||||
*/
|
||||
if (obj->xattr_known && !obj->has_xattr) {
|
||||
if (name)
|
||||
return -ENODATA;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer = (char *)yaffs_get_temp_buffer(dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
result =
|
||||
yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags);
|
||||
|
||||
if (result != YAFFS_OK)
|
||||
retval = -ENOENT;
|
||||
else {
|
||||
x_buffer = buffer + x_offs;
|
||||
|
||||
if (!obj->xattr_known) {
|
||||
obj->has_xattr = nval_hasvalues(x_buffer, x_size);
|
||||
obj->xattr_known = 1;
|
||||
}
|
||||
|
||||
if (name)
|
||||
retval = nval_get(x_buffer, x_size, name, value, size);
|
||||
else
|
||||
retval = nval_list(x_buffer, x_size, value, size);
|
||||
}
|
||||
yaffs_release_temp_buffer(dev, (u8 *) buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name,
|
||||
const void *value, int size, int flags)
|
||||
{
|
||||
return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name)
|
||||
{
|
||||
return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value,
|
||||
int size)
|
||||
{
|
||||
return yaffs_do_xattrib_fetch(obj, name, value, size);
|
||||
}
|
||||
|
||||
int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size)
|
||||
{
|
||||
return yaffs_do_xattrib_fetch(obj, NULL, buffer, size);
|
||||
}
|
||||
|
||||
static void yaffs_check_obj_details_loaded(struct yaffs_obj *in)
|
||||
{
|
||||
u8 *buf;
|
||||
struct yaffs_obj_hdr *oh;
|
||||
struct yaffs_dev *dev;
|
||||
struct yaffs_ext_tags tags;
|
||||
//int result;
|
||||
//int alloc_failed = 0;
|
||||
|
||||
if (!in || !in->lazy_loaded || in->hdr_chunk < 1)
|
||||
return;
|
||||
|
||||
dev = in->my_dev;
|
||||
in->lazy_loaded = 0;
|
||||
buf = yaffs_get_temp_buffer(dev);
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_check_obj_details_loaded: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
oh = (struct yaffs_obj_hdr *)buf;
|
||||
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
yaffs_set_obj_name_from_oh(in, oh);
|
||||
|
||||
if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {
|
||||
in->variant.symlink_variant.alias =
|
||||
yaffs_clone_str(oh->alias);
|
||||
if (!in->variant.symlink_variant.alias)
|
||||
{
|
||||
// alloc_failed = 1;
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_check_obj_details_loaded: alloc_failed = 1??");
|
||||
}
|
||||
}
|
||||
yaffs_release_temp_buffer(dev, buf);
|
||||
}
|
||||
|
||||
/* UpdateObjectHeader updates the header on NAND for an object.
|
||||
* If name is not NULL, then that new name is used.
|
||||
*/
|
||||
int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force,
|
||||
int is_shrink, int shadows, struct yaffs_xattr_mod *xmod)
|
||||
{
|
||||
|
||||
struct yaffs_block_info *bi;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
int prev_chunk_id;
|
||||
int ret_val = 0;
|
||||
//int result = 0;
|
||||
int new_chunk_id;
|
||||
struct yaffs_ext_tags new_tags;
|
||||
struct yaffs_ext_tags old_tags;
|
||||
const YCHAR *alias = NULL;
|
||||
u8 *buffer = NULL;
|
||||
YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
struct yaffs_obj_hdr *oh = NULL;
|
||||
Y_LOFF_T file_size = 0;
|
||||
|
||||
yaffs_strcpy(old_name, _Y("silly old name"));
|
||||
|
||||
if (in->fake && in != dev->root_dir && !force && !xmod)
|
||||
return ret_val;
|
||||
|
||||
yaffs_check_gc(dev, 0);
|
||||
yaffs_check_obj_details_loaded(in);
|
||||
|
||||
buffer = yaffs_get_temp_buffer(in->my_dev);
|
||||
oh = (struct yaffs_obj_hdr *)buffer;
|
||||
|
||||
prev_chunk_id = in->hdr_chunk;
|
||||
|
||||
if (prev_chunk_id > 0) {
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, buffer, &old_tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_update_oh: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
yaffs_verify_oh(in, oh, &old_tags, 0);
|
||||
memcpy(old_name, oh->name, sizeof(oh->name));
|
||||
memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr));
|
||||
} else {
|
||||
memset(buffer, 0xff, dev->data_bytes_per_chunk);
|
||||
}
|
||||
|
||||
oh->type = in->variant_type;
|
||||
oh->yst_mode = in->yst_mode;
|
||||
oh->shadows_obj = oh->inband_shadowed_obj_id = shadows;
|
||||
|
||||
yaffs_load_attribs_oh(oh, in);
|
||||
|
||||
if (in->parent)
|
||||
oh->parent_obj_id = in->parent->obj_id;
|
||||
else
|
||||
oh->parent_obj_id = 0;
|
||||
|
||||
if (name && *name) {
|
||||
memset(oh->name, 0, sizeof(oh->name));
|
||||
yaffs_load_oh_from_name(dev, oh->name, name);
|
||||
} else if (prev_chunk_id > 0) {
|
||||
memcpy(oh->name, old_name, sizeof(oh->name));
|
||||
} else {
|
||||
memset(oh->name, 0, sizeof(oh->name));
|
||||
}
|
||||
|
||||
oh->is_shrink = is_shrink;
|
||||
|
||||
switch (in->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* Should not happen */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED &&
|
||||
oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED)
|
||||
file_size = in->variant.file_variant.file_size;
|
||||
yaffs_oh_size_load(oh, file_size);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
oh->equiv_id = in->variant.hardlink_variant.equiv_id;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
alias = in->variant.symlink_variant.alias;
|
||||
if (!alias)
|
||||
alias = _Y("no alias");
|
||||
yaffs_strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH);
|
||||
oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* process any xattrib modifications */
|
||||
if (xmod)
|
||||
yaffs_apply_xattrib_mod(in, (char *)buffer, xmod);
|
||||
|
||||
/* Tags */
|
||||
memset(&new_tags, 0, sizeof(new_tags));
|
||||
in->serial++;
|
||||
new_tags.chunk_id = 0;
|
||||
new_tags.obj_id = in->obj_id;
|
||||
new_tags.serial_number = in->serial;
|
||||
|
||||
/* Add extra info for file header */
|
||||
new_tags.extra_available = 1;
|
||||
new_tags.extra_parent_id = oh->parent_obj_id;
|
||||
new_tags.extra_file_size = file_size;
|
||||
new_tags.extra_is_shrink = oh->is_shrink;
|
||||
new_tags.extra_equiv_id = oh->equiv_id;
|
||||
new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0;
|
||||
new_tags.extra_obj_type = in->variant_type;
|
||||
yaffs_verify_oh(in, oh, &new_tags, 1);
|
||||
|
||||
/* Create new chunk in NAND */
|
||||
new_chunk_id =
|
||||
yaffs_write_new_chunk(dev, buffer, &new_tags,
|
||||
(prev_chunk_id > 0) ? 1 : 0);
|
||||
|
||||
if (buffer)
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
|
||||
if (new_chunk_id < 0)
|
||||
return new_chunk_id;
|
||||
|
||||
in->hdr_chunk = new_chunk_id;
|
||||
|
||||
if (prev_chunk_id > 0)
|
||||
yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__);
|
||||
|
||||
if (!yaffs_obj_cache_dirty(in))
|
||||
in->dirty = 0;
|
||||
|
||||
/* If this was a shrink, then mark the block
|
||||
* that the chunk lives on */
|
||||
if (is_shrink) {
|
||||
bi = yaffs_get_block_info(in->my_dev,
|
||||
new_chunk_id /
|
||||
in->my_dev->param.chunks_per_block);
|
||||
bi->has_shrink_hdr = 1;
|
||||
}
|
||||
|
||||
|
||||
return new_chunk_id;
|
||||
}
|
||||
|
||||
/*--------------------- File read/write ------------------------
|
||||
* Read and write have very similar structures.
|
||||
* In general the read/write has three parts to it
|
||||
* An incomplete chunk to start with (if the read/write is not chunk-aligned)
|
||||
* Some complete chunks
|
||||
* An incomplete chunk to end off with
|
||||
*
|
||||
* Curve-balls: the first chunk might also be the last chunk.
|
||||
*/
|
||||
|
||||
int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, Y_LOFF_T offset, int n_bytes)
|
||||
{
|
||||
int chunk;
|
||||
u32 start;
|
||||
int n_copy;
|
||||
int n = n_bytes;
|
||||
int n_done = 0;
|
||||
struct yaffs_cache *cache;
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
dev = in->my_dev;
|
||||
|
||||
while (n > 0) {
|
||||
yaffs_addr_to_chunk(dev, offset, &chunk, &start);
|
||||
chunk++;
|
||||
|
||||
/* OK now check for the curveball where the start and end are in
|
||||
* the same chunk.
|
||||
*/
|
||||
if ((start + n) < (u32)dev->data_bytes_per_chunk)
|
||||
n_copy = n;
|
||||
else
|
||||
n_copy = dev->data_bytes_per_chunk - start;
|
||||
|
||||
cache = yaffs_find_chunk_cache(in, chunk);
|
||||
|
||||
/* If the chunk is already in the cache or it is less than
|
||||
* a whole chunk or we're using inband tags then use the cache
|
||||
* (if there is caching) else bypass the cache.
|
||||
*/
|
||||
if (cache || n_copy != dev->data_bytes_per_chunk ||
|
||||
dev->param.inband_tags) {
|
||||
if (dev->param.n_caches > 0) {
|
||||
|
||||
/* If we can't find the data in the cache,
|
||||
* then load it up. */
|
||||
|
||||
if (!cache) {
|
||||
cache =
|
||||
yaffs_grab_chunk_cache(in->my_dev);
|
||||
cache->object = in;
|
||||
cache->chunk_id = chunk;
|
||||
cache->dirty = 0;
|
||||
cache->locked = 0;
|
||||
yaffs_rd_data_obj(in, chunk,
|
||||
cache->data);
|
||||
cache->n_bytes = 0;
|
||||
}
|
||||
|
||||
yaffs_use_cache(dev, cache, 0);
|
||||
|
||||
cache->locked = 1;
|
||||
|
||||
memcpy(buffer, &cache->data[start], n_copy);
|
||||
|
||||
cache->locked = 0;
|
||||
} else {
|
||||
/* Read into the local buffer then copy.. */
|
||||
|
||||
u8 *local_buffer =
|
||||
yaffs_get_temp_buffer(dev);
|
||||
yaffs_rd_data_obj(in, chunk, local_buffer);
|
||||
|
||||
memcpy(buffer, &local_buffer[start], n_copy);
|
||||
|
||||
yaffs_release_temp_buffer(dev, local_buffer);
|
||||
}
|
||||
} else {
|
||||
/* A full chunk. Read directly into the buffer. */
|
||||
yaffs_rd_data_obj(in, chunk, buffer);
|
||||
}
|
||||
n -= n_copy;
|
||||
offset += n_copy;
|
||||
buffer += n_copy;
|
||||
n_done += n_copy;
|
||||
}
|
||||
return n_done;
|
||||
}
|
||||
|
||||
int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, Y_LOFF_T offset,
|
||||
int n_bytes, int write_through)
|
||||
{
|
||||
|
||||
int chunk;
|
||||
u32 start;
|
||||
int n_copy;
|
||||
int n = n_bytes;
|
||||
int n_done = 0;
|
||||
int n_writeback;
|
||||
Y_LOFF_T start_write = offset;
|
||||
int chunk_written = 0;
|
||||
u32 n_bytes_read;
|
||||
Y_LOFF_T chunk_start;
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
dev = in->my_dev;
|
||||
|
||||
while (n > 0 && chunk_written >= 0) {
|
||||
yaffs_addr_to_chunk(dev, offset, &chunk, &start);
|
||||
|
||||
if (((Y_LOFF_T)chunk) *
|
||||
(s32)(dev->data_bytes_per_chunk + start) != offset ||
|
||||
(s32)start >= dev->data_bytes_per_chunk) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"AddrToChunk of offset %ud gives chunk %d start %d",
|
||||
(unsigned int)offset, chunk, start);
|
||||
}
|
||||
chunk++; /* File pos to chunk in file offset */
|
||||
|
||||
/* OK now check for the curveball where the start and end are in
|
||||
* the same chunk.
|
||||
*/
|
||||
|
||||
if ((start + n) < (u32)dev->data_bytes_per_chunk) {
|
||||
n_copy = n;
|
||||
|
||||
/* Now calculate how many bytes to write back....
|
||||
* If we're overwriting and not writing to then end of
|
||||
* file then we need to write back as much as was there
|
||||
* before.
|
||||
*/
|
||||
|
||||
chunk_start = (((Y_LOFF_T)(chunk - 1)) *
|
||||
dev->data_bytes_per_chunk);
|
||||
|
||||
if (chunk_start > in->variant.file_variant.file_size)
|
||||
n_bytes_read = 0; /* Past end of file */
|
||||
else
|
||||
n_bytes_read =
|
||||
in->variant.file_variant.file_size -
|
||||
chunk_start;
|
||||
|
||||
if ((int)n_bytes_read > dev->data_bytes_per_chunk)
|
||||
n_bytes_read = dev->data_bytes_per_chunk;
|
||||
|
||||
n_writeback =
|
||||
(n_bytes_read >
|
||||
(start + n)) ? n_bytes_read : (start + n);
|
||||
|
||||
if (n_writeback < 0 ||
|
||||
n_writeback > dev->data_bytes_per_chunk)
|
||||
BUG();
|
||||
|
||||
} else {
|
||||
n_copy = dev->data_bytes_per_chunk - start;
|
||||
n_writeback = dev->data_bytes_per_chunk;
|
||||
}
|
||||
|
||||
if (n_copy != dev->data_bytes_per_chunk ||
|
||||
!dev->param.cache_bypass_aligned ||
|
||||
dev->param.inband_tags) {
|
||||
/* An incomplete start or end chunk (or maybe both
|
||||
* start and end chunk), or we're using inband tags,
|
||||
* or we're forcing writes through the cache,
|
||||
* so we want to use the cache buffers.
|
||||
*/
|
||||
if (dev->param.n_caches > 0) {
|
||||
struct yaffs_cache *cache;
|
||||
|
||||
/* If we can't find the data in the cache, then
|
||||
* load the cache */
|
||||
cache = yaffs_find_chunk_cache(in, chunk);
|
||||
|
||||
if (!cache &&
|
||||
yaffs_check_alloc_available(dev, 1)) {
|
||||
cache = yaffs_grab_chunk_cache(dev);
|
||||
cache->object = in;
|
||||
cache->chunk_id = chunk;
|
||||
cache->dirty = 0;
|
||||
cache->locked = 0;
|
||||
yaffs_rd_data_obj(in, chunk,
|
||||
cache->data);
|
||||
} else if (cache &&
|
||||
!cache->dirty &&
|
||||
!yaffs_check_alloc_available(dev,
|
||||
1)) {
|
||||
/* Drop the cache if it was a read cache
|
||||
* item and no space check has been made
|
||||
* for it.
|
||||
*/
|
||||
cache = NULL;
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
yaffs_use_cache(dev, cache, 1);
|
||||
cache->locked = 1;
|
||||
|
||||
memcpy(&cache->data[start], buffer,
|
||||
n_copy);
|
||||
|
||||
cache->locked = 0;
|
||||
cache->n_bytes = n_writeback;
|
||||
|
||||
if (write_through) {
|
||||
chunk_written =
|
||||
yaffs_wr_data_obj
|
||||
(cache->object,
|
||||
cache->chunk_id,
|
||||
cache->data,
|
||||
cache->n_bytes, 1);
|
||||
cache->dirty = 0;
|
||||
}
|
||||
} else {
|
||||
chunk_written = -1; /* fail write */
|
||||
}
|
||||
} else {
|
||||
/* An incomplete start or end chunk (or maybe
|
||||
* both start and end chunk). Read into the
|
||||
* local buffer then copy over and write back.
|
||||
*/
|
||||
|
||||
u8 *local_buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
yaffs_rd_data_obj(in, chunk, local_buffer);
|
||||
memcpy(&local_buffer[start], buffer, n_copy);
|
||||
|
||||
chunk_written =
|
||||
yaffs_wr_data_obj(in, chunk,
|
||||
local_buffer,
|
||||
n_writeback, 0);
|
||||
|
||||
yaffs_release_temp_buffer(dev, local_buffer);
|
||||
}
|
||||
} else {
|
||||
/* A full chunk. Write directly from the buffer. */
|
||||
|
||||
chunk_written =
|
||||
yaffs_wr_data_obj(in, chunk, buffer,
|
||||
dev->data_bytes_per_chunk, 0);
|
||||
|
||||
/* Since we've overwritten the cached data,
|
||||
* we better invalidate it. */
|
||||
yaffs_invalidate_chunk_cache(in, chunk);
|
||||
}
|
||||
|
||||
if (chunk_written >= 0) {
|
||||
n -= n_copy;
|
||||
offset += n_copy;
|
||||
buffer += n_copy;
|
||||
n_done += n_copy;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update file object */
|
||||
|
||||
if ((start_write + n_done) > in->variant.file_variant.file_size)
|
||||
in->variant.file_variant.file_size = (start_write + n_done);
|
||||
|
||||
in->dirty = 1;
|
||||
return n_done;
|
||||
}
|
||||
|
||||
int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, Y_LOFF_T offset,
|
||||
int n_bytes, int write_through)
|
||||
{
|
||||
yaffs2_handle_hole(in, offset);
|
||||
return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through);
|
||||
}
|
||||
|
||||
/* ---------------------- File resizing stuff ------------------ */
|
||||
|
||||
static void yaffs_prune_chunks(struct yaffs_obj *in, Y_LOFF_T new_size)
|
||||
{
|
||||
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
Y_LOFF_T old_size = in->variant.file_variant.file_size;
|
||||
int i;
|
||||
int chunk_id;
|
||||
u32 dummy;
|
||||
int last_del;
|
||||
int start_del;
|
||||
|
||||
if (old_size > 0)
|
||||
yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy);
|
||||
else
|
||||
last_del = 0;
|
||||
|
||||
yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1,
|
||||
&start_del, &dummy);
|
||||
last_del++;
|
||||
start_del++;
|
||||
|
||||
/* Delete backwards so that we don't end up with holes if
|
||||
* power is lost part-way through the operation.
|
||||
*/
|
||||
for (i = last_del; i >= start_del; i--) {
|
||||
/* NB this could be optimised somewhat,
|
||||
* eg. could retrieve the tags and write them without
|
||||
* using yaffs_chunk_del
|
||||
*/
|
||||
|
||||
chunk_id = yaffs_find_del_file_chunk(in, i, NULL);
|
||||
|
||||
if (chunk_id < 1)
|
||||
continue;
|
||||
|
||||
if (chunk_id <
|
||||
(dev->internal_start_block * dev->param.chunks_per_block) ||
|
||||
chunk_id >=
|
||||
((dev->internal_end_block + 1) *
|
||||
dev->param.chunks_per_block)) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Found daft chunk_id %d for %d",
|
||||
chunk_id, i);
|
||||
} else {
|
||||
in->n_data_chunks--;
|
||||
yaffs_chunk_del(dev, chunk_id, 1, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_resize_file_down(struct yaffs_obj *obj, Y_LOFF_T new_size)
|
||||
{
|
||||
int new_full;
|
||||
u32 new_partial;
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
|
||||
yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial);
|
||||
|
||||
yaffs_prune_chunks(obj, new_size);
|
||||
|
||||
if (new_partial != 0) {
|
||||
int last_chunk = 1 + new_full;
|
||||
u8 *local_buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
/* Rewrite the last chunk with its new size and zero pad */
|
||||
yaffs_rd_data_obj(obj, last_chunk, local_buffer);
|
||||
memset(local_buffer + new_partial, 0,
|
||||
dev->data_bytes_per_chunk - new_partial);
|
||||
|
||||
yaffs_wr_data_obj(obj, last_chunk, local_buffer,
|
||||
new_partial, 1);
|
||||
|
||||
yaffs_release_temp_buffer(dev, local_buffer);
|
||||
}
|
||||
|
||||
obj->variant.file_variant.file_size = new_size;
|
||||
|
||||
yaffs_prune_tree(dev, &obj->variant.file_variant);
|
||||
}
|
||||
|
||||
int yaffs_resize_file(struct yaffs_obj *in, Y_LOFF_T new_size)
|
||||
{
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
Y_LOFF_T old_size = in->variant.file_variant.file_size;
|
||||
|
||||
yaffs_flush_file_cache(in, 1);
|
||||
yaffs_invalidate_whole_cache(in);
|
||||
|
||||
yaffs_check_gc(dev, 0);
|
||||
|
||||
if (in->variant_type != YAFFS_OBJECT_TYPE_FILE)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (new_size == old_size)
|
||||
return YAFFS_OK;
|
||||
|
||||
if (new_size > old_size) {
|
||||
yaffs2_handle_hole(in, new_size);
|
||||
in->variant.file_variant.file_size = new_size;
|
||||
} else {
|
||||
/* new_size < old_size */
|
||||
yaffs_resize_file_down(in, new_size);
|
||||
}
|
||||
|
||||
/* Write a new object header to reflect the resize.
|
||||
* show we've shrunk the file, if need be
|
||||
* Do this only if the file is not in the deleted directories
|
||||
* and is not shadowed.
|
||||
*/
|
||||
if (in->parent &&
|
||||
!in->is_shadowed &&
|
||||
in->parent->obj_id != YAFFS_OBJECTID_UNLINKED &&
|
||||
in->parent->obj_id != YAFFS_OBJECTID_DELETED)
|
||||
yaffs_update_oh(in, NULL, 0, 0, 0, NULL);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
int yaffs_flush_file(struct yaffs_obj *in,
|
||||
int update_time,
|
||||
int data_sync,
|
||||
int discard_cache)
|
||||
{
|
||||
if (!in->dirty)
|
||||
return YAFFS_OK;
|
||||
|
||||
yaffs_flush_file_cache(in, discard_cache);
|
||||
|
||||
if (data_sync)
|
||||
return YAFFS_OK;
|
||||
|
||||
if (update_time)
|
||||
yaffs_load_current_time(in, 0, 0);
|
||||
|
||||
return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ?
|
||||
YAFFS_OK : YAFFS_FAIL;
|
||||
}
|
||||
|
||||
|
||||
/* yaffs_del_file deletes the whole file data
|
||||
* and the inode associated with the file.
|
||||
* It does not delete the links associated with the file.
|
||||
*/
|
||||
static int yaffs_unlink_file_if_needed(struct yaffs_obj *in)
|
||||
{
|
||||
int ret_val;
|
||||
int del_now = 0;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (!in->my_inode)
|
||||
del_now = 1;
|
||||
|
||||
if (del_now) {
|
||||
ret_val =
|
||||
yaffs_change_obj_name(in, in->my_dev->del_dir,
|
||||
_Y("deleted"), 0, 0);
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"yaffs: immediate deletion of file %d",
|
||||
in->obj_id);
|
||||
in->deleted = 1;
|
||||
in->my_dev->n_deleted_files++;
|
||||
if (dev->param.disable_soft_del || dev->param.is_yaffs2)
|
||||
yaffs_resize_file(in, 0);
|
||||
yaffs_soft_del_file(in);
|
||||
} else {
|
||||
ret_val =
|
||||
yaffs_change_obj_name(in, in->my_dev->unlinked_dir,
|
||||
_Y("unlinked"), 0, 0);
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int yaffs_del_file(struct yaffs_obj *in)
|
||||
{
|
||||
int ret_val = YAFFS_OK;
|
||||
int deleted; /* Need to cache value on stack if in is freed */
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (dev->param.disable_soft_del || dev->param.is_yaffs2)
|
||||
yaffs_resize_file(in, 0);
|
||||
|
||||
if (in->n_data_chunks > 0) {
|
||||
/* Use soft deletion if there is data in the file.
|
||||
* That won't be the case if it has been resized to zero.
|
||||
*/
|
||||
if (!in->unlinked)
|
||||
ret_val = yaffs_unlink_file_if_needed(in);
|
||||
|
||||
deleted = in->deleted;
|
||||
|
||||
if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) {
|
||||
in->deleted = 1;
|
||||
deleted = 1;
|
||||
in->my_dev->n_deleted_files++;
|
||||
yaffs_soft_del_file(in);
|
||||
}
|
||||
return deleted ? YAFFS_OK : YAFFS_FAIL;
|
||||
} else {
|
||||
/* The file has no data chunks so we toss it immediately */
|
||||
yaffs_free_tnode(in->my_dev, in->variant.file_variant.top);
|
||||
in->variant.file_variant.top = NULL;
|
||||
yaffs_generic_obj_del(in);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_is_non_empty_dir(struct yaffs_obj *obj)
|
||||
{
|
||||
return (obj &&
|
||||
obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) &&
|
||||
!(list_empty(&obj->variant.dir_variant.children));
|
||||
}
|
||||
|
||||
static int yaffs_del_dir(struct yaffs_obj *obj)
|
||||
{
|
||||
/* First check that the directory is empty. */
|
||||
if (yaffs_is_non_empty_dir(obj))
|
||||
return YAFFS_FAIL;
|
||||
|
||||
return yaffs_generic_obj_del(obj);
|
||||
}
|
||||
|
||||
static int yaffs_del_symlink(struct yaffs_obj *in)
|
||||
{
|
||||
kfree(in->variant.symlink_variant.alias);
|
||||
in->variant.symlink_variant.alias = NULL;
|
||||
|
||||
return yaffs_generic_obj_del(in);
|
||||
}
|
||||
|
||||
static int yaffs_del_link(struct yaffs_obj *in)
|
||||
{
|
||||
/* remove this hardlink from the list associated with the equivalent
|
||||
* object
|
||||
*/
|
||||
list_del_init(&in->hard_links);
|
||||
return yaffs_generic_obj_del(in);
|
||||
}
|
||||
|
||||
int yaffs_del_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
int ret_val = -1;
|
||||
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
ret_val = yaffs_del_file(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
if (!list_empty(&obj->variant.dir_variant.dirty)) {
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND,
|
||||
"Remove object %d from dirty directories",
|
||||
obj->obj_id);
|
||||
list_del_init(&obj->variant.dir_variant.dirty);
|
||||
}
|
||||
return yaffs_del_dir(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
ret_val = yaffs_del_symlink(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
ret_val = yaffs_del_link(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
ret_val = yaffs_generic_obj_del(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
ret_val = 0;
|
||||
break; /* should not happen. */
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir,
|
||||
struct yaffs_obj *to_dir)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct list_head *lh;
|
||||
struct list_head *n;
|
||||
|
||||
list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) {
|
||||
obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
yaffs_add_obj_to_dir(to_dir, obj);
|
||||
}
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj,
|
||||
enum yaffs_obj_type type)
|
||||
{
|
||||
/* Tear down the old variant */
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
/* Nuke file data */
|
||||
yaffs_resize_file(obj, 0);
|
||||
yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top);
|
||||
obj->variant.file_variant.top = NULL;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
/* Put the children in lost and found. */
|
||||
yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found);
|
||||
if (!list_empty(&obj->variant.dir_variant.dirty))
|
||||
list_del_init(&obj->variant.dir_variant.dirty);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
/* Nuke symplink data */
|
||||
kfree(obj->variant.symlink_variant.alias);
|
||||
obj->variant.symlink_variant.alias = NULL;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
list_del_init(&obj->hard_links);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&obj->variant, 0, sizeof(obj->variant));
|
||||
|
||||
/*Set up new variant if the memset is not enough. */
|
||||
switch (type) {
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
INIT_LIST_HEAD(&obj->variant.dir_variant.children);
|
||||
INIT_LIST_HEAD(&obj->variant.dir_variant.dirty);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
obj->variant_type = type;
|
||||
|
||||
return obj;
|
||||
|
||||
}
|
||||
|
||||
static int yaffs_unlink_worker(struct yaffs_obj *obj)
|
||||
{
|
||||
int del_now = 0;
|
||||
|
||||
if (!obj)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (!obj->my_inode)
|
||||
del_now = 1;
|
||||
|
||||
yaffs_update_parent(obj->parent);
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) {
|
||||
return yaffs_del_link(obj);
|
||||
} else if (!list_empty(&obj->hard_links)) {
|
||||
/* Curve ball: We're unlinking an object that has a hardlink.
|
||||
*
|
||||
* This problem arises because we are not strictly following
|
||||
* The Linux link/inode model.
|
||||
*
|
||||
* We can't really delete the object.
|
||||
* Instead, we do the following:
|
||||
* - Select a hardlink.
|
||||
* - Unhook it from the hard links
|
||||
* - Move it from its parent directory so that the rename works.
|
||||
* - Rename the object to the hardlink's name.
|
||||
* - Delete the hardlink
|
||||
*/
|
||||
|
||||
struct yaffs_obj *hl;
|
||||
struct yaffs_obj *parent;
|
||||
int ret_val;
|
||||
YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
|
||||
hl = list_entry(obj->hard_links.next, struct yaffs_obj,
|
||||
hard_links);
|
||||
|
||||
yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1);
|
||||
parent = hl->parent;
|
||||
|
||||
list_del_init(&hl->hard_links);
|
||||
|
||||
yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl);
|
||||
|
||||
ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0);
|
||||
|
||||
if (ret_val == YAFFS_OK)
|
||||
ret_val = yaffs_generic_obj_del(hl);
|
||||
|
||||
return ret_val;
|
||||
|
||||
} else if (del_now) {
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
return yaffs_del_file(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
list_del_init(&obj->variant.dir_variant.dirty);
|
||||
return yaffs_del_dir(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
return yaffs_del_symlink(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
return yaffs_generic_obj_del(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
default:
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
} else if (yaffs_is_non_empty_dir(obj)) {
|
||||
return YAFFS_FAIL;
|
||||
} else {
|
||||
return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir,
|
||||
_Y("unlinked"), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffs_unlink_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && obj->unlink_allowed)
|
||||
return yaffs_unlink_worker(obj);
|
||||
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
obj = yaffs_find_by_name(dir, name);
|
||||
return yaffs_unlink_obj(obj);
|
||||
}
|
||||
|
||||
/* Note:
|
||||
* If old_name is NULL then we take old_dir as the object to be renamed.
|
||||
*/
|
||||
int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name,
|
||||
struct yaffs_obj *new_dir, const YCHAR *new_name)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *existing_target = NULL;
|
||||
int force = 0;
|
||||
int result;
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
dev = old_dir->my_dev;
|
||||
|
||||
#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
|
||||
/* Special case for case insemsitive systems.
|
||||
* While look-up is case insensitive, the name isn't.
|
||||
* Therefore we might want to change x.txt to X.txt
|
||||
*/
|
||||
if (old_dir == new_dir &&
|
||||
old_name && new_name &&
|
||||
yaffs_strcmp(old_name, new_name) == 0)
|
||||
force = 1;
|
||||
#endif
|
||||
|
||||
if (yaffs_strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) >
|
||||
YAFFS_MAX_NAME_LENGTH)
|
||||
/* ENAMETOOLONG */
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (old_name)
|
||||
obj = yaffs_find_by_name(old_dir, old_name);
|
||||
else{
|
||||
obj = old_dir;
|
||||
old_dir = obj->parent;
|
||||
}
|
||||
|
||||
if (obj && obj->rename_allowed) {
|
||||
/* Now handle an existing target, if there is one */
|
||||
existing_target = yaffs_find_by_name(new_dir, new_name);
|
||||
if (yaffs_is_non_empty_dir(existing_target)) {
|
||||
return YAFFS_FAIL; /* ENOTEMPTY */
|
||||
} else if (existing_target && existing_target != obj) {
|
||||
/* Nuke the target first, using shadowing,
|
||||
* but only if it isn't the same object.
|
||||
*
|
||||
* Note we must disable gc here otherwise it can mess
|
||||
* up the shadowing.
|
||||
*
|
||||
*/
|
||||
dev->gc_disable = 1;
|
||||
yaffs_change_obj_name(obj, new_dir, new_name, force,
|
||||
existing_target->obj_id);
|
||||
existing_target->is_shadowed = 1;
|
||||
yaffs_unlink_obj(existing_target);
|
||||
dev->gc_disable = 0;
|
||||
}
|
||||
|
||||
result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0);
|
||||
|
||||
yaffs_update_parent(old_dir);
|
||||
if (new_dir != old_dir)
|
||||
yaffs_update_parent(new_dir);
|
||||
|
||||
return result;
|
||||
}
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/*----------------------- Initialisation Scanning ---------------------- */
|
||||
|
||||
void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id,
|
||||
int backward_scanning)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
if (backward_scanning) {
|
||||
/* Handle YAFFS2 case (backward scanning)
|
||||
* If the shadowed object exists then ignore.
|
||||
*/
|
||||
obj = yaffs_find_by_number(dev, obj_id);
|
||||
if (obj)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let's create it (if it does not exist) assuming it is a file so that
|
||||
* it can do shrinking etc.
|
||||
* We put it in unlinked dir to be cleaned up after the scanning
|
||||
*/
|
||||
obj =
|
||||
yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE);
|
||||
if (!obj)
|
||||
return;
|
||||
obj->is_shadowed = 1;
|
||||
yaffs_add_obj_to_dir(dev->unlinked_dir, obj);
|
||||
obj->variant.file_variant.shrink_size = 0;
|
||||
obj->valid = 1; /* So that we don't read any other info. */
|
||||
}
|
||||
|
||||
void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list)
|
||||
{
|
||||
struct list_head *lh;
|
||||
struct list_head *save;
|
||||
struct yaffs_obj *hl;
|
||||
struct yaffs_obj *in;
|
||||
|
||||
list_for_each_safe(lh, save, hard_list) {
|
||||
hl = list_entry(lh, struct yaffs_obj, hard_links);
|
||||
in = yaffs_find_by_number(dev,
|
||||
hl->variant.hardlink_variant.equiv_id);
|
||||
|
||||
if (in) {
|
||||
/* Add the hardlink pointers */
|
||||
hl->variant.hardlink_variant.equiv_obj = in;
|
||||
list_add(&hl->hard_links, &in->hard_links);
|
||||
} else {
|
||||
/* Todo Need to report/handle this better.
|
||||
* Got a problem... hardlink to a non-existant object
|
||||
*/
|
||||
hl->variant.hardlink_variant.equiv_obj = NULL;
|
||||
INIT_LIST_HEAD(&hl->hard_links);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_strip_deleted_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
/*
|
||||
* Sort out state of unlinked and deleted objects after scanning.
|
||||
*/
|
||||
struct list_head *i;
|
||||
struct list_head *n;
|
||||
struct yaffs_obj *l;
|
||||
|
||||
if (dev->read_only)
|
||||
return;
|
||||
|
||||
/* Soft delete all the unlinked files */
|
||||
list_for_each_safe(i, n,
|
||||
&dev->unlinked_dir->variant.dir_variant.children) {
|
||||
l = list_entry(i, struct yaffs_obj, siblings);
|
||||
yaffs_del_obj(l);
|
||||
}
|
||||
|
||||
list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) {
|
||||
l = list_entry(i, struct yaffs_obj, siblings);
|
||||
yaffs_del_obj(l);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This code iterates through all the objects making sure that they are rooted.
|
||||
* Any unrooted objects are re-rooted in lost+found.
|
||||
* An object needs to be in one of:
|
||||
* - Directly under deleted, unlinked
|
||||
* - Directly or indirectly under root.
|
||||
*
|
||||
* Note:
|
||||
* This code assumes that we don't ever change the current relationships
|
||||
* between directories:
|
||||
* root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL
|
||||
* lost-n-found->parent == root_dir
|
||||
*
|
||||
* This fixes the problem where directories might have inadvertently been
|
||||
* deleted leaving the object "hanging" without being rooted in the
|
||||
* directory tree.
|
||||
*/
|
||||
|
||||
static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj)
|
||||
{
|
||||
return (obj == dev->del_dir ||
|
||||
obj == dev->unlinked_dir || obj == dev->root_dir);
|
||||
}
|
||||
|
||||
static void yaffs_fix_hanging_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_obj *parent;
|
||||
int i;
|
||||
struct list_head *lh;
|
||||
struct list_head *n;
|
||||
int depth_limit;
|
||||
int hanging;
|
||||
|
||||
if (dev->read_only)
|
||||
return;
|
||||
|
||||
/* Iterate through the objects in each hash entry,
|
||||
* looking at each object.
|
||||
* Make sure it is rooted.
|
||||
*/
|
||||
|
||||
for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
|
||||
list_for_each_safe(lh, n, &dev->obj_bucket[i].list) {
|
||||
obj = list_entry(lh, struct yaffs_obj, hash_link);
|
||||
parent = obj->parent;
|
||||
|
||||
if (yaffs_has_null_parent(dev, obj)) {
|
||||
/* These directories are not hanging */
|
||||
hanging = 0;
|
||||
} else if (!parent ||
|
||||
parent->variant_type !=
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
hanging = 1;
|
||||
} else if (yaffs_has_null_parent(dev, parent)) {
|
||||
hanging = 0;
|
||||
} else {
|
||||
/*
|
||||
* Need to follow the parent chain to
|
||||
* see if it is hanging.
|
||||
*/
|
||||
hanging = 0;
|
||||
depth_limit = 100;
|
||||
|
||||
while (parent != dev->root_dir &&
|
||||
parent->parent &&
|
||||
parent->parent->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY &&
|
||||
depth_limit > 0) {
|
||||
parent = parent->parent;
|
||||
depth_limit--;
|
||||
}
|
||||
if (parent != dev->root_dir)
|
||||
hanging = 1;
|
||||
}
|
||||
if (hanging) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Hanging object %d moved to lost and found",
|
||||
obj->obj_id);
|
||||
yaffs_add_obj_to_dir(dev->lost_n_found, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete directory contents for cleaning up lost and found.
|
||||
*/
|
||||
static void yaffs_del_dir_contents(struct yaffs_obj *dir)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct list_head *lh;
|
||||
struct list_head *n;
|
||||
|
||||
if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
BUG();
|
||||
|
||||
list_for_each_safe(lh, n, &dir->variant.dir_variant.children) {
|
||||
obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
yaffs_del_dir_contents(obj);
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Deleting lost_found object %d",
|
||||
obj->obj_id);
|
||||
yaffs_unlink_obj(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_empty_l_n_f(struct yaffs_dev *dev)
|
||||
{
|
||||
yaffs_del_dir_contents(dev->lost_n_found);
|
||||
}
|
||||
|
||||
|
||||
struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory,
|
||||
const YCHAR *name)
|
||||
{
|
||||
int sum;
|
||||
struct list_head *i;
|
||||
YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
struct yaffs_obj *l;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
if (!directory) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: yaffs_find_by_name: null pointer directory"
|
||||
);
|
||||
BUG();
|
||||
return NULL;
|
||||
}
|
||||
if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: yaffs_find_by_name: non-directory"
|
||||
);
|
||||
BUG();
|
||||
}
|
||||
|
||||
sum = yaffs_calc_name_sum(name);
|
||||
|
||||
list_for_each(i, &directory->variant.dir_variant.children) {
|
||||
l = list_entry(i, struct yaffs_obj, siblings);
|
||||
|
||||
if (l->parent != directory)
|
||||
BUG();
|
||||
|
||||
yaffs_check_obj_details_loaded(l);
|
||||
|
||||
/* Special case for lost-n-found */
|
||||
if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) {
|
||||
if (!yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME))
|
||||
return l;
|
||||
} else if (l->sum == sum || l->hdr_chunk <= 0) {
|
||||
/* LostnFound chunk called Objxxx
|
||||
* Do a real check
|
||||
*/
|
||||
yaffs_get_obj_name(l, buffer,
|
||||
YAFFS_MAX_NAME_LENGTH + 1);
|
||||
if (!yaffs_strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH))
|
||||
return l;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* GetEquivalentObject dereferences any hard links to get to the
|
||||
* actual object.
|
||||
*/
|
||||
|
||||
struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) {
|
||||
obj = obj->variant.hardlink_variant.equiv_obj;
|
||||
yaffs_check_obj_details_loaded(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* A note or two on object names.
|
||||
* * If the object name is missing, we then make one up in the form objnnn
|
||||
*
|
||||
* * ASCII names are stored in the object header's name field from byte zero
|
||||
* * Unicode names are historically stored starting from byte zero.
|
||||
*
|
||||
* Then there are automatic Unicode names...
|
||||
* The purpose of these is to save names in a way that can be read as
|
||||
* ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII
|
||||
* system to share files.
|
||||
*
|
||||
* These automatic unicode are stored slightly differently...
|
||||
* - If the name can fit in the ASCII character space then they are saved as
|
||||
* ascii names as per above.
|
||||
* - If the name needs Unicode then the name is saved in Unicode
|
||||
* starting at oh->name[1].
|
||||
|
||||
*/
|
||||
static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name,
|
||||
int buffer_size)
|
||||
{
|
||||
/* Create an object name if we could not find one. */
|
||||
if (yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) {
|
||||
YCHAR local_name[20];
|
||||
YCHAR num_string[20];
|
||||
YCHAR *x = &num_string[19];
|
||||
unsigned v = obj->obj_id;
|
||||
num_string[19] = 0;
|
||||
while (v > 0) {
|
||||
x--;
|
||||
*x = '0' + (v % 10);
|
||||
v /= 10;
|
||||
}
|
||||
/* make up a name */
|
||||
yaffs_strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX);
|
||||
yaffs_strcat(local_name, x);
|
||||
yaffs_strncpy(name, local_name, buffer_size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size)
|
||||
{
|
||||
memset(name, 0, buffer_size * sizeof(YCHAR));
|
||||
yaffs_check_obj_details_loaded(obj);
|
||||
if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) {
|
||||
yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1);
|
||||
} else if (obj->short_name[0]) {
|
||||
yaffs_strcpy(name, obj->short_name);
|
||||
} else if (obj->hdr_chunk > 0) {
|
||||
//int result;
|
||||
u8 *buffer = yaffs_get_temp_buffer(obj->my_dev);
|
||||
|
||||
struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer;
|
||||
|
||||
memset(buffer, 0, obj->my_dev->data_bytes_per_chunk);
|
||||
|
||||
if (obj->hdr_chunk > 0) {
|
||||
if (!yaffs_rd_chunk_tags_nand(obj->my_dev,
|
||||
obj->hdr_chunk,
|
||||
buffer, NULL))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs_get_obj_name: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
}
|
||||
yaffs_load_name_from_oh(obj->my_dev, name, oh->name,
|
||||
buffer_size);
|
||||
|
||||
yaffs_release_temp_buffer(obj->my_dev, buffer);
|
||||
}
|
||||
|
||||
yaffs_fix_null_name(obj, name, buffer_size);
|
||||
|
||||
return yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_get_obj_length(struct yaffs_obj *obj)
|
||||
{
|
||||
/* Dereference any hard linking */
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
return obj->variant.file_variant.file_size;
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {
|
||||
if (!obj->variant.symlink_variant.alias)
|
||||
return 0;
|
||||
return yaffs_strnlen(obj->variant.symlink_variant.alias,
|
||||
YAFFS_MAX_ALIAS_LENGTH);
|
||||
} else {
|
||||
/* Only a directory should drop through to here */
|
||||
return obj->my_dev->data_bytes_per_chunk;
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_get_obj_link_count(struct yaffs_obj *obj)
|
||||
{
|
||||
int count = 0;
|
||||
struct list_head *i;
|
||||
|
||||
if (!obj->unlinked)
|
||||
count++; /* the object itself */
|
||||
|
||||
list_for_each(i, &obj->hard_links)
|
||||
count++; /* add the hard links; */
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int yaffs_get_obj_inode(struct yaffs_obj *obj)
|
||||
{
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
return obj->obj_id;
|
||||
}
|
||||
|
||||
unsigned yaffs_get_obj_type(struct yaffs_obj *obj)
|
||||
{
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
return DT_REG;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
return DT_DIR;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
return DT_LNK;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
return DT_REG;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
if (S_ISFIFO(obj->yst_mode))
|
||||
return DT_FIFO;
|
||||
if (S_ISCHR(obj->yst_mode))
|
||||
return DT_CHR;
|
||||
if (S_ISBLK(obj->yst_mode))
|
||||
return DT_BLK;
|
||||
if (S_ISSOCK(obj->yst_mode))
|
||||
return DT_SOCK;
|
||||
return DT_REG;
|
||||
break;
|
||||
default:
|
||||
return DT_REG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj)
|
||||
{
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK)
|
||||
return yaffs_clone_str(obj->variant.symlink_variant.alias);
|
||||
else
|
||||
return yaffs_clone_str(_Y(""));
|
||||
}
|
||||
|
||||
/*--------------------------- Initialisation code -------------------------- */
|
||||
|
||||
static int yaffs_check_dev_fns(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_driver *drv = &dev->drv;
|
||||
struct yaffs_tags_handler *tagger = &dev->tagger;
|
||||
|
||||
/* Common functions, gotta have */
|
||||
if (!drv->drv_read_chunk_fn ||
|
||||
!drv->drv_write_chunk_fn ||
|
||||
!drv->drv_erase_fn)
|
||||
return 0;
|
||||
|
||||
if (dev->param.is_yaffs2 &&
|
||||
(!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn))
|
||||
return 0;
|
||||
|
||||
/* Install the default tags marshalling functions if needed. */
|
||||
yaffs_tags_compat_install(dev);
|
||||
yaffs_tags_marshall_install(dev);
|
||||
|
||||
/* Check we now have the marshalling functions required. */
|
||||
if (!tagger->write_chunk_tags_fn ||
|
||||
!tagger->read_chunk_tags_fn ||
|
||||
!tagger->query_block_fn ||
|
||||
!tagger->mark_bad_fn)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs_create_initial_dir(struct yaffs_dev *dev)
|
||||
{
|
||||
/* Initialise the unlinked, deleted, root and lost+found directories */
|
||||
dev->lost_n_found = dev->root_dir = NULL;
|
||||
dev->unlinked_dir = dev->del_dir = NULL;
|
||||
dev->unlinked_dir =
|
||||
yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR);
|
||||
dev->del_dir =
|
||||
yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR);
|
||||
dev->root_dir =
|
||||
yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT,
|
||||
YAFFS_ROOT_MODE | S_IFDIR);
|
||||
dev->lost_n_found =
|
||||
yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND,
|
||||
YAFFS_LOSTNFOUND_MODE | S_IFDIR);
|
||||
|
||||
if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir
|
||||
&& dev->del_dir) {
|
||||
yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Low level init.
|
||||
* Typically only used by yaffs_guts_initialise, but also used by the
|
||||
* Low level yaffs driver tests.
|
||||
*/
|
||||
|
||||
int yaffs_guts_ll_init(struct yaffs_dev *dev)
|
||||
{
|
||||
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()");
|
||||
|
||||
if (!dev) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs: Need a device"
|
||||
);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (dev->ll_init)
|
||||
return YAFFS_OK;
|
||||
|
||||
dev->internal_start_block = dev->param.start_block;
|
||||
dev->internal_end_block = dev->param.end_block;
|
||||
dev->block_offset = 0;
|
||||
dev->chunk_offset = 0;
|
||||
dev->n_free_chunks = 0;
|
||||
|
||||
dev->gc_block = 0;
|
||||
|
||||
if (dev->param.start_block == 0) {
|
||||
dev->internal_start_block = dev->param.start_block + 1;
|
||||
dev->internal_end_block = dev->param.end_block + 1;
|
||||
dev->block_offset = 1;
|
||||
dev->chunk_offset = dev->param.chunks_per_block;
|
||||
}
|
||||
|
||||
/* Check geometry parameters. */
|
||||
|
||||
if ((!dev->param.inband_tags && dev->param.is_yaffs2 &&
|
||||
dev->param.total_bytes_per_chunk < 1024) ||
|
||||
(!dev->param.is_yaffs2 &&
|
||||
dev->param.total_bytes_per_chunk < 512) ||
|
||||
(dev->param.inband_tags && !dev->param.is_yaffs2) ||
|
||||
dev->param.chunks_per_block < 2 ||
|
||||
dev->param.n_reserved_blocks < 2 ||
|
||||
dev->internal_start_block <= 0 ||
|
||||
dev->internal_end_block <= 0 ||
|
||||
dev->internal_end_block <=
|
||||
(dev->internal_start_block + dev->param.n_reserved_blocks + 2)
|
||||
) {
|
||||
/* otherwise it is too small */
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ",
|
||||
dev->param.total_bytes_per_chunk,
|
||||
dev->param.is_yaffs2 ? "2" : "",
|
||||
dev->param.inband_tags);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Sort out space for inband tags, if required */
|
||||
if (dev->param.inband_tags)
|
||||
dev->data_bytes_per_chunk =
|
||||
dev->param.total_bytes_per_chunk -
|
||||
sizeof(struct yaffs_packed_tags2_tags_only);
|
||||
else
|
||||
dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk;
|
||||
|
||||
/* Got the right mix of functions? */
|
||||
if (!yaffs_check_dev_fns(dev)) {
|
||||
/* Function missing */
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"device function(s) missing or wrong");
|
||||
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (yaffs_init_nand(dev) != YAFFS_OK) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed");
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
|
||||
int yaffs_guts_format_dev(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
enum yaffs_block_state state;
|
||||
u32 dummy;
|
||||
|
||||
if(yaffs_guts_ll_init(dev) != YAFFS_OK)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if(dev->is_mounted)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
yaffs_query_init_block_state(dev, i, &state, &dummy);
|
||||
if (state != YAFFS_BLOCK_STATE_DEAD)
|
||||
yaffs_erase_block(dev, i);
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
|
||||
int yaffs_guts_initialise(struct yaffs_dev *dev)
|
||||
{
|
||||
int init_failed = 0;
|
||||
unsigned x;
|
||||
int bits;
|
||||
|
||||
if(yaffs_guts_ll_init(dev) != YAFFS_OK)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (dev->is_mounted) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted");
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
dev->is_mounted = 1;
|
||||
|
||||
/* OK now calculate a few things for the device */
|
||||
|
||||
/*
|
||||
* Calculate all the chunk size manipulation numbers:
|
||||
*/
|
||||
x = dev->data_bytes_per_chunk;
|
||||
/* We always use dev->chunk_shift and dev->chunk_div */
|
||||
dev->chunk_shift = calc_shifts(x);
|
||||
x >>= dev->chunk_shift;
|
||||
dev->chunk_div = x;
|
||||
/* We only use chunk mask if chunk_div is 1 */
|
||||
dev->chunk_mask = (1 << dev->chunk_shift) - 1;
|
||||
|
||||
/*
|
||||
* Calculate chunk_grp_bits.
|
||||
* We need to find the next power of 2 > than internal_end_block
|
||||
*/
|
||||
|
||||
x = dev->param.chunks_per_block * (dev->internal_end_block + 1);
|
||||
|
||||
bits = calc_shifts_ceiling(x);
|
||||
|
||||
/* Set up tnode width if wide tnodes are enabled. */
|
||||
if (!dev->param.wide_tnodes_disabled) {
|
||||
/* bits must be even so that we end up with 32-bit words */
|
||||
if (bits & 1)
|
||||
bits++;
|
||||
if (bits < 16)
|
||||
dev->tnode_width = 16;
|
||||
else
|
||||
dev->tnode_width = bits;
|
||||
} else {
|
||||
dev->tnode_width = 16;
|
||||
}
|
||||
|
||||
dev->tnode_mask = (1 << dev->tnode_width) - 1;
|
||||
|
||||
/* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled),
|
||||
* so if the bitwidth of the
|
||||
* chunk range we're using is greater than 16 we need
|
||||
* to figure out chunk shift and chunk_grp_size
|
||||
*/
|
||||
|
||||
if (bits <= (int)dev->tnode_width)
|
||||
dev->chunk_grp_bits = 0;
|
||||
else
|
||||
dev->chunk_grp_bits = bits - dev->tnode_width;
|
||||
|
||||
dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8;
|
||||
if (dev->tnode_size < sizeof(struct yaffs_tnode))
|
||||
dev->tnode_size = sizeof(struct yaffs_tnode);
|
||||
|
||||
dev->chunk_grp_size = 1 << dev->chunk_grp_bits;
|
||||
|
||||
if (dev->param.chunks_per_block < dev->chunk_grp_size) {
|
||||
/* We have a problem because the soft delete won't work if
|
||||
* the chunk group size > chunks per block.
|
||||
* This can be remedied by using larger "virtual blocks".
|
||||
*/
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large");
|
||||
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Finished verifying the device, continue with initialisation */
|
||||
|
||||
/* More device initialisation */
|
||||
dev->all_gcs = 0;
|
||||
dev->passive_gc_count = 0;
|
||||
dev->oldest_dirty_gc_count = 0;
|
||||
dev->bg_gcs = 0;
|
||||
dev->gc_block_finder = 0;
|
||||
dev->buffered_block = -1;
|
||||
dev->doing_buffered_block_rewrite = 0;
|
||||
dev->n_deleted_files = 0;
|
||||
dev->n_bg_deletions = 0;
|
||||
dev->n_unlinked_files = 0;
|
||||
dev->n_ecc_fixed = 0;
|
||||
dev->n_ecc_unfixed = 0;
|
||||
dev->n_tags_ecc_fixed = 0;
|
||||
dev->n_tags_ecc_unfixed = 0;
|
||||
dev->n_erase_failures = 0;
|
||||
dev->n_erased_blocks = 0;
|
||||
dev->gc_disable = 0;
|
||||
dev->has_pending_prioritised_gc = 1;
|
||||
/* Assume the worst for now, will get fixed on first GC */
|
||||
INIT_LIST_HEAD(&dev->dirty_dirs);
|
||||
dev->oldest_dirty_seq = 0;
|
||||
dev->oldest_dirty_block = 0;
|
||||
|
||||
/* Initialise temporary buffers and caches. */
|
||||
if (!yaffs_init_tmp_buffers(dev))
|
||||
init_failed = 1;
|
||||
|
||||
dev->cache = NULL;
|
||||
dev->gc_cleanup_list = NULL;
|
||||
|
||||
if (!init_failed && dev->param.n_caches > 0) {
|
||||
int i;
|
||||
void *buf;
|
||||
int cache_bytes =
|
||||
dev->param.n_caches * sizeof(struct yaffs_cache);
|
||||
|
||||
if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES)
|
||||
dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES;
|
||||
|
||||
dev->cache = kmalloc(cache_bytes, GFP_NOFS);
|
||||
|
||||
buf = (u8 *) dev->cache;
|
||||
|
||||
if (dev->cache)
|
||||
memset(dev->cache, 0, cache_bytes);
|
||||
|
||||
for (i = 0; i < dev->param.n_caches && buf; i++) {
|
||||
dev->cache[i].object = NULL;
|
||||
dev->cache[i].last_use = 0;
|
||||
dev->cache[i].dirty = 0;
|
||||
dev->cache[i].data = buf =
|
||||
kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
|
||||
}
|
||||
if (!buf)
|
||||
init_failed = 1;
|
||||
|
||||
dev->cache_last_use = 0;
|
||||
}
|
||||
|
||||
dev->cache_hits = 0;
|
||||
|
||||
if (!init_failed) {
|
||||
dev->gc_cleanup_list =
|
||||
kmalloc(dev->param.chunks_per_block * sizeof(u32),
|
||||
GFP_NOFS);
|
||||
if (!dev->gc_cleanup_list)
|
||||
init_failed = 1;
|
||||
}
|
||||
|
||||
if (dev->param.is_yaffs2)
|
||||
dev->param.use_header_file_size = 1;
|
||||
|
||||
if (!init_failed && !yaffs_init_blocks(dev))
|
||||
init_failed = 1;
|
||||
|
||||
yaffs_init_tnodes_and_objs(dev);
|
||||
|
||||
if (!init_failed && !yaffs_create_initial_dir(dev))
|
||||
init_failed = 1;
|
||||
|
||||
if (!init_failed && dev->param.is_yaffs2 &&
|
||||
!dev->param.disable_summary &&
|
||||
!yaffs_summary_init(dev))
|
||||
init_failed = 1;
|
||||
|
||||
if (!init_failed) {
|
||||
/* Now scan the flash. */
|
||||
if (dev->param.is_yaffs2) {
|
||||
if (yaffs2_checkpt_restore(dev)) {
|
||||
yaffs_check_obj_details_loaded(dev->root_dir);
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT |
|
||||
YAFFS_TRACE_MOUNT,
|
||||
"yaffs: restored from checkpoint"
|
||||
);
|
||||
} else {
|
||||
|
||||
/* Clean up the mess caused by an aborted
|
||||
* checkpoint load then scan backwards.
|
||||
*/
|
||||
yaffs_deinit_blocks(dev);
|
||||
|
||||
yaffs_deinit_tnodes_and_objs(dev);
|
||||
|
||||
dev->n_erased_blocks = 0;
|
||||
dev->n_free_chunks = 0;
|
||||
dev->alloc_block = -1;
|
||||
dev->alloc_page = -1;
|
||||
dev->n_deleted_files = 0;
|
||||
dev->n_unlinked_files = 0;
|
||||
dev->n_bg_deletions = 0;
|
||||
|
||||
if (!init_failed && !yaffs_init_blocks(dev))
|
||||
init_failed = 1;
|
||||
|
||||
yaffs_init_tnodes_and_objs(dev);
|
||||
|
||||
if (!init_failed
|
||||
&& !yaffs_create_initial_dir(dev))
|
||||
init_failed = 1;
|
||||
|
||||
if (!init_failed && !yaffs2_scan_backwards(dev))
|
||||
init_failed = 1;
|
||||
}
|
||||
} else if (!yaffs1_scan(dev)) {
|
||||
init_failed = 1;
|
||||
}
|
||||
|
||||
yaffs_strip_deleted_objs(dev);
|
||||
yaffs_fix_hanging_objs(dev);
|
||||
if (dev->param.empty_lost_n_found)
|
||||
yaffs_empty_l_n_f(dev);
|
||||
}
|
||||
|
||||
if (init_failed) {
|
||||
/* Clean up the mess */
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"yaffs: yaffs_guts_initialise() aborted.");
|
||||
|
||||
yaffs_deinitialise(dev);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Zero out stats */
|
||||
dev->n_page_reads = 0;
|
||||
dev->n_page_writes = 0;
|
||||
dev->n_erasures = 0;
|
||||
dev->n_gc_copies = 0;
|
||||
dev->n_retried_writes = 0;
|
||||
|
||||
dev->n_retired_blocks = 0;
|
||||
|
||||
yaffs_verify_free_chunks(dev);
|
||||
yaffs_verify_blocks(dev);
|
||||
|
||||
/* Clean up any aborted checkpoint data */
|
||||
if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0)
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"yaffs: yaffs_guts_initialise() done.");
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
void yaffs_deinitialise(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->is_mounted) {
|
||||
int i;
|
||||
|
||||
yaffs_deinit_blocks(dev);
|
||||
yaffs_deinit_tnodes_and_objs(dev);
|
||||
yaffs_summary_deinit(dev);
|
||||
|
||||
if (dev->param.n_caches > 0 && dev->cache) {
|
||||
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
kfree(dev->cache[i].data);
|
||||
dev->cache[i].data = NULL;
|
||||
}
|
||||
|
||||
kfree(dev->cache);
|
||||
dev->cache = NULL;
|
||||
}
|
||||
|
||||
kfree(dev->gc_cleanup_list);
|
||||
|
||||
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
|
||||
kfree(dev->temp_buffer[i].buffer);
|
||||
|
||||
dev->is_mounted = 0;
|
||||
|
||||
yaffs_deinit_nand(dev);
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_count_free_chunks(struct yaffs_dev *dev)
|
||||
{
|
||||
int n_free = 0;
|
||||
int b;
|
||||
struct yaffs_block_info *blk;
|
||||
|
||||
blk = dev->block_info;
|
||||
for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) {
|
||||
switch (blk->block_state) {
|
||||
case YAFFS_BLOCK_STATE_EMPTY:
|
||||
case YAFFS_BLOCK_STATE_ALLOCATING:
|
||||
case YAFFS_BLOCK_STATE_COLLECTING:
|
||||
case YAFFS_BLOCK_STATE_FULL:
|
||||
n_free +=
|
||||
(dev->param.chunks_per_block - blk->pages_in_use +
|
||||
blk->soft_del_pages);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
blk++;
|
||||
}
|
||||
return n_free;
|
||||
}
|
||||
|
||||
int yaffs_get_n_free_chunks(struct yaffs_dev *dev)
|
||||
{
|
||||
/* This is what we report to the outside world */
|
||||
int n_free;
|
||||
int n_dirty_caches;
|
||||
int blocks_for_checkpt;
|
||||
int i;
|
||||
|
||||
n_free = dev->n_free_chunks;
|
||||
n_free += dev->n_deleted_files;
|
||||
|
||||
/* Now count and subtract the number of dirty chunks in the cache. */
|
||||
|
||||
for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) {
|
||||
if (dev->cache[i].dirty)
|
||||
n_dirty_caches++;
|
||||
}
|
||||
|
||||
n_free -= n_dirty_caches;
|
||||
|
||||
n_free -=
|
||||
((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block);
|
||||
|
||||
/* Now figure checkpoint space and report that... */
|
||||
blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev);
|
||||
|
||||
n_free -= (blocks_for_checkpt * dev->param.chunks_per_block);
|
||||
|
||||
if (n_free < 0)
|
||||
n_free = 0;
|
||||
|
||||
return n_free;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Marshalling functions to get Y_LOFF_T file sizes into and out of
|
||||
* object headers.
|
||||
*/
|
||||
void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, Y_LOFF_T fsize)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_OP
|
||||
oh->file_size_low = (fsize & 0xFFFFFFFF);
|
||||
oh->file_size_high = 0; //((fsize >> 32) & 0xFFFFFFFF);
|
||||
#else
|
||||
oh->file_size_low = (fsize & 0xFFFFFFFF);
|
||||
oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF);
|
||||
#endif
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_oh_to_size(struct yaffs_obj_hdr *oh)
|
||||
{
|
||||
Y_LOFF_T retval;
|
||||
|
||||
if (sizeof(Y_LOFF_T) >= 8 && ~(oh->file_size_high))
|
||||
#ifdef CONFIG_YAFFS_OP
|
||||
retval = (((Y_LOFF_T) oh->file_size_low) & 0xFFFFFFFF);
|
||||
#else
|
||||
retval = (((Y_LOFF_T) oh->file_size_high) << 32) |
|
||||
(((Y_LOFF_T) oh->file_size_low) & 0xFFFFFFFF);
|
||||
#endif
|
||||
else
|
||||
retval = (Y_LOFF_T) oh->file_size_low;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10])
|
||||
{
|
||||
int i;
|
||||
struct yaffs_block_info *bi;
|
||||
int s;
|
||||
|
||||
for(i = 0; i < 10; i++)
|
||||
bs[i] = 0;
|
||||
|
||||
for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
bi = yaffs_get_block_info(dev, i);
|
||||
s = bi->block_state;
|
||||
if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN)
|
||||
bs[0]++;
|
||||
else
|
||||
bs[s]++;
|
||||
}
|
||||
}
|
53
flight/pios/common/libraries/yaffs2/yaffs_hweight.c
Normal file
53
flight/pios/common/libraries/yaffs2/yaffs_hweight.c
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* These functions have been renamed to hweightxx to match the
|
||||
* equivaqlent functions in the Linux kernel.
|
||||
*/
|
||||
|
||||
#include "yaffs_hweight.h"
|
||||
|
||||
static const char yaffs_count_bits_table[256] = {
|
||||
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
|
||||
};
|
||||
|
||||
int yaffs_hweight8(u8 x)
|
||||
{
|
||||
int ret_val;
|
||||
ret_val = yaffs_count_bits_table[x];
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int yaffs_hweight32(u32 x)
|
||||
{
|
||||
return yaffs_hweight8(x & 0xff) +
|
||||
yaffs_hweight8((x >> 8) & 0xff) +
|
||||
yaffs_hweight8((x >> 16) & 0xff) +
|
||||
yaffs_hweight8((x >> 24) & 0xff);
|
||||
}
|
||||
|
208
flight/pios/common/libraries/yaffs2/yaffs_nameval.c
Normal file
208
flight/pios/common/libraries/yaffs2/yaffs_nameval.c
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This simple implementation of a name-value store assumes a small number of
|
||||
* values and fits into a small finite buffer.
|
||||
*
|
||||
* Each attribute is stored as a record:
|
||||
* sizeof(int) bytes record size.
|
||||
* yaffs_strnlen+1 bytes name null terminated.
|
||||
* nbytes value.
|
||||
* ----------
|
||||
* total size stored in record size
|
||||
*
|
||||
* This code has not been tested with unicode yet.
|
||||
*/
|
||||
|
||||
#include "yaffs_nameval.h"
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
static int nval_find(const char *xb, int xb_size, const YCHAR *name,
|
||||
int *exist_size)
|
||||
{
|
||||
int pos = 0;
|
||||
int size;
|
||||
|
||||
memcpy(&size, xb, sizeof(int));
|
||||
while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
|
||||
if (!yaffs_strncmp((YCHAR *) (xb + pos + sizeof(int)),
|
||||
name, size)) {
|
||||
if (exist_size)
|
||||
*exist_size = size;
|
||||
return pos;
|
||||
}
|
||||
pos += size;
|
||||
if (pos < xb_size - (int)sizeof(int))
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
else
|
||||
size = 0;
|
||||
}
|
||||
if (exist_size)
|
||||
*exist_size = 0;
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
static int nval_used(const char *xb, int xb_size)
|
||||
{
|
||||
int pos = 0;
|
||||
int size;
|
||||
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
|
||||
pos += size;
|
||||
if (pos < xb_size - (int)sizeof(int))
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
else
|
||||
size = 0;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
int nval_del(char *xb, int xb_size, const YCHAR *name)
|
||||
{
|
||||
int pos = nval_find(xb, xb_size, name, NULL);
|
||||
int size;
|
||||
|
||||
if (pos < 0 || pos >= xb_size)
|
||||
return -ENODATA;
|
||||
|
||||
/* Find size, shift rest over this record,
|
||||
* then zero out the rest of buffer */
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
memcpy(xb + pos, xb + pos + size, xb_size - (pos + size));
|
||||
memset(xb + (xb_size - size), 0, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf,
|
||||
int bsize, int flags)
|
||||
{
|
||||
int pos;
|
||||
int namelen = yaffs_strnlen(name, xb_size);
|
||||
int reclen;
|
||||
int size_exist = 0;
|
||||
int space;
|
||||
int start;
|
||||
|
||||
pos = nval_find(xb, xb_size, name, &size_exist);
|
||||
|
||||
if (flags & XATTR_CREATE && pos >= 0)
|
||||
return -EEXIST;
|
||||
if (flags & XATTR_REPLACE && pos < 0)
|
||||
return -ENODATA;
|
||||
|
||||
start = nval_used(xb, xb_size);
|
||||
space = xb_size - start + size_exist;
|
||||
|
||||
reclen = (sizeof(int) + namelen + 1 + bsize);
|
||||
|
||||
if (reclen > space)
|
||||
return -ENOSPC;
|
||||
|
||||
if (pos >= 0) {
|
||||
nval_del(xb, xb_size, name);
|
||||
start = nval_used(xb, xb_size);
|
||||
}
|
||||
|
||||
pos = start;
|
||||
|
||||
memcpy(xb + pos, &reclen, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
yaffs_strncpy((YCHAR *) (xb + pos), name, reclen);
|
||||
pos += (namelen + 1);
|
||||
memcpy(xb + pos, buf, bsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,
|
||||
int bsize)
|
||||
{
|
||||
int pos = nval_find(xb, xb_size, name, NULL);
|
||||
int size;
|
||||
|
||||
if (pos >= 0 && pos < xb_size) {
|
||||
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
pos += sizeof(int); /* advance past record length */
|
||||
size -= sizeof(int);
|
||||
|
||||
/* Advance over name string */
|
||||
while (xb[pos] && size > 0 && pos < xb_size) {
|
||||
pos++;
|
||||
size--;
|
||||
}
|
||||
/*Advance over NUL */
|
||||
pos++;
|
||||
size--;
|
||||
|
||||
/* If bsize is zero then this is a size query.
|
||||
* Return the size, but don't copy.
|
||||
*/
|
||||
if (!bsize)
|
||||
return size;
|
||||
|
||||
if (size <= bsize) {
|
||||
memcpy(buf, xb + pos, size);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
if (pos >= 0)
|
||||
return -ERANGE;
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
int nval_list(const char *xb, int xb_size, char *buf, int bsize)
|
||||
{
|
||||
int pos = 0;
|
||||
int size;
|
||||
int name_len;
|
||||
int ncopied = 0;
|
||||
int filled = 0;
|
||||
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
while (size > (int)sizeof(int) &&
|
||||
size <= xb_size &&
|
||||
(pos + size) < xb_size &&
|
||||
!filled) {
|
||||
pos += sizeof(int);
|
||||
size -= sizeof(int);
|
||||
name_len = yaffs_strnlen((YCHAR *) (xb + pos), size);
|
||||
if (ncopied + name_len + 1 < bsize) {
|
||||
memcpy(buf, xb + pos, name_len * sizeof(YCHAR));
|
||||
buf += name_len;
|
||||
*buf = '\0';
|
||||
buf++;
|
||||
if (sizeof(YCHAR) > 1) {
|
||||
*buf = '\0';
|
||||
buf++;
|
||||
}
|
||||
ncopied += (name_len + 1);
|
||||
} else {
|
||||
filled = 1;
|
||||
}
|
||||
pos += size;
|
||||
if (pos < xb_size - (int)sizeof(int))
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
else
|
||||
size = 0;
|
||||
}
|
||||
return ncopied;
|
||||
}
|
||||
|
||||
int nval_hasvalues(const char *xb, int xb_size)
|
||||
{
|
||||
return nval_used(xb, xb_size) > 0;
|
||||
}
|
122
flight/pios/common/libraries/yaffs2/yaffs_nand.c
Normal file
122
flight/pios/common/libraries/yaffs2/yaffs_nand.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_tagscompat.h"
|
||||
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_summary.h"
|
||||
|
||||
static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)
|
||||
{
|
||||
return chunk - dev->chunk_offset;
|
||||
}
|
||||
|
||||
int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *buffer, struct yaffs_ext_tags *tags)
|
||||
{
|
||||
int result;
|
||||
struct yaffs_ext_tags local_tags;
|
||||
int flash_chunk = apply_chunk_offset(dev, nand_chunk);
|
||||
|
||||
dev->n_page_reads++;
|
||||
|
||||
/* If there are no tags provided use local tags. */
|
||||
if (!tags)
|
||||
tags = &local_tags;
|
||||
|
||||
result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags);
|
||||
if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) {
|
||||
|
||||
struct yaffs_block_info *bi;
|
||||
bi = yaffs_get_block_info(dev,
|
||||
nand_chunk /
|
||||
dev->param.chunks_per_block);
|
||||
yaffs_handle_chunk_error(dev, bi);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *buffer, struct yaffs_ext_tags *tags)
|
||||
{
|
||||
int result;
|
||||
int flash_chunk = apply_chunk_offset(dev, nand_chunk);
|
||||
|
||||
dev->n_page_writes++;
|
||||
|
||||
if (!tags) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags");
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
tags->seq_number = dev->seq_number;
|
||||
tags->chunk_used = 1;
|
||||
yaffs_trace(YAFFS_TRACE_WRITE,
|
||||
"Writing chunk %d tags %d %d",
|
||||
nand_chunk, tags->obj_id, tags->chunk_id);
|
||||
|
||||
result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk,
|
||||
buffer, tags);
|
||||
|
||||
yaffs_summary_add(dev, tags, nand_chunk);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_mark_bad(struct yaffs_dev *dev, int block_no)
|
||||
{
|
||||
block_no -= dev->block_offset;
|
||||
dev->n_bad_markings++;
|
||||
|
||||
if (dev->param.disable_bad_block_marking)
|
||||
return YAFFS_OK;
|
||||
|
||||
return dev->tagger.mark_bad_fn(dev, block_no);
|
||||
}
|
||||
|
||||
|
||||
int yaffs_query_init_block_state(struct yaffs_dev *dev,
|
||||
int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number)
|
||||
{
|
||||
block_no -= dev->block_offset;
|
||||
return dev->tagger.query_block_fn(dev, block_no, state, seq_number);
|
||||
}
|
||||
|
||||
int yaffs_erase_block(struct yaffs_dev *dev, int block_no)
|
||||
{
|
||||
int result;
|
||||
|
||||
block_no -= dev->block_offset;
|
||||
dev->n_erasures++;
|
||||
result = dev->drv.drv_erase_fn(dev, block_no);
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_init_nand(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->drv.drv_initialise_fn)
|
||||
return dev->drv.drv_initialise_fn(dev);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
int yaffs_deinit_nand(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->drv.drv_deinitialise_fn)
|
||||
return dev->drv.drv_deinitialise_fn(dev);
|
||||
return YAFFS_OK;
|
||||
}
|
197
flight/pios/common/libraries/yaffs2/yaffs_packedtags2.c
Normal file
197
flight/pios/common/libraries/yaffs2/yaffs_packedtags2.c
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_packedtags2.h"
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
/* This code packs a set of extended tags into a binary structure for
|
||||
* NAND storage
|
||||
*/
|
||||
|
||||
/* Some of the information is "extra" struff which can be packed in to
|
||||
* speed scanning
|
||||
* This is defined by having the EXTRA_HEADER_INFO_FLAG set.
|
||||
*/
|
||||
|
||||
/* Extra flags applied to chunk_id */
|
||||
|
||||
#define EXTRA_HEADER_INFO_FLAG 0x80000000
|
||||
#define EXTRA_SHRINK_FLAG 0x40000000
|
||||
#define EXTRA_SHADOWS_FLAG 0x20000000
|
||||
#define EXTRA_SPARE_FLAGS 0x10000000
|
||||
|
||||
#define ALL_EXTRA_FLAGS 0xf0000000
|
||||
|
||||
/* Also, the top 4 bits of the object Id are set to the object type. */
|
||||
#define EXTRA_OBJECT_TYPE_SHIFT (28)
|
||||
#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT)
|
||||
|
||||
static void yaffs_dump_packed_tags2_tags_only(
|
||||
const struct yaffs_packed_tags2_tags_only *ptt)
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"packed tags obj %d chunk %d byte %d seq %d",
|
||||
ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number);
|
||||
}
|
||||
|
||||
static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt)
|
||||
{
|
||||
yaffs_dump_packed_tags2_tags_only(&pt->t);
|
||||
}
|
||||
|
||||
static void yaffs_dump_tags2(const struct yaffs_ext_tags *t)
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d",
|
||||
t->ecc_result, t->block_bad, t->chunk_used, t->obj_id,
|
||||
t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number,
|
||||
t->seq_number);
|
||||
|
||||
}
|
||||
|
||||
static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t)
|
||||
{
|
||||
if (t->chunk_id != 0 || !t->extra_available)
|
||||
return 0;
|
||||
|
||||
/* Check if the file size is too long to store */
|
||||
if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE &&
|
||||
(t->extra_file_size >> 31) != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt,
|
||||
const struct yaffs_ext_tags *t)
|
||||
{
|
||||
ptt->chunk_id = t->chunk_id;
|
||||
ptt->seq_number = t->seq_number;
|
||||
ptt->n_bytes = t->n_bytes;
|
||||
ptt->obj_id = t->obj_id;
|
||||
|
||||
/* Only store extra tags for object headers.
|
||||
* If it is a file then only store if the file size is short\
|
||||
* enough to fit.
|
||||
*/
|
||||
if (yaffs_check_tags_extra_packable(t)) {
|
||||
/* Store the extra header info instead */
|
||||
/* We save the parent object in the chunk_id */
|
||||
ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id;
|
||||
if (t->extra_is_shrink)
|
||||
ptt->chunk_id |= EXTRA_SHRINK_FLAG;
|
||||
if (t->extra_shadows)
|
||||
ptt->chunk_id |= EXTRA_SHADOWS_FLAG;
|
||||
|
||||
ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;
|
||||
ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT);
|
||||
|
||||
if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
||||
ptt->n_bytes = t->extra_equiv_id;
|
||||
else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
ptt->n_bytes = (unsigned) t->extra_file_size;
|
||||
else
|
||||
ptt->n_bytes = 0;
|
||||
}
|
||||
|
||||
yaffs_dump_packed_tags2_tags_only(ptt);
|
||||
yaffs_dump_tags2(t);
|
||||
}
|
||||
|
||||
void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,
|
||||
const struct yaffs_ext_tags *t, int tags_ecc)
|
||||
{
|
||||
yaffs_pack_tags2_tags_only(&pt->t, t);
|
||||
|
||||
if (tags_ecc)
|
||||
yaffs_ecc_calc_other((unsigned char *)&pt->t,
|
||||
sizeof(struct yaffs_packed_tags2_tags_only),
|
||||
&pt->ecc);
|
||||
}
|
||||
|
||||
void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,
|
||||
struct yaffs_packed_tags2_tags_only *ptt)
|
||||
{
|
||||
memset(t, 0, sizeof(struct yaffs_ext_tags));
|
||||
|
||||
if (ptt->seq_number == 0xffffffff)
|
||||
return;
|
||||
|
||||
t->block_bad = 0;
|
||||
t->chunk_used = 1;
|
||||
t->obj_id = ptt->obj_id;
|
||||
t->chunk_id = ptt->chunk_id;
|
||||
t->n_bytes = ptt->n_bytes;
|
||||
t->is_deleted = 0;
|
||||
t->serial_number = 0;
|
||||
t->seq_number = ptt->seq_number;
|
||||
|
||||
/* Do extra header info stuff */
|
||||
if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) {
|
||||
t->chunk_id = 0;
|
||||
t->n_bytes = 0;
|
||||
|
||||
t->extra_available = 1;
|
||||
t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS));
|
||||
t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0;
|
||||
t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0;
|
||||
t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT;
|
||||
t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;
|
||||
|
||||
if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
||||
t->extra_equiv_id = ptt->n_bytes;
|
||||
else
|
||||
t->extra_file_size = ptt->n_bytes;
|
||||
}
|
||||
yaffs_dump_packed_tags2_tags_only(ptt);
|
||||
yaffs_dump_tags2(t);
|
||||
}
|
||||
|
||||
void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,
|
||||
int tags_ecc)
|
||||
{
|
||||
enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
||||
|
||||
if (pt->t.seq_number != 0xffffffff && tags_ecc) {
|
||||
/* Chunk is in use and we need to do ECC */
|
||||
|
||||
struct yaffs_ecc_other ecc;
|
||||
int result;
|
||||
yaffs_ecc_calc_other((unsigned char *)&pt->t,
|
||||
sizeof(struct yaffs_packed_tags2_tags_only),
|
||||
&ecc);
|
||||
result =
|
||||
yaffs_ecc_correct_other((unsigned char *)&pt->t,
|
||||
sizeof(struct yaffs_packed_tags2_tags_only),
|
||||
&pt->ecc, &ecc);
|
||||
switch (result) {
|
||||
case 0:
|
||||
ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
||||
break;
|
||||
case 1:
|
||||
ecc_result = YAFFS_ECC_RESULT_FIXED;
|
||||
break;
|
||||
case -1:
|
||||
ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
||||
break;
|
||||
default:
|
||||
ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
yaffs_unpack_tags2_tags_only(t, &pt->t);
|
||||
|
||||
t->ecc_result = ecc_result;
|
||||
|
||||
yaffs_dump_packed_tags2(pt);
|
||||
yaffs_dump_tags2(t);
|
||||
}
|
163
flight/pios/common/libraries/yaffs2/yaffs_qsort.c
Normal file
163
flight/pios/common/libraries/yaffs2/yaffs_qsort.c
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* 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 University 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 REGENTS 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 REGENTS 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 "yportenv.h"
|
||||
/* #include <linux/string.h> */
|
||||
|
||||
/*
|
||||
* Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
|
||||
*/
|
||||
#define swapcode(TYPE, parmi, parmj, n) do { \
|
||||
long i = (n) / sizeof(TYPE); \
|
||||
register TYPE *pi = (TYPE *) (parmi); \
|
||||
register TYPE *pj = (TYPE *) (parmj); \
|
||||
do { \
|
||||
register TYPE t = *pi; \
|
||||
*pi++ = *pj; \
|
||||
*pj++ = t; \
|
||||
} while (--i > 0); \
|
||||
} while (0)
|
||||
|
||||
#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
|
||||
es % sizeof(long) ? 2 : es == sizeof(long) ? 0 : 1;
|
||||
|
||||
static inline void
|
||||
swapfunc(char *a, char *b, int n, int swaptype)
|
||||
{
|
||||
if (swaptype <= 1)
|
||||
swapcode(long, a, b, n);
|
||||
else
|
||||
swapcode(char, a, b, n);
|
||||
}
|
||||
|
||||
#define yswap(a, b) do { \
|
||||
if (swaptype == 0) { \
|
||||
long t = *(long *)(a); \
|
||||
*(long *)(a) = *(long *)(b); \
|
||||
*(long *)(b) = t; \
|
||||
} else \
|
||||
swapfunc(a, b, es, swaptype); \
|
||||
} while (0)
|
||||
|
||||
#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
|
||||
|
||||
static inline char *
|
||||
med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
|
||||
{
|
||||
return cmp(a, b) < 0 ?
|
||||
(cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a))
|
||||
: (cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c));
|
||||
}
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
void
|
||||
yaffs_qsort(void *aa, size_t n, size_t es,
|
||||
int (*cmp)(const void *, const void *))
|
||||
{
|
||||
char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
|
||||
int d, r, swaptype, swap_cnt;
|
||||
register char *a = aa;
|
||||
|
||||
loop: SWAPINIT(a, es);
|
||||
swap_cnt = 0;
|
||||
if (n < 7) {
|
||||
for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
|
||||
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
|
||||
pl -= es)
|
||||
yswap(pl, pl - es);
|
||||
return;
|
||||
}
|
||||
pm = (char *)a + (n / 2) * es;
|
||||
if (n > 7) {
|
||||
pl = (char *)a;
|
||||
pn = (char *)a + (n - 1) * es;
|
||||
if (n > 40) {
|
||||
d = (n / 8) * es;
|
||||
pl = med3(pl, pl + d, pl + 2 * d, cmp);
|
||||
pm = med3(pm - d, pm, pm + d, cmp);
|
||||
pn = med3(pn - 2 * d, pn - d, pn, cmp);
|
||||
}
|
||||
pm = med3(pl, pm, pn, cmp);
|
||||
}
|
||||
yswap(a, pm);
|
||||
pa = pb = (char *)a + es;
|
||||
|
||||
pc = pd = (char *)a + (n - 1) * es;
|
||||
for (;;) {
|
||||
while (pb <= pc && (r = cmp(pb, a)) <= 0) {
|
||||
if (r == 0) {
|
||||
swap_cnt = 1;
|
||||
yswap(pa, pb);
|
||||
pa += es;
|
||||
}
|
||||
pb += es;
|
||||
}
|
||||
while (pb <= pc && (r = cmp(pc, a)) >= 0) {
|
||||
if (r == 0) {
|
||||
swap_cnt = 1;
|
||||
yswap(pc, pd);
|
||||
pd -= es;
|
||||
}
|
||||
pc -= es;
|
||||
}
|
||||
if (pb > pc)
|
||||
break;
|
||||
yswap(pb, pc);
|
||||
swap_cnt = 1;
|
||||
pb += es;
|
||||
pc -= es;
|
||||
}
|
||||
if (swap_cnt == 0) { /* Switch to insertion sort */
|
||||
for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
|
||||
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
|
||||
pl -= es)
|
||||
yswap(pl, pl - es);
|
||||
return;
|
||||
}
|
||||
|
||||
pn = (char *)a + n * es;
|
||||
r = min(pa - (char *)a, pb - pa);
|
||||
vecswap(a, pb - r, r);
|
||||
r = min((long)(pd - pc), (long)(pn - pd - es));
|
||||
vecswap(pb, pn - r, r);
|
||||
r = pb - pa;
|
||||
if (r > (int)es)
|
||||
yaffs_qsort(a, r / es, es, cmp);
|
||||
r = pd - pc;
|
||||
if (r > (int)es) {
|
||||
/* Iterate rather than recurse to save stack space */
|
||||
a = pn - r;
|
||||
n = r / es;
|
||||
goto loop;
|
||||
}
|
||||
/* yaffs_qsort(pn - r, r / es, es, cmp);*/
|
||||
}
|
315
flight/pios/common/libraries/yaffs2/yaffs_summary.c
Normal file
315
flight/pios/common/libraries/yaffs2/yaffs_summary.c
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* Summaries write the useful part of the tags for the chunks in a block into an
|
||||
* an array which is written to the last n chunks of the block.
|
||||
* Reading the summaries gives all the tags for the block in one read. Much
|
||||
* faster.
|
||||
*
|
||||
* Chunks holding summaries are marked with tags making it look like
|
||||
* they are part of a fake file.
|
||||
*
|
||||
* The summary could also be used during gc.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "yaffs_summary.h"
|
||||
#include "yaffs_packedtags2.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
|
||||
/*
|
||||
* The summary is built up in an array of summary tags.
|
||||
* This gets written to the last one or two (maybe more) chunks in a block.
|
||||
* A summary header is written as the first part of each chunk of summary data.
|
||||
* The summary header must match or the summary is rejected.
|
||||
*/
|
||||
|
||||
/* Summary tags don't need the sequence number because that is redundant. */
|
||||
struct yaffs_summary_tags {
|
||||
unsigned obj_id;
|
||||
unsigned chunk_id;
|
||||
unsigned n_bytes;
|
||||
};
|
||||
|
||||
/* Summary header */
|
||||
struct yaffs_summary_header {
|
||||
unsigned version; /* Must match current version */
|
||||
unsigned block; /* Must be this block */
|
||||
unsigned seq; /* Must be this sequence number */
|
||||
unsigned sum; /* Just add up all the bytes in the tags */
|
||||
};
|
||||
|
||||
|
||||
static void yaffs_summary_clear(struct yaffs_dev *dev)
|
||||
{
|
||||
if (!dev->sum_tags)
|
||||
return;
|
||||
memset(dev->sum_tags, 0, dev->chunks_per_summary *
|
||||
sizeof(struct yaffs_summary_tags));
|
||||
}
|
||||
|
||||
|
||||
void yaffs_summary_deinit(struct yaffs_dev *dev)
|
||||
{
|
||||
kfree(dev->sum_tags);
|
||||
dev->sum_tags = NULL;
|
||||
kfree(dev->gc_sum_tags);
|
||||
dev->gc_sum_tags = NULL;
|
||||
dev->chunks_per_summary = 0;
|
||||
}
|
||||
|
||||
int yaffs_summary_init(struct yaffs_dev *dev)
|
||||
{
|
||||
int sum_bytes;
|
||||
int chunks_used; /* Number of chunks used by summary */
|
||||
int sum_tags_bytes;
|
||||
|
||||
sum_bytes = dev->param.chunks_per_block *
|
||||
sizeof(struct yaffs_summary_tags);
|
||||
|
||||
chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/
|
||||
(dev->data_bytes_per_chunk -
|
||||
sizeof(struct yaffs_summary_header));
|
||||
|
||||
dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used;
|
||||
sum_tags_bytes = sizeof(struct yaffs_summary_tags) *
|
||||
dev->chunks_per_summary;
|
||||
dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
|
||||
dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
|
||||
if (!dev->sum_tags || !dev->gc_sum_tags) {
|
||||
yaffs_summary_deinit(dev);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
yaffs_summary_clear(dev);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static unsigned yaffs_summary_sum(struct yaffs_dev *dev)
|
||||
{
|
||||
u8 *sum_buffer = (u8 *)dev->sum_tags;
|
||||
int i;
|
||||
unsigned sum = 0;
|
||||
|
||||
i = sizeof(struct yaffs_summary_tags) *
|
||||
dev->chunks_per_summary;
|
||||
while (i > 0) {
|
||||
sum += *sum_buffer;
|
||||
sum_buffer++;
|
||||
i--;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static int yaffs_summary_write(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
struct yaffs_ext_tags tags;
|
||||
u8 *buffer;
|
||||
u8 *sum_buffer = (u8 *)dev->sum_tags;
|
||||
int n_bytes;
|
||||
int chunk_in_nand;
|
||||
int chunk_in_block;
|
||||
int result;
|
||||
int this_tx;
|
||||
struct yaffs_summary_header hdr;
|
||||
int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
|
||||
|
||||
buffer = yaffs_get_temp_buffer(dev);
|
||||
n_bytes = sizeof(struct yaffs_summary_tags) *
|
||||
dev->chunks_per_summary;
|
||||
memset(&tags, 0, sizeof(struct yaffs_ext_tags));
|
||||
tags.obj_id = YAFFS_OBJECTID_SUMMARY;
|
||||
tags.chunk_id = 1;
|
||||
chunk_in_block = dev->chunks_per_summary;
|
||||
chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block +
|
||||
dev->chunks_per_summary;
|
||||
hdr.version = YAFFS_SUMMARY_VERSION;
|
||||
hdr.block = blk;
|
||||
hdr.seq = bi->seq_number;
|
||||
hdr.sum = yaffs_summary_sum(dev);
|
||||
|
||||
do {
|
||||
this_tx = n_bytes;
|
||||
if (this_tx > sum_bytes_per_chunk)
|
||||
this_tx = sum_bytes_per_chunk;
|
||||
memcpy(buffer, &hdr, sizeof(hdr));
|
||||
memcpy(buffer + sizeof(hdr), sum_buffer, this_tx);
|
||||
tags.n_bytes = this_tx + sizeof(hdr);
|
||||
result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand,
|
||||
buffer, &tags);
|
||||
|
||||
if (result != YAFFS_OK)
|
||||
break;
|
||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
||||
bi->pages_in_use++;
|
||||
dev->n_free_chunks--;
|
||||
|
||||
n_bytes -= this_tx;
|
||||
sum_buffer += this_tx;
|
||||
chunk_in_nand++;
|
||||
chunk_in_block++;
|
||||
tags.chunk_id++;
|
||||
} while (result == YAFFS_OK && n_bytes > 0);
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
|
||||
|
||||
if (result == YAFFS_OK)
|
||||
bi->has_summary = 1;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_summary_read(struct yaffs_dev *dev,
|
||||
struct yaffs_summary_tags *st,
|
||||
int blk)
|
||||
{
|
||||
struct yaffs_ext_tags tags;
|
||||
u8 *buffer;
|
||||
u8 *sum_buffer = (u8 *)st;
|
||||
int n_bytes;
|
||||
int chunk_id;
|
||||
int chunk_in_nand;
|
||||
int chunk_in_block;
|
||||
int result;
|
||||
int this_tx;
|
||||
struct yaffs_summary_header hdr;
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
|
||||
int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
|
||||
#ifndef CONFIG_YAFFS_OP
|
||||
int sum_tags_bytes;
|
||||
|
||||
sum_tags_bytes = sizeof(struct yaffs_summary_tags) *
|
||||
dev->chunks_per_summary;
|
||||
#endif //unused code
|
||||
|
||||
buffer = yaffs_get_temp_buffer(dev);
|
||||
n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary;
|
||||
chunk_in_block = dev->chunks_per_summary;
|
||||
chunk_in_nand = blk * dev->param.chunks_per_block +
|
||||
dev->chunks_per_summary;
|
||||
chunk_id = 1;
|
||||
do {
|
||||
this_tx = n_bytes;
|
||||
if (this_tx > sum_bytes_per_chunk)
|
||||
this_tx = sum_bytes_per_chunk;
|
||||
result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand,
|
||||
buffer, &tags);
|
||||
|
||||
if ((int)tags.chunk_id != chunk_id ||
|
||||
tags.obj_id != YAFFS_OBJECTID_SUMMARY ||
|
||||
tags.chunk_used == 0 ||
|
||||
tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
|
||||
tags.n_bytes != (this_tx + sizeof(hdr)))
|
||||
result = YAFFS_FAIL;
|
||||
if (result != YAFFS_OK)
|
||||
break;
|
||||
|
||||
if (st == dev->sum_tags) {
|
||||
/* If we're scanning then update the block info */
|
||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
||||
bi->pages_in_use++;
|
||||
}
|
||||
memcpy(&hdr, buffer, sizeof(hdr));
|
||||
memcpy(sum_buffer, buffer + sizeof(hdr), this_tx);
|
||||
n_bytes -= this_tx;
|
||||
sum_buffer += this_tx;
|
||||
chunk_in_nand++;
|
||||
chunk_in_block++;
|
||||
chunk_id++;
|
||||
} while (result == YAFFS_OK && n_bytes > 0);
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
|
||||
if (result == YAFFS_OK) {
|
||||
/* Verify header */
|
||||
if (hdr.version != YAFFS_SUMMARY_VERSION ||
|
||||
hdr.seq != bi->seq_number ||
|
||||
hdr.sum != yaffs_summary_sum(dev))
|
||||
result = YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (st == dev->sum_tags && result == YAFFS_OK)
|
||||
bi->has_summary = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_summary_add(struct yaffs_dev *dev,
|
||||
struct yaffs_ext_tags *tags,
|
||||
int chunk_in_nand)
|
||||
{
|
||||
struct yaffs_packed_tags2_tags_only tags_only;
|
||||
struct yaffs_summary_tags *sum_tags;
|
||||
int block_in_nand = chunk_in_nand / dev->param.chunks_per_block;
|
||||
int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block;
|
||||
|
||||
if (!dev->sum_tags)
|
||||
return YAFFS_OK;
|
||||
|
||||
if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
|
||||
yaffs_pack_tags2_tags_only(&tags_only, tags);
|
||||
sum_tags = &dev->sum_tags[chunk_in_block];
|
||||
sum_tags->chunk_id = tags_only.chunk_id;
|
||||
sum_tags->n_bytes = tags_only.n_bytes;
|
||||
sum_tags->obj_id = tags_only.obj_id;
|
||||
|
||||
if (chunk_in_block == dev->chunks_per_summary - 1) {
|
||||
/* Time to write out the summary */
|
||||
yaffs_summary_write(dev, block_in_nand);
|
||||
yaffs_summary_clear(dev);
|
||||
yaffs_skip_rest_of_block(dev);
|
||||
}
|
||||
}
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
int yaffs_summary_fetch(struct yaffs_dev *dev,
|
||||
struct yaffs_ext_tags *tags,
|
||||
int chunk_in_block)
|
||||
{
|
||||
struct yaffs_packed_tags2_tags_only tags_only;
|
||||
struct yaffs_summary_tags *sum_tags;
|
||||
if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
|
||||
sum_tags = &dev->sum_tags[chunk_in_block];
|
||||
tags_only.chunk_id = sum_tags->chunk_id;
|
||||
tags_only.n_bytes = sum_tags->n_bytes;
|
||||
tags_only.obj_id = sum_tags->obj_id;
|
||||
yaffs_unpack_tags2_tags_only(tags, &tags_only);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
void yaffs_summary_gc(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
|
||||
int i;
|
||||
|
||||
if (!bi->has_summary)
|
||||
return;
|
||||
|
||||
for (i = dev->chunks_per_summary;
|
||||
i < dev->param.chunks_per_block;
|
||||
i++) {
|
||||
if (yaffs_check_chunk_bit(dev, blk, i)) {
|
||||
yaffs_clear_chunk_bit(dev, blk, i);
|
||||
bi->pages_in_use--;
|
||||
dev->n_free_chunks++;
|
||||
}
|
||||
}
|
||||
}
|
381
flight/pios/common/libraries/yaffs2/yaffs_tagscompat.c
Normal file
381
flight/pios/common/libraries/yaffs2/yaffs_tagscompat.c
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_tagscompat.h"
|
||||
#include "yaffs_ecc.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
|
||||
|
||||
|
||||
/********** Tags ECC calculations *********/
|
||||
|
||||
|
||||
void yaffs_calc_tags_ecc(struct yaffs_tags *tags)
|
||||
{
|
||||
/* Calculate an ecc */
|
||||
unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
|
||||
unsigned i, j;
|
||||
unsigned ecc = 0;
|
||||
unsigned bit = 0;
|
||||
|
||||
tags->ecc = 0;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (j = 1; j & 0xff; j <<= 1) {
|
||||
bit++;
|
||||
if (b[i] & j)
|
||||
ecc ^= bit;
|
||||
}
|
||||
}
|
||||
tags->ecc = ecc;
|
||||
}
|
||||
|
||||
int yaffs_check_tags_ecc(struct yaffs_tags *tags)
|
||||
{
|
||||
unsigned ecc = tags->ecc;
|
||||
|
||||
yaffs_calc_tags_ecc(tags);
|
||||
|
||||
ecc ^= tags->ecc;
|
||||
|
||||
if (ecc && ecc <= 64) {
|
||||
/* TODO: Handle the failure better. Retire? */
|
||||
unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
|
||||
|
||||
ecc--;
|
||||
|
||||
b[ecc / 8] ^= (1 << (ecc & 7));
|
||||
|
||||
/* Now recvalc the ecc */
|
||||
yaffs_calc_tags_ecc(tags);
|
||||
|
||||
return 1; /* recovered error */
|
||||
} else if (ecc) {
|
||||
/* Wierd ecc failure value */
|
||||
/* TODO Need to do somethiong here */
|
||||
return -1; /* unrecovered error */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********** Tags **********/
|
||||
|
||||
static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
|
||||
struct yaffs_tags *tags_ptr)
|
||||
{
|
||||
union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
|
||||
|
||||
yaffs_calc_tags_ecc(tags_ptr);
|
||||
|
||||
spare_ptr->tb0 = tu->as_bytes[0];
|
||||
spare_ptr->tb1 = tu->as_bytes[1];
|
||||
spare_ptr->tb2 = tu->as_bytes[2];
|
||||
spare_ptr->tb3 = tu->as_bytes[3];
|
||||
spare_ptr->tb4 = tu->as_bytes[4];
|
||||
spare_ptr->tb5 = tu->as_bytes[5];
|
||||
spare_ptr->tb6 = tu->as_bytes[6];
|
||||
spare_ptr->tb7 = tu->as_bytes[7];
|
||||
}
|
||||
|
||||
static void yaffs_get_tags_from_spare(struct yaffs_dev *dev,
|
||||
struct yaffs_spare *spare_ptr,
|
||||
struct yaffs_tags *tags_ptr)
|
||||
{
|
||||
union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
|
||||
int result;
|
||||
|
||||
tu->as_bytes[0] = spare_ptr->tb0;
|
||||
tu->as_bytes[1] = spare_ptr->tb1;
|
||||
tu->as_bytes[2] = spare_ptr->tb2;
|
||||
tu->as_bytes[3] = spare_ptr->tb3;
|
||||
tu->as_bytes[4] = spare_ptr->tb4;
|
||||
tu->as_bytes[5] = spare_ptr->tb5;
|
||||
tu->as_bytes[6] = spare_ptr->tb6;
|
||||
tu->as_bytes[7] = spare_ptr->tb7;
|
||||
|
||||
result = yaffs_check_tags_ecc(tags_ptr);
|
||||
if (result > 0)
|
||||
dev->n_tags_ecc_fixed++;
|
||||
else if (result < 0)
|
||||
dev->n_tags_ecc_unfixed++;
|
||||
}
|
||||
|
||||
static void yaffs_spare_init(struct yaffs_spare *spare)
|
||||
{
|
||||
memset(spare, 0xff, sizeof(struct yaffs_spare));
|
||||
}
|
||||
|
||||
static int yaffs_wr_nand(struct yaffs_dev *dev,
|
||||
int nand_chunk, const u8 *data,
|
||||
struct yaffs_spare *spare)
|
||||
{
|
||||
int data_size = dev->data_bytes_per_chunk;
|
||||
|
||||
return dev->drv.drv_write_chunk_fn(dev, nand_chunk,
|
||||
data, data_size,
|
||||
(u8 *) spare, sizeof(*spare));
|
||||
}
|
||||
|
||||
static int yaffs_rd_chunk_nand(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
u8 *data,
|
||||
struct yaffs_spare *spare,
|
||||
enum yaffs_ecc_result *ecc_result,
|
||||
int correct_errors)
|
||||
{
|
||||
int ret_val;
|
||||
struct yaffs_spare local_spare;
|
||||
int data_size;
|
||||
int spare_size;
|
||||
int ecc_result1, ecc_result2;
|
||||
u8 calc_ecc[3];
|
||||
|
||||
if (!spare) {
|
||||
/* If we don't have a real spare, then we use a local one. */
|
||||
/* Need this for the calculation of the ecc */
|
||||
spare = &local_spare;
|
||||
}
|
||||
data_size = dev->data_bytes_per_chunk;
|
||||
spare_size = sizeof(struct yaffs_spare);
|
||||
|
||||
if (dev->param.use_nand_ecc)
|
||||
return dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
||||
data, data_size,
|
||||
(u8 *) spare, spare_size,
|
||||
ecc_result);
|
||||
|
||||
|
||||
/* Handle the ECC at this level. */
|
||||
|
||||
ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
||||
data, data_size,
|
||||
(u8 *)spare, spare_size,
|
||||
NULL);
|
||||
if (!data || !correct_errors)
|
||||
return ret_val;
|
||||
|
||||
/* Do ECC correction if needed. */
|
||||
yaffs_ecc_calc(data, calc_ecc);
|
||||
ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc);
|
||||
yaffs_ecc_calc(&data[256], calc_ecc);
|
||||
ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc);
|
||||
|
||||
if (ecc_result1 > 0) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>>yaffs ecc error fix performed on chunk %d:0",
|
||||
nand_chunk);
|
||||
dev->n_ecc_fixed++;
|
||||
} else if (ecc_result1 < 0) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>>yaffs ecc error unfixed on chunk %d:0",
|
||||
nand_chunk);
|
||||
dev->n_ecc_unfixed++;
|
||||
}
|
||||
|
||||
if (ecc_result2 > 0) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>>yaffs ecc error fix performed on chunk %d:1",
|
||||
nand_chunk);
|
||||
dev->n_ecc_fixed++;
|
||||
} else if (ecc_result2 < 0) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>>yaffs ecc error unfixed on chunk %d:1",
|
||||
nand_chunk);
|
||||
dev->n_ecc_unfixed++;
|
||||
}
|
||||
|
||||
if (ecc_result1 || ecc_result2) {
|
||||
/* We had a data problem on this page */
|
||||
yaffs_handle_rd_data_error(dev, nand_chunk);
|
||||
}
|
||||
|
||||
if (ecc_result1 < 0 || ecc_result2 < 0)
|
||||
*ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
||||
else if (ecc_result1 > 0 || ecc_result2 > 0)
|
||||
*ecc_result = YAFFS_ECC_RESULT_FIXED;
|
||||
else
|
||||
*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for robustisizing
|
||||
*/
|
||||
|
||||
static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk)
|
||||
{
|
||||
int flash_block = nand_chunk / dev->param.chunks_per_block;
|
||||
|
||||
/* Mark the block for retirement */
|
||||
yaffs_get_block_info(dev, flash_block + dev->block_offset)->
|
||||
needs_retiring = 1;
|
||||
yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
||||
"**>>Block %d marked for retirement",
|
||||
flash_block);
|
||||
|
||||
/* TODO:
|
||||
* Just do a garbage collection on the affected block
|
||||
* then retire the block
|
||||
* NB recursion
|
||||
*/
|
||||
}
|
||||
|
||||
static int yaffs_tags_compat_wr(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *data, const struct yaffs_ext_tags *ext_tags)
|
||||
{
|
||||
struct yaffs_spare spare;
|
||||
struct yaffs_tags tags;
|
||||
|
||||
yaffs_spare_init(&spare);
|
||||
|
||||
if (ext_tags->is_deleted)
|
||||
spare.page_status = 0;
|
||||
else {
|
||||
tags.obj_id = ext_tags->obj_id;
|
||||
tags.chunk_id = ext_tags->chunk_id;
|
||||
|
||||
tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1);
|
||||
|
||||
if (dev->data_bytes_per_chunk >= 1024)
|
||||
tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;
|
||||
else
|
||||
tags.n_bytes_msb = 3;
|
||||
|
||||
tags.serial_number = ext_tags->serial_number;
|
||||
|
||||
if (!dev->param.use_nand_ecc && data) {
|
||||
yaffs_ecc_calc(data, spare.ecc1);
|
||||
yaffs_ecc_calc(&data[256], spare.ecc2);
|
||||
}
|
||||
|
||||
yaffs_load_tags_to_spare(&spare, &tags);
|
||||
}
|
||||
return yaffs_wr_nand(dev, nand_chunk, data, &spare);
|
||||
}
|
||||
|
||||
static int yaffs_tags_compat_rd(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
u8 *data, struct yaffs_ext_tags *ext_tags)
|
||||
{
|
||||
struct yaffs_spare spare;
|
||||
struct yaffs_tags tags;
|
||||
enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
|
||||
static struct yaffs_spare spare_ff;
|
||||
static int init;
|
||||
int deleted;
|
||||
|
||||
if (!init) {
|
||||
memset(&spare_ff, 0xff, sizeof(spare_ff));
|
||||
init = 1;
|
||||
}
|
||||
|
||||
if (!yaffs_rd_chunk_nand(dev, nand_chunk,
|
||||
data, &spare, &ecc_result, 1))
|
||||
return YAFFS_FAIL;
|
||||
|
||||
/* ext_tags may be NULL */
|
||||
if (!ext_tags)
|
||||
return YAFFS_OK;
|
||||
|
||||
deleted = (hweight8(spare.page_status) < 7) ? 1 : 0;
|
||||
|
||||
ext_tags->is_deleted = deleted;
|
||||
ext_tags->ecc_result = ecc_result;
|
||||
ext_tags->block_bad = 0; /* We're reading it */
|
||||
/* therefore it is not a bad block */
|
||||
ext_tags->chunk_used =
|
||||
memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0;
|
||||
|
||||
if (ext_tags->chunk_used) {
|
||||
yaffs_get_tags_from_spare(dev, &spare, &tags);
|
||||
ext_tags->obj_id = tags.obj_id;
|
||||
ext_tags->chunk_id = tags.chunk_id;
|
||||
ext_tags->n_bytes = tags.n_bytes_lsb;
|
||||
|
||||
if (dev->data_bytes_per_chunk >= 1024)
|
||||
ext_tags->n_bytes |=
|
||||
(((unsigned)tags.n_bytes_msb) << 10);
|
||||
|
||||
ext_tags->serial_number = tags.serial_number;
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block)
|
||||
{
|
||||
struct yaffs_spare spare;
|
||||
|
||||
memset(&spare, 0xff, sizeof(struct yaffs_spare));
|
||||
|
||||
spare.block_status = 'Y';
|
||||
|
||||
yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL,
|
||||
&spare);
|
||||
yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1,
|
||||
NULL, &spare);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
|
||||
int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number)
|
||||
{
|
||||
struct yaffs_spare spare0, spare1;
|
||||
static struct yaffs_spare spare_ff;
|
||||
static int init;
|
||||
enum yaffs_ecc_result dummy;
|
||||
|
||||
if (!init) {
|
||||
memset(&spare_ff, 0xff, sizeof(spare_ff));
|
||||
init = 1;
|
||||
}
|
||||
|
||||
*seq_number = 0;
|
||||
|
||||
/* Look for bad block markers in the first two chunks */
|
||||
yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block,
|
||||
NULL, &spare0, &dummy, 0);
|
||||
yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1,
|
||||
NULL, &spare1, &dummy, 0);
|
||||
|
||||
if (hweight8(spare0.block_status & spare1.block_status) < 7)
|
||||
*state = YAFFS_BLOCK_STATE_DEAD;
|
||||
else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0)
|
||||
*state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
else
|
||||
*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
void yaffs_tags_compat_install(struct yaffs_dev *dev)
|
||||
{
|
||||
if(dev->param.is_yaffs2)
|
||||
return;
|
||||
if(!dev->tagger.write_chunk_tags_fn)
|
||||
dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr;
|
||||
if(!dev->tagger.read_chunk_tags_fn)
|
||||
dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd;
|
||||
if(!dev->tagger.query_block_fn)
|
||||
dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
|
||||
if(!dev->tagger.mark_bad_fn)
|
||||
dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
|
||||
}
|
199
flight/pios/common/libraries/yaffs2/yaffs_tagsmarshall.c
Normal file
199
flight/pios/common/libraries/yaffs2/yaffs_tagsmarshall.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_packedtags2.h"
|
||||
|
||||
static int yaffs_tags_marshall_write(struct yaffs_dev *dev,
|
||||
int nand_chunk, const u8 *data,
|
||||
const struct yaffs_ext_tags *tags)
|
||||
{
|
||||
struct yaffs_packed_tags2 pt;
|
||||
int retval;
|
||||
|
||||
int packed_tags_size =
|
||||
dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
|
||||
void *packed_tags_ptr =
|
||||
dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"yaffs_tags_marshall_write chunk %d data %p tags %p",
|
||||
nand_chunk, data, tags);
|
||||
|
||||
/* For yaffs2 writing there must be both data and tags.
|
||||
* If we're using inband tags, then the tags are stuffed into
|
||||
* the end of the data buffer.
|
||||
*/
|
||||
if (!data || !tags)
|
||||
BUG();
|
||||
else if (dev->param.inband_tags) {
|
||||
struct yaffs_packed_tags2_tags_only *pt2tp;
|
||||
pt2tp =
|
||||
(struct yaffs_packed_tags2_tags_only *)(data +
|
||||
dev->
|
||||
data_bytes_per_chunk);
|
||||
yaffs_pack_tags2_tags_only(pt2tp, tags);
|
||||
} else {
|
||||
yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
|
||||
}
|
||||
|
||||
retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
|
||||
data, dev->param.total_bytes_per_chunk,
|
||||
(dev->param.inband_tags) ? NULL : packed_tags_ptr,
|
||||
(dev->param.inband_tags) ? 0 : packed_tags_size);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int yaffs_tags_marshall_read(struct yaffs_dev *dev,
|
||||
int nand_chunk, u8 *data,
|
||||
struct yaffs_ext_tags *tags)
|
||||
{
|
||||
//int retval = 0;
|
||||
int local_data = 0;
|
||||
u8 spare_buffer[100];
|
||||
enum yaffs_ecc_result ecc_result;
|
||||
|
||||
struct yaffs_packed_tags2 pt;
|
||||
|
||||
int packed_tags_size =
|
||||
dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
|
||||
void *packed_tags_ptr =
|
||||
dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"yaffs_tags_marshall_read chunk %d data %p tags %p",
|
||||
nand_chunk, data, tags);
|
||||
|
||||
if (dev->param.inband_tags) {
|
||||
if (!data) {
|
||||
local_data = 1;
|
||||
data = yaffs_get_temp_buffer(dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->param.inband_tags || (data && !tags))
|
||||
dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
||||
data, dev->param.total_bytes_per_chunk,
|
||||
NULL, 0,
|
||||
&ecc_result); //TODO check retval
|
||||
else if (tags)
|
||||
dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
||||
data, dev->param.total_bytes_per_chunk,
|
||||
spare_buffer, packed_tags_size,
|
||||
&ecc_result); //TODO check retval
|
||||
else
|
||||
BUG();
|
||||
|
||||
|
||||
if (dev->param.inband_tags) {
|
||||
if (tags) {
|
||||
struct yaffs_packed_tags2_tags_only *pt2tp;
|
||||
pt2tp =
|
||||
(struct yaffs_packed_tags2_tags_only *)
|
||||
&data[dev->data_bytes_per_chunk];
|
||||
yaffs_unpack_tags2_tags_only(tags, pt2tp);
|
||||
}
|
||||
} else if (tags) {
|
||||
memcpy(packed_tags_ptr, spare_buffer, packed_tags_size);
|
||||
yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
|
||||
}
|
||||
|
||||
if (local_data)
|
||||
yaffs_release_temp_buffer(dev, data);
|
||||
|
||||
if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) {
|
||||
tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
||||
dev->n_ecc_unfixed++;
|
||||
}
|
||||
|
||||
if (tags && ecc_result == YAFFS_ECC_RESULT_FIXED) {
|
||||
if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR)
|
||||
tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
|
||||
dev->n_ecc_fixed++;
|
||||
}
|
||||
|
||||
if (ecc_result < YAFFS_ECC_RESULT_UNFIXED)
|
||||
return YAFFS_OK;
|
||||
else
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number)
|
||||
{
|
||||
int retval;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d",
|
||||
block_no);
|
||||
|
||||
retval = dev->drv.drv_check_bad_fn(dev, block_no);
|
||||
|
||||
if (retval== YAFFS_FAIL) {
|
||||
yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
|
||||
|
||||
*state = YAFFS_BLOCK_STATE_DEAD;
|
||||
*seq_number = 0;
|
||||
} else {
|
||||
struct yaffs_ext_tags t;
|
||||
|
||||
yaffs_tags_marshall_read(dev,
|
||||
block_no * dev->param.chunks_per_block,
|
||||
NULL, &t);
|
||||
|
||||
if (t.chunk_used) {
|
||||
*seq_number = t.seq_number;
|
||||
*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
||||
} else {
|
||||
*seq_number = 0;
|
||||
*state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"block query returns seq %d state %d",
|
||||
*seq_number, *state);
|
||||
|
||||
if (retval == 0)
|
||||
return YAFFS_OK;
|
||||
else
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no)
|
||||
{
|
||||
return dev->drv.drv_mark_bad_fn(dev, block_no);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void yaffs_tags_marshall_install(struct yaffs_dev *dev)
|
||||
{
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
if (!dev->tagger.write_chunk_tags_fn)
|
||||
dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write;
|
||||
|
||||
if (!dev->tagger.read_chunk_tags_fn)
|
||||
dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read;
|
||||
|
||||
if (!dev->tagger.query_block_fn)
|
||||
dev->tagger.query_block_fn = yaffs_tags_marshall_query_block;
|
||||
|
||||
if (!dev->tagger.mark_bad_fn)
|
||||
dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad;
|
||||
|
||||
}
|
529
flight/pios/common/libraries/yaffs2/yaffs_verify.c
Normal file
529
flight/pios/common/libraries/yaffs2/yaffs_verify.c
Normal file
@ -0,0 +1,529 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_verify.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_nand.h"
|
||||
|
||||
int yaffs_skip_verification(struct yaffs_dev *dev)
|
||||
{
|
||||
(void) dev;
|
||||
return !(yaffs_trace_mask &
|
||||
(YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL));
|
||||
}
|
||||
|
||||
static int yaffs_skip_full_verification(struct yaffs_dev *dev)
|
||||
{
|
||||
(void) dev;
|
||||
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL));
|
||||
}
|
||||
|
||||
static int yaffs_skip_nand_verification(struct yaffs_dev *dev)
|
||||
{
|
||||
(void) dev;
|
||||
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND));
|
||||
}
|
||||
|
||||
static const char * const block_state_name[] = {
|
||||
"Unknown",
|
||||
"Needs scan",
|
||||
"Scanning",
|
||||
"Empty",
|
||||
"Allocating",
|
||||
"Full",
|
||||
"Dirty",
|
||||
"Checkpoint",
|
||||
"Collecting",
|
||||
"Dead"
|
||||
};
|
||||
|
||||
void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n)
|
||||
{
|
||||
int actually_used;
|
||||
int in_use;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
/* Report illegal runtime states */
|
||||
if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Block %d has undefined state %d",
|
||||
n, bi->block_state);
|
||||
|
||||
switch (bi->block_state) {
|
||||
case YAFFS_BLOCK_STATE_UNKNOWN:
|
||||
case YAFFS_BLOCK_STATE_SCANNING:
|
||||
case YAFFS_BLOCK_STATE_NEEDS_SCAN:
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Block %d has bad run-state %s",
|
||||
n, block_state_name[bi->block_state]);
|
||||
}
|
||||
|
||||
/* Check pages in use and soft deletions are legal */
|
||||
|
||||
actually_used = bi->pages_in_use - bi->soft_del_pages;
|
||||
|
||||
if (bi->pages_in_use < 0 ||
|
||||
bi->pages_in_use > dev->param.chunks_per_block ||
|
||||
bi->soft_del_pages < 0 ||
|
||||
bi->soft_del_pages > dev->param.chunks_per_block ||
|
||||
actually_used < 0 || actually_used > dev->param.chunks_per_block)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Block %d has illegal values pages_in_used %d soft_del_pages %d",
|
||||
n, bi->pages_in_use, bi->soft_del_pages);
|
||||
|
||||
/* Check chunk bitmap legal */
|
||||
in_use = yaffs_count_chunk_bits(dev, n);
|
||||
if (in_use != bi->pages_in_use)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Block %d has inconsistent values pages_in_use %d counted chunk bits %d",
|
||||
n, bi->pages_in_use, in_use);
|
||||
}
|
||||
|
||||
void yaffs_verify_collected_blk(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi, int n)
|
||||
{
|
||||
yaffs_verify_blk(dev, bi, n);
|
||||
|
||||
/* After collection the block should be in the erased state */
|
||||
|
||||
if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING &&
|
||||
bi->block_state != YAFFS_BLOCK_STATE_EMPTY) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Block %d is in state %d after gc, should be erased",
|
||||
n, bi->block_state);
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_blocks(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
int state_count[YAFFS_NUMBER_OF_BLOCK_STATES];
|
||||
int illegal_states = 0;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
memset(state_count, 0, sizeof(state_count));
|
||||
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
|
||||
yaffs_verify_blk(dev, bi, i);
|
||||
|
||||
if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES)
|
||||
state_count[bi->block_state]++;
|
||||
else
|
||||
illegal_states++;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary");
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"%d blocks have illegal states",
|
||||
illegal_states);
|
||||
if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Too many allocating blocks");
|
||||
|
||||
for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"%s %d blocks",
|
||||
block_state_name[i], state_count[i]);
|
||||
|
||||
if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT])
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Checkpoint block count wrong dev %d count %d",
|
||||
dev->blocks_in_checkpt,
|
||||
state_count[YAFFS_BLOCK_STATE_CHECKPOINT]);
|
||||
|
||||
if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY])
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Erased block count wrong dev %d count %d",
|
||||
dev->n_erased_blocks,
|
||||
state_count[YAFFS_BLOCK_STATE_EMPTY]);
|
||||
|
||||
if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Too many collecting blocks %d (max is 1)",
|
||||
state_count[YAFFS_BLOCK_STATE_COLLECTING]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the object header. oh must be valid, but obj and tags may be NULL in
|
||||
* which case those tests will not be performed.
|
||||
*/
|
||||
void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,
|
||||
struct yaffs_ext_tags *tags, int parent_check)
|
||||
{
|
||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
if (!(tags && obj && oh)) {
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Verifying object header tags %p obj %p oh %p",
|
||||
tags, obj, oh);
|
||||
return;
|
||||
}
|
||||
|
||||
if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN ||
|
||||
oh->type > YAFFS_OBJECT_TYPE_MAX)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header type is illegal value 0x%x",
|
||||
tags->obj_id, oh->type);
|
||||
|
||||
if (tags->obj_id != obj->obj_id)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header mismatch obj_id %d",
|
||||
tags->obj_id, obj->obj_id);
|
||||
|
||||
/*
|
||||
* Check that the object's parent ids match if parent_check requested.
|
||||
*
|
||||
* Tests do not apply to the root object.
|
||||
*/
|
||||
|
||||
if (parent_check && tags->obj_id > 1 && !obj->parent)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header mismatch parent_id %d obj->parent is NULL",
|
||||
tags->obj_id, oh->parent_obj_id);
|
||||
|
||||
if (parent_check && obj->parent &&
|
||||
oh->parent_obj_id != (s32)obj->parent->obj_id &&
|
||||
(oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED ||
|
||||
obj->parent->obj_id != YAFFS_OBJECTID_DELETED))
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header mismatch parent_id %d parent_obj_id %d",
|
||||
tags->obj_id, oh->parent_obj_id,
|
||||
obj->parent->obj_id);
|
||||
|
||||
if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header name is NULL",
|
||||
obj->obj_id);
|
||||
|
||||
if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header name is 0xff",
|
||||
obj->obj_id);
|
||||
}
|
||||
|
||||
void yaffs_verify_file(struct yaffs_obj *obj)
|
||||
{
|
||||
u32 x;
|
||||
int required_depth;
|
||||
//int actual_depth;
|
||||
int last_chunk;
|
||||
u32 offset_in_chunk;
|
||||
u32 the_chunk;
|
||||
|
||||
u32 i;
|
||||
struct yaffs_dev *dev;
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_tnode *tn;
|
||||
u32 obj_id;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
if (yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
dev = obj->my_dev;
|
||||
obj_id = obj->obj_id;
|
||||
|
||||
|
||||
/* Check file size is consistent with tnode depth */
|
||||
yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size,
|
||||
&last_chunk, &offset_in_chunk);
|
||||
last_chunk++;
|
||||
x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS;
|
||||
required_depth = 0;
|
||||
while (x > 0) {
|
||||
x >>= YAFFS_TNODES_INTERNAL_BITS;
|
||||
required_depth++;
|
||||
}
|
||||
|
||||
// actual_depth = obj->variant.file_variant.top_level; // unused
|
||||
|
||||
/* Check that the chunks in the tnode tree are all correct.
|
||||
* We do this by scanning through the tnode tree and
|
||||
* checking the tags for every chunk match.
|
||||
*/
|
||||
|
||||
if (yaffs_skip_nand_verification(dev))
|
||||
return;
|
||||
|
||||
for (i = 1; i <= (u32)last_chunk; i++) {
|
||||
tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i);
|
||||
|
||||
if (!tn)
|
||||
continue;
|
||||
|
||||
the_chunk = yaffs_get_group_base(dev, tn, i);
|
||||
if (the_chunk > 0) {
|
||||
yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL,
|
||||
&tags);
|
||||
if (tags.obj_id != obj_id || tags.chunk_id != i)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)",
|
||||
obj_id, i, the_chunk,
|
||||
tags.obj_id, tags.chunk_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_link(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
/* Verify sane equivalent object */
|
||||
}
|
||||
|
||||
void yaffs_verify_symlink(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
/* Verify symlink string */
|
||||
}
|
||||
|
||||
void yaffs_verify_special(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
}
|
||||
|
||||
void yaffs_verify_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
u32 chunk_min;
|
||||
u32 chunk_max;
|
||||
u32 chunk_id_ok;
|
||||
u32 chunk_in_range;
|
||||
u32 chunk_wrongly_deleted;
|
||||
u32 chunk_valid;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
if (obj->being_created)
|
||||
return;
|
||||
|
||||
dev = obj->my_dev;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
/* Check sane object header chunk */
|
||||
|
||||
chunk_min = dev->internal_start_block * dev->param.chunks_per_block;
|
||||
chunk_max =
|
||||
(dev->internal_end_block + 1) * dev->param.chunks_per_block - 1;
|
||||
|
||||
chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min &&
|
||||
((unsigned)(obj->hdr_chunk)) <= chunk_max);
|
||||
chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0);
|
||||
chunk_valid = chunk_in_range &&
|
||||
yaffs_check_chunk_bit(dev,
|
||||
obj->hdr_chunk / dev->param.chunks_per_block,
|
||||
obj->hdr_chunk % dev->param.chunks_per_block);
|
||||
chunk_wrongly_deleted = chunk_in_range && !chunk_valid;
|
||||
|
||||
if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted))
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d has chunk_id %d %s %s",
|
||||
obj->obj_id, obj->hdr_chunk,
|
||||
chunk_id_ok ? "" : ",out of range",
|
||||
chunk_wrongly_deleted ? ",marked as deleted" : "");
|
||||
|
||||
if (chunk_valid && !yaffs_skip_nand_verification(dev)) {
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_obj_hdr *oh;
|
||||
u8 *buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
oh = (struct yaffs_obj_hdr *)buffer;
|
||||
|
||||
yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags);
|
||||
|
||||
yaffs_verify_oh(obj, oh, &tags, 1);
|
||||
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
}
|
||||
|
||||
/* Verify it has a parent */
|
||||
if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) {
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d has parent pointer %p which does not look like an object",
|
||||
obj->obj_id, obj->parent);
|
||||
}
|
||||
|
||||
/* Verify parent is a directory */
|
||||
if (obj->parent &&
|
||||
obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d's parent is not a directory (type %d)",
|
||||
obj->obj_id, obj->parent->variant_type);
|
||||
}
|
||||
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
yaffs_verify_file(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
yaffs_verify_symlink(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
yaffs_verify_dir(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
yaffs_verify_link(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
yaffs_verify_special(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
default:
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d has illegaltype %d",
|
||||
obj->obj_id, obj->variant_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_objects(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
int i;
|
||||
struct list_head *lh;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
/* Iterate through the objects in each hash entry */
|
||||
|
||||
for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
|
||||
list_for_each(lh, &dev->obj_bucket[i].list) {
|
||||
obj = list_entry(lh, struct yaffs_obj, hash_link);
|
||||
yaffs_verify_obj(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_obj_in_dir(struct yaffs_obj *obj)
|
||||
{
|
||||
struct list_head *lh;
|
||||
struct yaffs_obj *list_obj;
|
||||
int count = 0;
|
||||
|
||||
if (!obj) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify");
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
if (yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
if (!obj->parent) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent");
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory");
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Iterate through the objects in each hash entry */
|
||||
|
||||
list_for_each(lh, &obj->parent->variant.dir_variant.children) {
|
||||
list_obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
yaffs_verify_obj(list_obj);
|
||||
if (obj == list_obj)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count != 1) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Object in directory %d times",
|
||||
count);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_dir(struct yaffs_obj *directory)
|
||||
{
|
||||
struct list_head *lh;
|
||||
struct yaffs_obj *list_obj;
|
||||
|
||||
if (!directory) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
if (yaffs_skip_full_verification(directory->my_dev))
|
||||
return;
|
||||
|
||||
if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Directory has wrong type: %d",
|
||||
directory->variant_type);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Iterate through the objects in each hash entry */
|
||||
|
||||
list_for_each(lh, &directory->variant.dir_variant.children) {
|
||||
list_obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
if (list_obj->parent != directory) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Object in directory list has wrong parent %p",
|
||||
list_obj->parent);
|
||||
BUG();
|
||||
}
|
||||
yaffs_verify_obj_in_dir(list_obj);
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffs_free_verification_failures;
|
||||
|
||||
void yaffs_verify_free_chunks(struct yaffs_dev *dev)
|
||||
{
|
||||
int counted;
|
||||
int difference;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
counted = yaffs_count_free_chunks(dev);
|
||||
|
||||
difference = dev->n_free_chunks - counted;
|
||||
|
||||
if (difference) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Freechunks verification failure %d %d %d",
|
||||
dev->n_free_chunks, counted, difference);
|
||||
yaffs_free_verification_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_verify_file_sane(struct yaffs_obj *in)
|
||||
{
|
||||
(void) in;
|
||||
return YAFFS_OK;
|
||||
}
|
428
flight/pios/common/libraries/yaffs2/yaffs_yaffs1.c
Normal file
428
flight/pios/common/libraries/yaffs2/yaffs_yaffs1.c
Normal file
@ -0,0 +1,428 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_yaffs1.h"
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_attribs.h"
|
||||
|
||||
int yaffs1_scan(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_ext_tags tags;
|
||||
int blk;
|
||||
//int result;
|
||||
int chunk;
|
||||
int c;
|
||||
int deleted;
|
||||
enum yaffs_block_state state;
|
||||
LIST_HEAD(hard_list);
|
||||
struct yaffs_block_info *bi;
|
||||
u32 seq_number;
|
||||
struct yaffs_obj_hdr *oh;
|
||||
struct yaffs_obj *in;
|
||||
struct yaffs_obj *parent;
|
||||
int alloc_failed = 0;
|
||||
struct yaffs_shadow_fixer *shadow_fixers = NULL;
|
||||
u8 *chunk_data;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"yaffs1_scan starts intstartblk %d intendblk %d...",
|
||||
dev->internal_start_block, dev->internal_end_block);
|
||||
|
||||
chunk_data = yaffs_get_temp_buffer(dev);
|
||||
|
||||
dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
|
||||
|
||||
/* Scan all the blocks to determine their state */
|
||||
bi = dev->block_info;
|
||||
for (blk = dev->internal_start_block; blk <= dev->internal_end_block;
|
||||
blk++) {
|
||||
yaffs_clear_chunk_bits(dev, blk);
|
||||
bi->pages_in_use = 0;
|
||||
bi->soft_del_pages = 0;
|
||||
|
||||
yaffs_query_init_block_state(dev, blk, &state, &seq_number);
|
||||
|
||||
bi->block_state = state;
|
||||
bi->seq_number = seq_number;
|
||||
|
||||
if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)
|
||||
bi->block_state = state = YAFFS_BLOCK_STATE_DEAD;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,
|
||||
"Block scanning block %d state %d seq %d",
|
||||
blk, state, seq_number);
|
||||
|
||||
if (state == YAFFS_BLOCK_STATE_DEAD) {
|
||||
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
|
||||
"block %d is bad", blk);
|
||||
} else if (state == YAFFS_BLOCK_STATE_EMPTY) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");
|
||||
dev->n_erased_blocks++;
|
||||
dev->n_free_chunks += dev->param.chunks_per_block;
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
|
||||
/* For each block.... */
|
||||
for (blk = dev->internal_start_block;
|
||||
!alloc_failed && blk <= dev->internal_end_block; blk++) {
|
||||
|
||||
cond_resched();
|
||||
|
||||
bi = yaffs_get_block_info(dev, blk);
|
||||
state = bi->block_state;
|
||||
|
||||
deleted = 0;
|
||||
|
||||
/* For each chunk in each block that needs scanning.... */
|
||||
for (c = 0;
|
||||
!alloc_failed && c < dev->param.chunks_per_block &&
|
||||
state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) {
|
||||
/* Read the tags and decide what to do */
|
||||
chunk = blk * dev->param.chunks_per_block + c;
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs1_scan: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
/* Let's have a good look at this chunk... */
|
||||
|
||||
if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED ||
|
||||
tags.is_deleted) {
|
||||
/* YAFFS1 only...
|
||||
* A deleted chunk
|
||||
*/
|
||||
deleted++;
|
||||
dev->n_free_chunks++;
|
||||
} else if (!tags.chunk_used) {
|
||||
/* An unassigned chunk in the block
|
||||
* This means that either the block is empty or
|
||||
* this is the one being allocated from
|
||||
*/
|
||||
|
||||
if (c == 0) {
|
||||
/* We're looking at the first chunk in
|
||||
*the block so the block is unused */
|
||||
state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
dev->n_erased_blocks++;
|
||||
} else {
|
||||
/* this is the block being allocated */
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
" Allocating from %d %d",
|
||||
blk, c);
|
||||
state = YAFFS_BLOCK_STATE_ALLOCATING;
|
||||
dev->alloc_block = blk;
|
||||
dev->alloc_page = c;
|
||||
dev->alloc_block_finder = blk;
|
||||
|
||||
}
|
||||
|
||||
dev->n_free_chunks +=
|
||||
(dev->param.chunks_per_block - c);
|
||||
} else if (tags.chunk_id > 0) {
|
||||
/* chunk_id > 0 so it is a data chunk... */
|
||||
unsigned int endpos;
|
||||
|
||||
yaffs_set_chunk_bit(dev, blk, c);
|
||||
bi->pages_in_use++;
|
||||
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id,
|
||||
YAFFS_OBJECT_TYPE_FILE);
|
||||
/* PutChunkIntoFile checks for a clash
|
||||
* (two data chunks with the same chunk_id).
|
||||
*/
|
||||
|
||||
if (!in)
|
||||
alloc_failed = 1;
|
||||
|
||||
if (in) {
|
||||
if (!yaffs_put_chunk_in_file
|
||||
(in, tags.chunk_id, chunk, 1))
|
||||
alloc_failed = 1;
|
||||
}
|
||||
|
||||
endpos =
|
||||
(tags.chunk_id - 1) *
|
||||
dev->data_bytes_per_chunk +
|
||||
tags.n_bytes;
|
||||
if (in &&
|
||||
in->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_FILE &&
|
||||
in->variant.file_variant.scanned_size <
|
||||
(s32)endpos) {
|
||||
in->variant.file_variant.scanned_size =
|
||||
endpos;
|
||||
if (!dev->param.use_header_file_size) {
|
||||
in->variant.
|
||||
file_variant.file_size =
|
||||
in->variant.
|
||||
file_variant.scanned_size;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
/* chunk_id == 0, so it is an ObjectHeader.
|
||||
* Make the object
|
||||
*/
|
||||
yaffs_set_chunk_bit(dev, blk, c);
|
||||
bi->pages_in_use++;
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, chunk, chunk_data, NULL))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs1_scan: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
oh = (struct yaffs_obj_hdr *)chunk_data;
|
||||
|
||||
in = yaffs_find_by_number(dev, tags.obj_id);
|
||||
if (in && in->variant_type != oh->type) {
|
||||
/* This should not happen, but somehow
|
||||
* Wev'e ended up with an obj_id that
|
||||
* has been reused but not yet deleted,
|
||||
* and worse still it has changed type.
|
||||
* Delete the old object.
|
||||
*/
|
||||
|
||||
yaffs_del_obj(in);
|
||||
in = NULL;
|
||||
}
|
||||
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id,
|
||||
oh->type);
|
||||
|
||||
if (!in)
|
||||
alloc_failed = 1;
|
||||
|
||||
if (in && oh->shadows_obj > 0) {
|
||||
|
||||
struct yaffs_shadow_fixer *fixer;
|
||||
fixer =
|
||||
kmalloc(sizeof
|
||||
(struct yaffs_shadow_fixer),
|
||||
GFP_NOFS);
|
||||
if (fixer) {
|
||||
fixer->next = shadow_fixers;
|
||||
shadow_fixers = fixer;
|
||||
fixer->obj_id = tags.obj_id;
|
||||
fixer->shadowed_id =
|
||||
oh->shadows_obj;
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
" Shadow fixer: %d shadows %d",
|
||||
fixer->obj_id,
|
||||
fixer->shadowed_id);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (in && in->valid) {
|
||||
/* We have already filled this one.
|
||||
* We have a duplicate and need to
|
||||
* resolve it. */
|
||||
|
||||
unsigned existing_serial = in->serial;
|
||||
unsigned new_serial =
|
||||
tags.serial_number;
|
||||
|
||||
if (((existing_serial + 1) & 3) ==
|
||||
new_serial) {
|
||||
/* Use new one - destroy the
|
||||
* exisiting one */
|
||||
yaffs_chunk_del(dev,
|
||||
in->hdr_chunk,
|
||||
1, __LINE__);
|
||||
in->valid = 0;
|
||||
} else {
|
||||
/* Use existing - destroy
|
||||
* this one. */
|
||||
yaffs_chunk_del(dev, chunk, 1,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
if (in && !in->valid &&
|
||||
(tags.obj_id == YAFFS_OBJECTID_ROOT ||
|
||||
tags.obj_id ==
|
||||
YAFFS_OBJECTID_LOSTNFOUND)) {
|
||||
/* We only load some info, don't fiddle
|
||||
* with directory structure */
|
||||
in->valid = 1;
|
||||
in->variant_type = oh->type;
|
||||
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
in->hdr_chunk = chunk;
|
||||
in->serial = tags.serial_number;
|
||||
|
||||
} else if (in && !in->valid) {
|
||||
/* we need to load this info */
|
||||
|
||||
in->valid = 1;
|
||||
in->variant_type = oh->type;
|
||||
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
in->hdr_chunk = chunk;
|
||||
in->serial = tags.serial_number;
|
||||
|
||||
yaffs_set_obj_name_from_oh(in, oh);
|
||||
in->dirty = 0;
|
||||
|
||||
/* directory stuff...
|
||||
* hook up to parent
|
||||
*/
|
||||
|
||||
parent =
|
||||
yaffs_find_or_create_by_number
|
||||
(dev, oh->parent_obj_id,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
if (!parent)
|
||||
alloc_failed = 1;
|
||||
if (parent && parent->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_UNKNOWN) {
|
||||
/* Set up as a directory */
|
||||
parent->variant_type =
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY;
|
||||
INIT_LIST_HEAD(&parent->
|
||||
variant.dir_variant.
|
||||
children);
|
||||
} else if (!parent ||
|
||||
parent->variant_type !=
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
/* Hoosterman, a problem....
|
||||
* We're trying to use a
|
||||
* non-directory as a directory
|
||||
*/
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
|
||||
);
|
||||
parent = dev->lost_n_found;
|
||||
}
|
||||
|
||||
yaffs_add_obj_to_dir(parent, in);
|
||||
|
||||
switch (in->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* Todo got a problem */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
if (dev->param.
|
||||
use_header_file_size)
|
||||
in->variant.
|
||||
file_variant.file_size
|
||||
= yaffs_oh_to_size(oh);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
in->variant.
|
||||
hardlink_variant.equiv_id =
|
||||
oh->equiv_id;
|
||||
list_add(&in->hard_links,
|
||||
&hard_list);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
in->variant.symlink_variant.
|
||||
alias =
|
||||
yaffs_clone_str(oh->alias);
|
||||
if (!in->variant.
|
||||
symlink_variant.alias)
|
||||
alloc_failed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {
|
||||
/* If we got this far while scanning,
|
||||
* then the block is fully allocated. */
|
||||
state = YAFFS_BLOCK_STATE_FULL;
|
||||
}
|
||||
|
||||
if (state == YAFFS_BLOCK_STATE_ALLOCATING) {
|
||||
/* If the block was partially allocated then
|
||||
* treat it as fully allocated. */
|
||||
state = YAFFS_BLOCK_STATE_FULL;
|
||||
dev->alloc_block = -1;
|
||||
}
|
||||
|
||||
bi->block_state = state;
|
||||
|
||||
/* Now let's see if it was dirty */
|
||||
if (bi->pages_in_use == 0 &&
|
||||
!bi->has_shrink_hdr &&
|
||||
bi->block_state == YAFFS_BLOCK_STATE_FULL)
|
||||
yaffs_block_became_dirty(dev, blk);
|
||||
}
|
||||
|
||||
/* Ok, we've done all the scanning.
|
||||
* Fix up the hard link chains.
|
||||
* We should now have scanned all the objects, now it's time to add
|
||||
* these hardlinks.
|
||||
*/
|
||||
|
||||
yaffs_link_fixup(dev, &hard_list);
|
||||
|
||||
/*
|
||||
* Fix up any shadowed objects.
|
||||
* There should not be more than one of these.
|
||||
*/
|
||||
{
|
||||
struct yaffs_shadow_fixer *fixer;
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
while (shadow_fixers) {
|
||||
fixer = shadow_fixers;
|
||||
shadow_fixers = fixer->next;
|
||||
/* Complete the rename transaction by deleting the
|
||||
* shadowed object then setting the object header
|
||||
to unshadowed.
|
||||
*/
|
||||
obj = yaffs_find_by_number(dev, fixer->shadowed_id);
|
||||
if (obj)
|
||||
yaffs_del_obj(obj);
|
||||
|
||||
obj = yaffs_find_by_number(dev, fixer->obj_id);
|
||||
|
||||
if (obj)
|
||||
yaffs_update_oh(obj, NULL, 1, 0, 0, NULL);
|
||||
|
||||
kfree(fixer);
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_release_temp_buffer(dev, chunk_data);
|
||||
|
||||
if (alloc_failed)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends");
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
1546
flight/pios/common/libraries/yaffs2/yaffs_yaffs2.c
Normal file
1546
flight/pios/common/libraries/yaffs2/yaffs_yaffs2.c
Normal file
@ -0,0 +1,1546 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_yaffs2.h"
|
||||
#include "yaffs_checkptrw.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_verify.h"
|
||||
#include "yaffs_attribs.h"
|
||||
#include "yaffs_summary.h"
|
||||
|
||||
/*
|
||||
* Checkpoints are really no benefit on very small partitions.
|
||||
*
|
||||
* To save space on small partitions don't bother with checkpoints unless
|
||||
* the partition is at least this big.
|
||||
*/
|
||||
#define YAFFS_CHECKPOINT_MIN_BLOCKS 60
|
||||
#define YAFFS_SMALL_HOLE_THRESHOLD 4
|
||||
|
||||
/*
|
||||
* Oldest Dirty Sequence Number handling.
|
||||
*/
|
||||
|
||||
/* yaffs_calc_oldest_dirty_seq()
|
||||
* yaffs2_find_oldest_dirty_seq()
|
||||
* Calculate the oldest dirty sequence number if we don't know it.
|
||||
*/
|
||||
void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
unsigned seq;
|
||||
unsigned block_no = 0;
|
||||
struct yaffs_block_info *b;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
/* Find the oldest dirty sequence number. */
|
||||
seq = dev->seq_number + 1;
|
||||
b = dev->block_info;
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
if (b->block_state == YAFFS_BLOCK_STATE_FULL &&
|
||||
(b->pages_in_use - b->soft_del_pages) <
|
||||
dev->param.chunks_per_block &&
|
||||
b->seq_number < seq) {
|
||||
seq = b->seq_number;
|
||||
block_no = i;
|
||||
}
|
||||
b++;
|
||||
}
|
||||
|
||||
if (block_no) {
|
||||
dev->oldest_dirty_seq = seq;
|
||||
dev->oldest_dirty_block = block_no;
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev)
|
||||
{
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
if (!dev->oldest_dirty_seq)
|
||||
yaffs_calc_oldest_dirty_seq(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_clear_oldest_dirty_seq()
|
||||
* Called when a block is erased or marked bad. (ie. when its seq_number
|
||||
* becomes invalid). If the value matches the oldest then we clear
|
||||
* dev->oldest_dirty_seq to force its recomputation.
|
||||
*/
|
||||
void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi)
|
||||
{
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
if (!bi || bi->seq_number == dev->oldest_dirty_seq) {
|
||||
dev->oldest_dirty_seq = 0;
|
||||
dev->oldest_dirty_block = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs2_update_oldest_dirty_seq()
|
||||
* Update the oldest dirty sequence number whenever we dirty a block.
|
||||
* Only do this if the oldest_dirty_seq is actually being tracked.
|
||||
*/
|
||||
void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,
|
||||
struct yaffs_block_info *bi)
|
||||
{
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
if (dev->oldest_dirty_seq) {
|
||||
if (dev->oldest_dirty_seq > bi->seq_number) {
|
||||
dev->oldest_dirty_seq = bi->seq_number;
|
||||
dev->oldest_dirty_block = block_no;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi)
|
||||
{
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return 1; /* disqualification only applies to yaffs2. */
|
||||
|
||||
if (!bi->has_shrink_hdr)
|
||||
return 1; /* can gc */
|
||||
|
||||
yaffs2_find_oldest_dirty_seq(dev);
|
||||
|
||||
/* Can't do gc of this block if there are any blocks older than this
|
||||
* one that have discarded pages.
|
||||
*/
|
||||
return (bi->seq_number <= dev->oldest_dirty_seq);
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs2_find_refresh_block()
|
||||
* periodically finds the oldest full block by sequence number for refreshing.
|
||||
* Only for yaffs2.
|
||||
*/
|
||||
u32 yaffs2_find_refresh_block(struct yaffs_dev *dev)
|
||||
{
|
||||
u32 b;
|
||||
u32 oldest = 0;
|
||||
u32 oldest_seq = 0;
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return oldest;
|
||||
|
||||
/*
|
||||
* If refresh period < 10 then refreshing is disabled.
|
||||
*/
|
||||
if (dev->param.refresh_period < 10)
|
||||
return oldest;
|
||||
|
||||
/*
|
||||
* Fix broken values.
|
||||
*/
|
||||
if (dev->refresh_skip > dev->param.refresh_period)
|
||||
dev->refresh_skip = dev->param.refresh_period;
|
||||
|
||||
if (dev->refresh_skip > 0)
|
||||
return oldest;
|
||||
|
||||
/*
|
||||
* Refresh skip is now zero.
|
||||
* We'll do a refresh this time around....
|
||||
* Update the refresh skip and find the oldest block.
|
||||
*/
|
||||
dev->refresh_skip = dev->param.refresh_period;
|
||||
dev->refresh_count++;
|
||||
bi = dev->block_info;
|
||||
for (b = dev->internal_start_block; b <= (u32)dev->internal_end_block; b++) {
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_FULL) {
|
||||
|
||||
if (oldest < 1 || bi->seq_number < oldest_seq) {
|
||||
oldest = b;
|
||||
oldest_seq = bi->seq_number;
|
||||
}
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
|
||||
if (oldest > 0) {
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"GC refresh count %d selected block %d with seq_number %d",
|
||||
dev->refresh_count, oldest, oldest_seq);
|
||||
}
|
||||
|
||||
return oldest;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_required(struct yaffs_dev *dev)
|
||||
{
|
||||
int nblocks;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return 0;
|
||||
|
||||
nblocks = dev->internal_end_block - dev->internal_start_block + 1;
|
||||
|
||||
return !dev->param.skip_checkpt_wr &&
|
||||
!dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS);
|
||||
}
|
||||
|
||||
int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev)
|
||||
{
|
||||
int retval;
|
||||
int n_bytes = 0;
|
||||
int n_blocks;
|
||||
int dev_blocks;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return 0;
|
||||
|
||||
if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) {
|
||||
/* Not a valid value so recalculate */
|
||||
dev_blocks = dev->param.end_block - dev->param.start_block + 1;
|
||||
n_bytes += sizeof(struct yaffs_checkpt_validity);
|
||||
n_bytes += sizeof(struct yaffs_checkpt_dev);
|
||||
n_bytes += dev_blocks * sizeof(struct yaffs_block_info);
|
||||
n_bytes += dev_blocks * dev->chunk_bit_stride;
|
||||
n_bytes +=
|
||||
(sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) *
|
||||
dev->n_obj;
|
||||
n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes;
|
||||
n_bytes += sizeof(struct yaffs_checkpt_validity);
|
||||
n_bytes += sizeof(u32); /* checksum */
|
||||
|
||||
/* Round up and add 2 blocks to allow for some bad blocks,
|
||||
* so add 3 */
|
||||
|
||||
n_blocks =
|
||||
(n_bytes /
|
||||
(dev->data_bytes_per_chunk *
|
||||
dev->param.chunks_per_block)) + 3;
|
||||
|
||||
dev->checkpoint_blocks_required = n_blocks;
|
||||
}
|
||||
|
||||
retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt;
|
||||
if (retval < 0)
|
||||
retval = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*--------------------- Checkpointing --------------------*/
|
||||
|
||||
static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head)
|
||||
{
|
||||
struct yaffs_checkpt_validity cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.struct_type = sizeof(cp);
|
||||
cp.magic = YAFFS_MAGIC;
|
||||
cp.version = YAFFS_CHECKPOINT_VERSION;
|
||||
cp.head = (head) ? 1 : 0;
|
||||
|
||||
return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head)
|
||||
{
|
||||
struct yaffs_checkpt_validity cp;
|
||||
int ok;
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == (int)sizeof(cp));
|
||||
|
||||
if (ok)
|
||||
ok = (cp.struct_type == sizeof(cp)) &&
|
||||
(cp.magic == YAFFS_MAGIC) &&
|
||||
(cp.version == YAFFS_CHECKPOINT_VERSION) &&
|
||||
(cp.head == ((head) ? 1 : 0));
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp,
|
||||
struct yaffs_dev *dev)
|
||||
{
|
||||
cp->n_erased_blocks = dev->n_erased_blocks;
|
||||
cp->alloc_block = dev->alloc_block;
|
||||
cp->alloc_page = dev->alloc_page;
|
||||
cp->n_free_chunks = dev->n_free_chunks;
|
||||
|
||||
cp->n_deleted_files = dev->n_deleted_files;
|
||||
cp->n_unlinked_files = dev->n_unlinked_files;
|
||||
cp->n_bg_deletions = dev->n_bg_deletions;
|
||||
cp->seq_number = dev->seq_number;
|
||||
|
||||
}
|
||||
|
||||
static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev,
|
||||
struct yaffs_checkpt_dev *cp)
|
||||
{
|
||||
dev->n_erased_blocks = cp->n_erased_blocks;
|
||||
dev->alloc_block = cp->alloc_block;
|
||||
dev->alloc_page = cp->alloc_page;
|
||||
dev->n_free_chunks = cp->n_free_chunks;
|
||||
|
||||
dev->n_deleted_files = cp->n_deleted_files;
|
||||
dev->n_unlinked_files = cp->n_unlinked_files;
|
||||
dev->n_bg_deletions = cp->n_bg_deletions;
|
||||
dev->seq_number = cp->seq_number;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_checkpt_dev cp;
|
||||
u32 n_bytes;
|
||||
u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
|
||||
int ok;
|
||||
|
||||
/* Write device runtime values */
|
||||
yaffs2_dev_to_checkpt_dev(&cp, dev);
|
||||
cp.struct_type = sizeof(cp);
|
||||
|
||||
ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
/* Write block info */
|
||||
n_bytes = n_blocks * sizeof(struct yaffs_block_info);
|
||||
ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == (int)n_bytes);
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
/* Write chunk bits */
|
||||
n_bytes = n_blocks * dev->chunk_bit_stride;
|
||||
ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == (int)n_bytes);
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_checkpt_dev cp;
|
||||
u32 n_bytes;
|
||||
u32 n_blocks =
|
||||
(dev->internal_end_block - dev->internal_start_block + 1);
|
||||
int ok;
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == (int)sizeof(cp));
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
if (cp.struct_type != sizeof(cp))
|
||||
return 0;
|
||||
|
||||
yaffs_checkpt_dev_to_dev(dev, &cp);
|
||||
|
||||
n_bytes = n_blocks * sizeof(struct yaffs_block_info);
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == (int)n_bytes);
|
||||
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
n_bytes = n_blocks * dev->chunk_bit_stride;
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == (int)n_bytes);
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp,
|
||||
struct yaffs_obj *obj)
|
||||
{
|
||||
cp->obj_id = obj->obj_id;
|
||||
cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0;
|
||||
cp->hdr_chunk = obj->hdr_chunk;
|
||||
cp->variant_type = obj->variant_type;
|
||||
cp->deleted = obj->deleted;
|
||||
cp->soft_del = obj->soft_del;
|
||||
cp->unlinked = obj->unlinked;
|
||||
cp->fake = obj->fake;
|
||||
cp->rename_allowed = obj->rename_allowed;
|
||||
cp->unlink_allowed = obj->unlink_allowed;
|
||||
cp->serial = obj->serial;
|
||||
cp->n_data_chunks = obj->n_data_chunks;
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
cp->size_or_equiv_obj = obj->variant.file_variant.file_size;
|
||||
else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
||||
cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id;
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj,
|
||||
struct yaffs_checkpt_obj *cp)
|
||||
{
|
||||
struct yaffs_obj *parent;
|
||||
|
||||
if (obj->variant_type != cp->variant_type) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Checkpoint read object %d type %d chunk %d does not match existing object type %d",
|
||||
cp->obj_id, cp->variant_type, cp->hdr_chunk,
|
||||
obj->variant_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
obj->obj_id = cp->obj_id;
|
||||
|
||||
if (cp->parent_id)
|
||||
parent = yaffs_find_or_create_by_number(obj->my_dev,
|
||||
cp->parent_id,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
else
|
||||
parent = NULL;
|
||||
|
||||
if (parent) {
|
||||
if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory",
|
||||
cp->obj_id, cp->parent_id,
|
||||
cp->variant_type, cp->hdr_chunk,
|
||||
parent->variant_type);
|
||||
return 0;
|
||||
}
|
||||
yaffs_add_obj_to_dir(parent, obj);
|
||||
}
|
||||
|
||||
obj->hdr_chunk = cp->hdr_chunk;
|
||||
obj->variant_type = cp->variant_type;
|
||||
obj->deleted = cp->deleted;
|
||||
obj->soft_del = cp->soft_del;
|
||||
obj->unlinked = cp->unlinked;
|
||||
obj->fake = cp->fake;
|
||||
obj->rename_allowed = cp->rename_allowed;
|
||||
obj->unlink_allowed = cp->unlink_allowed;
|
||||
obj->serial = cp->serial;
|
||||
obj->n_data_chunks = cp->n_data_chunks;
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
obj->variant.file_variant.file_size = cp->size_or_equiv_obj;
|
||||
else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
||||
obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj;
|
||||
|
||||
if (obj->hdr_chunk > 0)
|
||||
obj->lazy_loaded = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in,
|
||||
struct yaffs_tnode *tn, u32 level,
|
||||
int chunk_offset)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
int ok = 1;
|
||||
u32 base_offset;
|
||||
|
||||
if (!tn)
|
||||
return 1;
|
||||
|
||||
if (level > 0) {
|
||||
for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) {
|
||||
if (!tn->internal[i])
|
||||
continue;
|
||||
ok = yaffs2_checkpt_tnode_worker(in,
|
||||
tn->internal[i],
|
||||
level - 1,
|
||||
(chunk_offset <<
|
||||
YAFFS_TNODES_INTERNAL_BITS) + i);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* Level 0 tnode */
|
||||
base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS;
|
||||
ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) ==
|
||||
sizeof(base_offset));
|
||||
if (ok)
|
||||
ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) ==
|
||||
(int)dev->tnode_size);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj)
|
||||
{
|
||||
u32 end_marker = ~0;
|
||||
int ok = 1;
|
||||
|
||||
if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
|
||||
return ok;
|
||||
|
||||
ok = yaffs2_checkpt_tnode_worker(obj,
|
||||
obj->variant.file_variant.top,
|
||||
obj->variant.file_variant.
|
||||
top_level, 0);
|
||||
if (ok)
|
||||
ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker,
|
||||
sizeof(end_marker)) == sizeof(end_marker));
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj)
|
||||
{
|
||||
u32 base_chunk;
|
||||
int ok = 1;
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant;
|
||||
struct yaffs_tnode *tn;
|
||||
int nread = 0;
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) ==
|
||||
(int)sizeof(base_chunk));
|
||||
|
||||
while (ok && (~base_chunk)) {
|
||||
nread++;
|
||||
/* Read level 0 tnode */
|
||||
|
||||
tn = yaffs_get_tnode(dev);
|
||||
if (tn)
|
||||
ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) ==
|
||||
(int)dev->tnode_size);
|
||||
else
|
||||
ok = 0;
|
||||
|
||||
if (tn && ok)
|
||||
ok = yaffs_add_find_tnode_0(dev,
|
||||
file_stuct_ptr,
|
||||
base_chunk, tn) ? 1 : 0;
|
||||
|
||||
if (ok)
|
||||
ok = (yaffs2_checkpt_rd
|
||||
(dev, &base_chunk,
|
||||
sizeof(base_chunk)) == (int)sizeof(base_chunk));
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"Checkpoint read tnodes %d records, last %d. ok %d",
|
||||
nread, base_chunk, ok);
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_checkpt_obj cp;
|
||||
int i;
|
||||
int ok = 1;
|
||||
struct list_head *lh;
|
||||
|
||||
/* Iterate through the objects in each hash entry,
|
||||
* dumping them to the checkpointing stream.
|
||||
*/
|
||||
|
||||
for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) {
|
||||
list_for_each(lh, &dev->obj_bucket[i].list) {
|
||||
obj = list_entry(lh, struct yaffs_obj, hash_link);
|
||||
if (!obj->defered_free) {
|
||||
yaffs2_obj_checkpt_obj(&cp, obj);
|
||||
cp.struct_type = sizeof(cp);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"Checkpoint write object %d parent %d type %d chunk %d obj addr %p",
|
||||
cp.obj_id, cp.parent_id,
|
||||
cp.variant_type, cp.hdr_chunk, obj);
|
||||
|
||||
ok = (yaffs2_checkpt_wr(dev, &cp,
|
||||
sizeof(cp)) == sizeof(cp));
|
||||
|
||||
if (ok &&
|
||||
obj->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_FILE)
|
||||
ok = yaffs2_wr_checkpt_tnodes(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump end of list */
|
||||
memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj));
|
||||
cp.struct_type = sizeof(cp);
|
||||
|
||||
if (ok)
|
||||
ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_checkpt_obj cp;
|
||||
int ok = 1;
|
||||
int done = 0;
|
||||
LIST_HEAD(hard_list);
|
||||
|
||||
|
||||
while (ok && !done) {
|
||||
ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == (int)sizeof(cp));
|
||||
if (cp.struct_type != sizeof(cp)) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"struct size %d instead of %d ok %d",
|
||||
cp.struct_type, (int)sizeof(cp), ok);
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"Checkpoint read object %d parent %d type %d chunk %d ",
|
||||
cp.obj_id, cp.parent_id, cp.variant_type,
|
||||
cp.hdr_chunk);
|
||||
|
||||
if (ok && cp.obj_id == (u32)~0) {
|
||||
done = 1;
|
||||
} else if (ok) {
|
||||
obj =
|
||||
yaffs_find_or_create_by_number(dev, cp.obj_id,
|
||||
cp.variant_type);
|
||||
if (obj) {
|
||||
ok = yaffs2_checkpt_obj_to_obj(obj, &cp);
|
||||
if (!ok)
|
||||
break;
|
||||
if (obj->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_FILE) {
|
||||
ok = yaffs2_rd_checkpt_tnodes(obj);
|
||||
} else if (obj->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_HARDLINK) {
|
||||
list_add(&obj->hard_links, &hard_list);
|
||||
}
|
||||
} else {
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
yaffs_link_fixup(dev, &hard_list);
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev)
|
||||
{
|
||||
u32 checkpt_sum;
|
||||
int ok;
|
||||
|
||||
yaffs2_get_checkpt_sum(dev, &checkpt_sum);
|
||||
|
||||
ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) ==
|
||||
sizeof(checkpt_sum));
|
||||
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev)
|
||||
{
|
||||
u32 checkpt_sum0;
|
||||
u32 checkpt_sum1;
|
||||
int ok;
|
||||
|
||||
yaffs2_get_checkpt_sum(dev, &checkpt_sum0);
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) ==
|
||||
(int)sizeof(checkpt_sum1));
|
||||
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
if (checkpt_sum0 != checkpt_sum1)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev)
|
||||
{
|
||||
int ok = 1;
|
||||
|
||||
if (!yaffs2_checkpt_required(dev)) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"skipping checkpoint write");
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
ok = yaffs2_checkpt_open(dev, 1);
|
||||
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"write checkpoint validity");
|
||||
ok = yaffs2_wr_checkpt_validity_marker(dev, 1);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"write checkpoint device");
|
||||
ok = yaffs2_wr_checkpt_dev(dev);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"write checkpoint objects");
|
||||
ok = yaffs2_wr_checkpt_objs(dev);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"write checkpoint validity");
|
||||
ok = yaffs2_wr_checkpt_validity_marker(dev, 0);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
ok = yaffs2_wr_checkpt_sum(dev);
|
||||
|
||||
if (!yaffs_checkpt_close(dev))
|
||||
ok = 0;
|
||||
|
||||
if (ok)
|
||||
dev->is_checkpointed = 1;
|
||||
else
|
||||
dev->is_checkpointed = 0;
|
||||
|
||||
return dev->is_checkpointed;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev)
|
||||
{
|
||||
int ok = 1;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
ok = 0;
|
||||
|
||||
if (ok && dev->param.skip_checkpt_rd) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"skipping checkpoint read");
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
ok = yaffs2_checkpt_open(dev, 0); /* open for read */
|
||||
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint validity");
|
||||
ok = yaffs2_rd_checkpt_validity_marker(dev, 1);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint device");
|
||||
ok = yaffs2_rd_checkpt_dev(dev);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint objects");
|
||||
ok = yaffs2_rd_checkpt_objs(dev);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint validity");
|
||||
ok = yaffs2_rd_checkpt_validity_marker(dev, 0);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
ok = yaffs2_rd_checkpt_sum(dev);
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint checksum %d", ok);
|
||||
}
|
||||
|
||||
if (!yaffs_checkpt_close(dev))
|
||||
ok = 0;
|
||||
|
||||
if (ok)
|
||||
dev->is_checkpointed = 1;
|
||||
else
|
||||
dev->is_checkpointed = 0;
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
void yaffs2_checkpt_invalidate(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) {
|
||||
dev->is_checkpointed = 0;
|
||||
yaffs2_checkpt_invalidate_stream(dev);
|
||||
}
|
||||
if (dev->param.sb_dirty_fn)
|
||||
dev->param.sb_dirty_fn(dev);
|
||||
}
|
||||
|
||||
int yaffs_checkpoint_save(struct yaffs_dev *dev)
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"save entry: is_checkpointed %d",
|
||||
dev->is_checkpointed);
|
||||
|
||||
yaffs_verify_objects(dev);
|
||||
yaffs_verify_blocks(dev);
|
||||
yaffs_verify_free_chunks(dev);
|
||||
|
||||
if (!dev->is_checkpointed) {
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
yaffs2_wr_checkpt_data(dev);
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT,
|
||||
"save exit: is_checkpointed %d",
|
||||
dev->is_checkpointed);
|
||||
|
||||
return dev->is_checkpointed;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_restore(struct yaffs_dev *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"restore entry: is_checkpointed %d",
|
||||
dev->is_checkpointed);
|
||||
|
||||
retval = yaffs2_rd_checkpt_data(dev);
|
||||
|
||||
if (dev->is_checkpointed) {
|
||||
yaffs_verify_objects(dev);
|
||||
yaffs_verify_blocks(dev);
|
||||
yaffs_verify_free_chunks(dev);
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"restore exit: is_checkpointed %d",
|
||||
dev->is_checkpointed);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int yaffs2_handle_hole(struct yaffs_obj *obj, Y_LOFF_T new_size)
|
||||
{
|
||||
/* if new_size > old_file_size.
|
||||
* We're going to be writing a hole.
|
||||
* If the hole is small then write zeros otherwise write a start
|
||||
* of hole marker.
|
||||
*/
|
||||
Y_LOFF_T old_file_size;
|
||||
Y_LOFF_T increase;
|
||||
int small_hole;
|
||||
int result = YAFFS_OK;
|
||||
struct yaffs_dev *dev = NULL;
|
||||
u8 *local_buffer = NULL;
|
||||
int small_increase_ok = 0;
|
||||
|
||||
if (!obj)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
dev = obj->my_dev;
|
||||
|
||||
/* Bail out if not yaffs2 mode */
|
||||
if (!dev->param.is_yaffs2)
|
||||
return YAFFS_OK;
|
||||
|
||||
old_file_size = obj->variant.file_variant.file_size;
|
||||
|
||||
if (new_size <= old_file_size)
|
||||
return YAFFS_OK;
|
||||
|
||||
increase = new_size - old_file_size;
|
||||
|
||||
if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk &&
|
||||
yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1))
|
||||
small_hole = 1;
|
||||
else
|
||||
small_hole = 0;
|
||||
|
||||
if (small_hole)
|
||||
local_buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
if (local_buffer) {
|
||||
/* fill hole with zero bytes */
|
||||
Y_LOFF_T pos = old_file_size;
|
||||
int this_write;
|
||||
int written;
|
||||
memset(local_buffer, 0, dev->data_bytes_per_chunk);
|
||||
small_increase_ok = 1;
|
||||
|
||||
while (increase > 0 && small_increase_ok) {
|
||||
this_write = increase;
|
||||
if (this_write > dev->data_bytes_per_chunk)
|
||||
this_write = dev->data_bytes_per_chunk;
|
||||
written =
|
||||
yaffs_do_file_wr(obj, local_buffer, pos, this_write,
|
||||
0);
|
||||
if (written == this_write) {
|
||||
pos += this_write;
|
||||
increase -= this_write;
|
||||
} else {
|
||||
small_increase_ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_release_temp_buffer(dev, local_buffer);
|
||||
|
||||
/* If out of space then reverse any chunks we've added */
|
||||
if (!small_increase_ok)
|
||||
yaffs_resize_file_down(obj, old_file_size);
|
||||
}
|
||||
|
||||
if (!small_increase_ok &&
|
||||
obj->parent &&
|
||||
obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED &&
|
||||
obj->parent->obj_id != YAFFS_OBJECTID_DELETED) {
|
||||
/* Write a hole start header with the old file size */
|
||||
yaffs_update_oh(obj, NULL, 0, 1, 0, NULL);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct yaffs_block_index {
|
||||
int seq;
|
||||
int block;
|
||||
};
|
||||
|
||||
static int yaffs2_ybicmp(const void *a, const void *b)
|
||||
{
|
||||
int aseq = ((struct yaffs_block_index *)a)->seq;
|
||||
int bseq = ((struct yaffs_block_index *)b)->seq;
|
||||
int ablock = ((struct yaffs_block_index *)a)->block;
|
||||
int bblock = ((struct yaffs_block_index *)b)->block;
|
||||
|
||||
if (aseq == bseq)
|
||||
return ablock - bblock;
|
||||
|
||||
return aseq - bseq;
|
||||
}
|
||||
|
||||
static inline int yaffs2_scan_chunk(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi,
|
||||
int blk, int chunk_in_block,
|
||||
int *found_chunks,
|
||||
u8 *chunk_data,
|
||||
struct list_head *hard_list,
|
||||
int summary_available)
|
||||
{
|
||||
struct yaffs_obj_hdr *oh;
|
||||
struct yaffs_obj *in;
|
||||
struct yaffs_obj *parent;
|
||||
int equiv_id;
|
||||
Y_LOFF_T file_size;
|
||||
int is_shrink;
|
||||
int is_unlinked;
|
||||
struct yaffs_ext_tags tags;
|
||||
//int result;
|
||||
int alloc_failed = 0;
|
||||
int chunk = blk * dev->param.chunks_per_block + chunk_in_block;
|
||||
struct yaffs_file_var *file_var;
|
||||
struct yaffs_hardlink_var *hl_var;
|
||||
struct yaffs_symlink_var *sl_var;
|
||||
|
||||
if (summary_available) {
|
||||
if (!yaffs_summary_fetch(dev, &tags, chunk_in_block))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs2_scan_chunk: unhandled error from yaffs_summary_fetch");
|
||||
}
|
||||
tags.seq_number = bi->seq_number;
|
||||
}
|
||||
|
||||
if (!summary_available || tags.obj_id == 0) {
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs2_scan_chunk: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
dev->tags_used++;
|
||||
} else {
|
||||
dev->summary_used++;
|
||||
}
|
||||
|
||||
/* Let's have a good look at this chunk... */
|
||||
|
||||
if (!tags.chunk_used) {
|
||||
/* An unassigned chunk in the block.
|
||||
* If there are used chunks after this one, then
|
||||
* it is a chunk that was skipped due to failing
|
||||
* the erased check. Just skip it so that it can
|
||||
* be deleted.
|
||||
* But, more typically, We get here when this is
|
||||
* an unallocated chunk and his means that
|
||||
* either the block is empty or this is the one
|
||||
* being allocated from
|
||||
*/
|
||||
|
||||
if (*found_chunks) {
|
||||
/* This is a chunk that was skipped due
|
||||
* to failing the erased check */
|
||||
} else if (chunk_in_block == 0) {
|
||||
/* We're looking at the first chunk in
|
||||
* the block so the block is unused */
|
||||
bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
dev->n_erased_blocks++;
|
||||
} else {
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {
|
||||
if (dev->seq_number == bi->seq_number) {
|
||||
/* Allocating from this block*/
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
" Allocating from %d %d",
|
||||
blk, chunk_in_block);
|
||||
|
||||
bi->block_state =
|
||||
YAFFS_BLOCK_STATE_ALLOCATING;
|
||||
dev->alloc_block = blk;
|
||||
dev->alloc_page = chunk_in_block;
|
||||
dev->alloc_block_finder = blk;
|
||||
} else {
|
||||
/* This is a partially written block
|
||||
* that is not the current
|
||||
* allocation block.
|
||||
*/
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Partially written block %d detected. gc will fix this.",
|
||||
blk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev->n_free_chunks++;
|
||||
|
||||
} else if (tags.ecc_result ==
|
||||
YAFFS_ECC_RESULT_UNFIXED) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
" Unfixed ECC in chunk(%d:%d), chunk ignored",
|
||||
blk, chunk_in_block);
|
||||
dev->n_free_chunks++;
|
||||
} else if (tags.obj_id > YAFFS_MAX_OBJECT_ID ||
|
||||
tags.chunk_id > YAFFS_MAX_CHUNK_ID ||
|
||||
tags.obj_id == YAFFS_OBJECTID_SUMMARY ||
|
||||
(tags.chunk_id > 0 &&
|
||||
tags.n_bytes > (u32)dev->data_bytes_per_chunk) ||
|
||||
tags.seq_number != bi->seq_number) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored",
|
||||
blk, chunk_in_block, tags.obj_id,
|
||||
tags.chunk_id, tags.n_bytes);
|
||||
dev->n_free_chunks++;
|
||||
} else if (tags.chunk_id > 0) {
|
||||
/* chunk_id > 0 so it is a data chunk... */
|
||||
Y_LOFF_T endpos;
|
||||
Y_LOFF_T chunk_base = (tags.chunk_id - 1) *
|
||||
dev->data_bytes_per_chunk;
|
||||
|
||||
*found_chunks = 1;
|
||||
|
||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
||||
bi->pages_in_use++;
|
||||
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id,
|
||||
YAFFS_OBJECT_TYPE_FILE);
|
||||
if (!in)
|
||||
/* Out of memory */
|
||||
alloc_failed = 1;
|
||||
|
||||
if (in &&
|
||||
in->variant_type == YAFFS_OBJECT_TYPE_FILE &&
|
||||
chunk_base < in->variant.file_variant.shrink_size) {
|
||||
/* This has not been invalidated by
|
||||
* a resize */
|
||||
if (!yaffs_put_chunk_in_file(in, tags.chunk_id,
|
||||
chunk, -1))
|
||||
alloc_failed = 1;
|
||||
|
||||
/* File size is calculated by looking at
|
||||
* the data chunks if we have not
|
||||
* seen an object header yet.
|
||||
* Stop this practice once we find an
|
||||
* object header.
|
||||
*/
|
||||
endpos = chunk_base + tags.n_bytes;
|
||||
|
||||
if (!in->valid &&
|
||||
in->variant.file_variant.scanned_size < endpos) {
|
||||
in->variant.file_variant.
|
||||
scanned_size = endpos;
|
||||
in->variant.file_variant.
|
||||
file_size = endpos;
|
||||
}
|
||||
} else if (in) {
|
||||
/* This chunk has been invalidated by a
|
||||
* resize, or a past file deletion
|
||||
* so delete the chunk*/
|
||||
yaffs_chunk_del(dev, chunk, 1, __LINE__);
|
||||
}
|
||||
} else {
|
||||
/* chunk_id == 0, so it is an ObjectHeader.
|
||||
* Thus, we read in the object header and make
|
||||
* the object
|
||||
*/
|
||||
*found_chunks = 1;
|
||||
|
||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
||||
bi->pages_in_use++;
|
||||
|
||||
oh = NULL;
|
||||
in = NULL;
|
||||
|
||||
if (tags.extra_available) {
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id,
|
||||
tags.extra_obj_type);
|
||||
if (!in)
|
||||
alloc_failed = 1;
|
||||
}
|
||||
|
||||
if (!in ||
|
||||
(!in->valid && dev->param.disable_lazy_load) ||
|
||||
tags.extra_shadows ||
|
||||
(!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT ||
|
||||
tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) {
|
||||
|
||||
/* If we don't have valid info then we
|
||||
* need to read the chunk
|
||||
* TODO In future we can probably defer
|
||||
* reading the chunk and living with
|
||||
* invalid data until needed.
|
||||
*/
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev,
|
||||
chunk,
|
||||
chunk_data,
|
||||
NULL))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs2_scan_chunk: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
oh = (struct yaffs_obj_hdr *)chunk_data;
|
||||
|
||||
if (dev->param.inband_tags) {
|
||||
/* Fix up the header if they got
|
||||
* corrupted by inband tags */
|
||||
oh->shadows_obj =
|
||||
oh->inband_shadowed_obj_id;
|
||||
oh->is_shrink =
|
||||
oh->inband_is_shrink;
|
||||
}
|
||||
|
||||
if (!in) {
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id, oh->type);
|
||||
if (!in)
|
||||
alloc_failed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in) {
|
||||
/* TODO Hoosterman we have a problem! */
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: Could not make object for object %d at chunk %d during scan",
|
||||
tags.obj_id, chunk);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (in->valid) {
|
||||
/* We have already filled this one.
|
||||
* We have a duplicate that will be
|
||||
* discarded, but we first have to suck
|
||||
* out resize info if it is a file.
|
||||
*/
|
||||
if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) &&
|
||||
((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) ||
|
||||
(tags.extra_available &&
|
||||
tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
)) {
|
||||
Y_LOFF_T this_size = (oh) ?
|
||||
yaffs_oh_to_size(oh) :
|
||||
tags.extra_file_size;
|
||||
u32 parent_obj_id = (oh) ?
|
||||
(u32)oh->parent_obj_id :
|
||||
tags.extra_parent_id;
|
||||
|
||||
is_shrink = (oh) ?
|
||||
oh->is_shrink :
|
||||
tags.extra_is_shrink;
|
||||
|
||||
/* If it is deleted (unlinked
|
||||
* at start also means deleted)
|
||||
* we treat the file size as
|
||||
* being zeroed at this point.
|
||||
*/
|
||||
if (parent_obj_id == YAFFS_OBJECTID_DELETED ||
|
||||
parent_obj_id == YAFFS_OBJECTID_UNLINKED) {
|
||||
this_size = 0;
|
||||
is_shrink = 1;
|
||||
}
|
||||
|
||||
if (is_shrink &&
|
||||
in->variant.file_variant.shrink_size >
|
||||
this_size)
|
||||
in->variant.file_variant.shrink_size =
|
||||
this_size;
|
||||
|
||||
if (is_shrink)
|
||||
bi->has_shrink_hdr = 1;
|
||||
}
|
||||
/* Use existing - destroy this one. */
|
||||
yaffs_chunk_del(dev, chunk, 1, __LINE__);
|
||||
}
|
||||
|
||||
if (!in->valid && in->variant_type !=
|
||||
(oh ? oh->type : tags.extra_obj_type)) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan",
|
||||
oh ? oh->type : tags.extra_obj_type,
|
||||
in->variant_type, tags.obj_id,
|
||||
chunk);
|
||||
in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type);
|
||||
}
|
||||
|
||||
if (!in->valid &&
|
||||
(tags.obj_id == YAFFS_OBJECTID_ROOT ||
|
||||
tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) {
|
||||
/* We only load some info, don't fiddle
|
||||
* with directory structure */
|
||||
in->valid = 1;
|
||||
|
||||
if (oh) {
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
in->lazy_loaded = 0;
|
||||
} else {
|
||||
in->lazy_loaded = 1;
|
||||
}
|
||||
in->hdr_chunk = chunk;
|
||||
|
||||
} else if (!in->valid) {
|
||||
/* we need to load this info */
|
||||
in->valid = 1;
|
||||
in->hdr_chunk = chunk;
|
||||
if (oh) {
|
||||
in->variant_type = oh->type;
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
|
||||
if (oh->shadows_obj > 0)
|
||||
yaffs_handle_shadowed_obj(dev,
|
||||
oh->shadows_obj, 1);
|
||||
|
||||
yaffs_set_obj_name_from_oh(in, oh);
|
||||
parent = yaffs_find_or_create_by_number(dev,
|
||||
oh->parent_obj_id,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
file_size = yaffs_oh_to_size(oh);
|
||||
is_shrink = oh->is_shrink;
|
||||
equiv_id = oh->equiv_id;
|
||||
} else {
|
||||
in->variant_type = tags.extra_obj_type;
|
||||
parent = yaffs_find_or_create_by_number(dev,
|
||||
tags.extra_parent_id,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
file_size = tags.extra_file_size;
|
||||
is_shrink = tags.extra_is_shrink;
|
||||
equiv_id = tags.extra_equiv_id;
|
||||
in->lazy_loaded = 1;
|
||||
}
|
||||
in->dirty = 0;
|
||||
|
||||
if (!parent)
|
||||
alloc_failed = 1;
|
||||
|
||||
/* directory stuff...
|
||||
* hook up to parent
|
||||
*/
|
||||
|
||||
if (parent &&
|
||||
parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) {
|
||||
/* Set up as a directory */
|
||||
parent->variant_type =
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY;
|
||||
INIT_LIST_HEAD(&parent->
|
||||
variant.dir_variant.children);
|
||||
} else if (!parent ||
|
||||
parent->variant_type !=
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
/* Hoosterman, another problem....
|
||||
* Trying to use a non-directory as a directory
|
||||
*/
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
|
||||
);
|
||||
parent = dev->lost_n_found;
|
||||
}
|
||||
yaffs_add_obj_to_dir(parent, in);
|
||||
|
||||
is_unlinked = (parent == dev->del_dir) ||
|
||||
(parent == dev->unlinked_dir);
|
||||
|
||||
if (is_shrink)
|
||||
/* Mark the block */
|
||||
bi->has_shrink_hdr = 1;
|
||||
|
||||
/* Note re hardlinks.
|
||||
* Since we might scan a hardlink before its equivalent
|
||||
* object is scanned we put them all in a list.
|
||||
* After scanning is complete, we should have all the
|
||||
* objects, so we run through this list and fix up all
|
||||
* the chains.
|
||||
*/
|
||||
|
||||
switch (in->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* Todo got a problem */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
file_var = &in->variant.file_variant;
|
||||
if (file_var->scanned_size < file_size) {
|
||||
/* This covers the case where the file
|
||||
* size is greater than the data held.
|
||||
* This will happen if the file is
|
||||
* resized to be larger than its
|
||||
* current data extents.
|
||||
*/
|
||||
file_var->file_size = file_size;
|
||||
file_var->scanned_size = file_size;
|
||||
}
|
||||
|
||||
if (file_var->shrink_size > file_size)
|
||||
file_var->shrink_size = file_size;
|
||||
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
hl_var = &in->variant.hardlink_variant;
|
||||
if (!is_unlinked) {
|
||||
hl_var->equiv_id = equiv_id;
|
||||
list_add(&in->hard_links, hard_list);
|
||||
}
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
sl_var = &in->variant.symlink_variant;
|
||||
if (oh) {
|
||||
sl_var->alias =
|
||||
yaffs_clone_str(oh->alias);
|
||||
if (!sl_var->alias)
|
||||
alloc_failed = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return alloc_failed ? YAFFS_FAIL : YAFFS_OK;
|
||||
}
|
||||
|
||||
int yaffs2_scan_backwards(struct yaffs_dev *dev)
|
||||
{
|
||||
int blk;
|
||||
int block_iter;
|
||||
int start_iter;
|
||||
int end_iter;
|
||||
int n_to_scan = 0;
|
||||
enum yaffs_block_state state;
|
||||
int c;
|
||||
//int deleted;
|
||||
LIST_HEAD(hard_list);
|
||||
struct yaffs_block_info *bi;
|
||||
u32 seq_number;
|
||||
int n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
|
||||
u8 *chunk_data;
|
||||
int found_chunks;
|
||||
int alloc_failed = 0;
|
||||
struct yaffs_block_index *block_index = NULL;
|
||||
int alt_block_index = 0;
|
||||
int summary_available;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"yaffs2_scan_backwards starts intstartblk %d intendblk %d...",
|
||||
dev->internal_start_block, dev->internal_end_block);
|
||||
|
||||
dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
|
||||
|
||||
block_index =
|
||||
kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS);
|
||||
|
||||
if (!block_index) {
|
||||
block_index =
|
||||
vmalloc(n_blocks * sizeof(struct yaffs_block_index));
|
||||
alt_block_index = 1;
|
||||
}
|
||||
|
||||
if (!block_index) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"yaffs2_scan_backwards() could not allocate block index!"
|
||||
);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
dev->blocks_in_checkpt = 0;
|
||||
|
||||
chunk_data = yaffs_get_temp_buffer(dev);
|
||||
|
||||
/* Scan all the blocks to determine their state */
|
||||
bi = dev->block_info;
|
||||
for (blk = dev->internal_start_block; blk <= dev->internal_end_block;
|
||||
blk++) {
|
||||
yaffs_clear_chunk_bits(dev, blk);
|
||||
bi->pages_in_use = 0;
|
||||
bi->soft_del_pages = 0;
|
||||
|
||||
yaffs_query_init_block_state(dev, blk, &state, &seq_number);
|
||||
|
||||
bi->block_state = state;
|
||||
bi->seq_number = seq_number;
|
||||
|
||||
if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA)
|
||||
bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
|
||||
if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)
|
||||
bi->block_state = YAFFS_BLOCK_STATE_DEAD;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,
|
||||
"Block scanning block %d state %d seq %d",
|
||||
blk, bi->block_state, seq_number);
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
|
||||
dev->blocks_in_checkpt++;
|
||||
|
||||
} else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) {
|
||||
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
|
||||
"block %d is bad", blk);
|
||||
} else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");
|
||||
dev->n_erased_blocks++;
|
||||
dev->n_free_chunks += dev->param.chunks_per_block;
|
||||
} else if (bi->block_state ==
|
||||
YAFFS_BLOCK_STATE_NEEDS_SCAN) {
|
||||
/* Determine the highest sequence number */
|
||||
if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
|
||||
seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) {
|
||||
block_index[n_to_scan].seq = seq_number;
|
||||
block_index[n_to_scan].block = blk;
|
||||
n_to_scan++;
|
||||
if (seq_number >= dev->seq_number)
|
||||
dev->seq_number = seq_number;
|
||||
} else {
|
||||
/* TODO: Nasty sequence number! */
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Block scanning block %d has bad sequence number %d",
|
||||
blk, seq_number);
|
||||
}
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan);
|
||||
|
||||
cond_resched();
|
||||
|
||||
/* Sort the blocks by sequence number */
|
||||
sort(block_index, n_to_scan, sizeof(struct yaffs_block_index),
|
||||
yaffs2_ybicmp, NULL);
|
||||
|
||||
cond_resched();
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN, "...done");
|
||||
|
||||
/* Now scan the blocks looking at the data. */
|
||||
start_iter = 0;
|
||||
end_iter = n_to_scan - 1;
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan);
|
||||
|
||||
/* For each block.... backwards */
|
||||
for (block_iter = end_iter;
|
||||
!alloc_failed && block_iter >= start_iter;
|
||||
block_iter--) {
|
||||
/* Cooperative multitasking! This loop can run for so
|
||||
long that watchdog timers expire. */
|
||||
cond_resched();
|
||||
|
||||
/* get the block to scan in the correct order */
|
||||
blk = block_index[block_iter].block;
|
||||
bi = yaffs_get_block_info(dev, blk);
|
||||
//deleted = 0;
|
||||
|
||||
summary_available = yaffs_summary_read(dev, dev->sum_tags, blk);
|
||||
|
||||
/* For each chunk in each block that needs scanning.... */
|
||||
found_chunks = 0;
|
||||
if (summary_available)
|
||||
c = dev->chunks_per_summary - 1;
|
||||
else
|
||||
c = dev->param.chunks_per_block - 1;
|
||||
|
||||
for (/* c is already initialised */;
|
||||
!alloc_failed && c >= 0 &&
|
||||
(bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING);
|
||||
c--) {
|
||||
/* Scan backwards...
|
||||
* Read the tags and decide what to do
|
||||
*/
|
||||
if (yaffs2_scan_chunk(dev, bi, blk, c,
|
||||
&found_chunks, chunk_data,
|
||||
&hard_list, summary_available) ==
|
||||
YAFFS_FAIL)
|
||||
alloc_failed = 1;
|
||||
}
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {
|
||||
/* If we got this far while scanning, then the block
|
||||
* is fully allocated. */
|
||||
bi->block_state = YAFFS_BLOCK_STATE_FULL;
|
||||
}
|
||||
|
||||
/* Now let's see if it was dirty */
|
||||
if (bi->pages_in_use == 0 &&
|
||||
!bi->has_shrink_hdr &&
|
||||
bi->block_state == YAFFS_BLOCK_STATE_FULL) {
|
||||
yaffs_block_became_dirty(dev, blk);
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_skip_rest_of_block(dev);
|
||||
|
||||
if (alt_block_index)
|
||||
vfree(block_index);
|
||||
else
|
||||
kfree(block_index);
|
||||
|
||||
/* Ok, we've done all the scanning.
|
||||
* Fix up the hard link chains.
|
||||
* We have scanned all the objects, now it's time to add these
|
||||
* hardlinks.
|
||||
*/
|
||||
yaffs_link_fixup(dev, &hard_list);
|
||||
|
||||
yaffs_release_temp_buffer(dev, chunk_data);
|
||||
|
||||
if (alloc_failed)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends");
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
3867
flight/pios/common/libraries/yaffs2/yaffsfs.c
Normal file
3867
flight/pios/common/libraries/yaffs2/yaffsfs.c
Normal file
@ -0,0 +1,3867 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffsfs.h"
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffscfg.h"
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
#include "string.h"
|
||||
|
||||
#define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#define ROOT_DIR(dev) (((dev) && (dev)->is_mounted) ? (dev)->root_dir : NULL)
|
||||
|
||||
/* YAFFSFS_RW_SIZE must be a power of 2 */
|
||||
#define YAFFSFS_RW_SHIFT (13)
|
||||
#define YAFFSFS_RW_SIZE (1<<YAFFSFS_RW_SHIFT)
|
||||
|
||||
/* Some forward references */
|
||||
static struct yaffs_obj *yaffsfs_FindObject(struct yaffs_obj *relativeDirectory,
|
||||
const YCHAR *path,
|
||||
int symDepth, int getEquiv,
|
||||
struct yaffs_obj **dirOut,
|
||||
int *notDir, int *loop);
|
||||
|
||||
static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj);
|
||||
static yaffs_DIR *yaffsfs_opendir_reldir_no_lock(
|
||||
struct yaffs_obj *reldir, const YCHAR *dirname);
|
||||
static int yaffsfs_closedir_no_lock(yaffs_DIR *dirent);
|
||||
|
||||
unsigned int yaffs_wr_attempts;
|
||||
|
||||
/*
|
||||
* Handle management.
|
||||
* There are open inodes in struct yaffsfs_Inode.
|
||||
* There are open file descriptors in yaffsfs_FileDes.
|
||||
* There are open handles in yaffsfs_FileDes.
|
||||
*
|
||||
* Things are structured this way to be like the Linux VFS model
|
||||
* so that interactions with the yaffs guts calls are similar.
|
||||
* That means more common code paths and less special code.
|
||||
* That means better testing etc.
|
||||
*
|
||||
* We have 3 layers because:
|
||||
* A handle is different than an fd because you can use dup()
|
||||
* to create a new handle that accesses the *same* fd. The two
|
||||
* handles will use the same offset (part of the fd). We only close
|
||||
* down the fd when there are no more handles accessing it.
|
||||
*
|
||||
* More than one fd can currently access one file, but each fd
|
||||
* has its own permsiions and offset.
|
||||
*/
|
||||
|
||||
struct yaffsfs_Inode {
|
||||
int count; /* Number of handles accessing this inode */
|
||||
struct yaffs_obj *iObj;
|
||||
};
|
||||
|
||||
|
||||
struct yaffsfs_DirSearchContext {
|
||||
struct yaffs_dirent de; /* directory entry */
|
||||
YCHAR name[NAME_MAX + 1]; /* name of directory being searched */
|
||||
struct yaffs_obj *dirObj; /* ptr to directory being searched */
|
||||
struct yaffs_obj *nextReturn; /* obj returned by next readddir */
|
||||
struct list_head others;
|
||||
s32 offset:20;
|
||||
u8 inUse:1;
|
||||
};
|
||||
|
||||
struct yaffsfs_FileDes {
|
||||
u8 isDir:1; /* This s a directory */
|
||||
u8 reading:1;
|
||||
u8 writing:1;
|
||||
u8 append:1;
|
||||
u8 shareRead:1;
|
||||
u8 shareWrite:1;
|
||||
s32 inodeId:12; /* Index to corresponding yaffsfs_Inode */
|
||||
s32 handleCount:10; /* Number of handles for this fd */
|
||||
union {
|
||||
Y_LOFF_T position; /* current position in file */
|
||||
yaffs_DIR *dir;
|
||||
} v;
|
||||
};
|
||||
|
||||
struct yaffsfs_Handle {
|
||||
short int fdId;
|
||||
short int useCount;
|
||||
};
|
||||
|
||||
|
||||
static struct yaffsfs_DirSearchContext yaffsfs_dsc[YAFFSFS_N_DSC];
|
||||
static struct yaffsfs_Inode yaffsfs_inode[YAFFSFS_N_HANDLES];
|
||||
static struct yaffsfs_FileDes yaffsfs_fd[YAFFSFS_N_HANDLES];
|
||||
static struct yaffsfs_Handle yaffsfs_handle[YAFFSFS_N_HANDLES];
|
||||
|
||||
static int yaffsfs_handlesInitialised;
|
||||
|
||||
unsigned yaffs_set_trace(unsigned tm)
|
||||
{
|
||||
yaffs_trace_mask = tm;
|
||||
return yaffs_trace_mask;
|
||||
}
|
||||
|
||||
unsigned yaffs_get_trace(void)
|
||||
{
|
||||
return yaffs_trace_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_InitHandle
|
||||
* Inilitalise handle management on start-up.
|
||||
*/
|
||||
|
||||
static void yaffsfs_InitHandles(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (yaffsfs_handlesInitialised)
|
||||
return;
|
||||
|
||||
yaffsfs_handlesInitialised = 1;
|
||||
|
||||
memset(yaffsfs_inode, 0, sizeof(yaffsfs_inode));
|
||||
memset(yaffsfs_fd, 0, sizeof(yaffsfs_fd));
|
||||
memset(yaffsfs_handle, 0, sizeof(yaffsfs_handle));
|
||||
memset(yaffsfs_dsc, 0, sizeof(yaffsfs_dsc));
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++)
|
||||
yaffsfs_fd[i].inodeId = -1;
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++)
|
||||
yaffsfs_handle[i].fdId = -1;
|
||||
}
|
||||
|
||||
static struct yaffsfs_Handle *yaffsfs_HandleToPointer(int h)
|
||||
{
|
||||
if (h >= 0 && h < YAFFSFS_N_HANDLES)
|
||||
return &yaffsfs_handle[h];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffsfs_FileDes *yaffsfs_HandleToFileDes(int handle)
|
||||
{
|
||||
struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle);
|
||||
|
||||
if (h && h->useCount > 0 && h->fdId >= 0 && h->fdId < YAFFSFS_N_HANDLES)
|
||||
return &yaffsfs_fd[h->fdId];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffsfs_Inode *yaffsfs_HandleToInode(int handle)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = yaffsfs_HandleToFileDes(handle);
|
||||
|
||||
if (fd && fd->handleCount > 0 &&
|
||||
fd->inodeId >= 0 && fd->inodeId < YAFFSFS_N_HANDLES)
|
||||
return &yaffsfs_inode[fd->inodeId];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffsfs_HandleToObject(int handle)
|
||||
{
|
||||
struct yaffsfs_Inode *in = yaffsfs_HandleToInode(handle);
|
||||
|
||||
if (in)
|
||||
return in->iObj;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_FindInodeIdForObject
|
||||
* Find the inode entry for an object, if it exists.
|
||||
*/
|
||||
|
||||
static int yaffsfs_FindInodeIdForObject(struct yaffs_obj *obj)
|
||||
{
|
||||
int i;
|
||||
int ret = -1;
|
||||
|
||||
if (obj)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
/* Look for it in open inode table */
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++) {
|
||||
if (yaffsfs_inode[i].iObj == obj)
|
||||
ret = i;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_GetInodeIdForObject
|
||||
* Grab an inode entry when opening a new inode.
|
||||
*/
|
||||
static int yaffsfs_GetInodeIdForObject(struct yaffs_obj *obj)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
struct yaffsfs_Inode *in = NULL;
|
||||
|
||||
if (obj)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
ret = yaffsfs_FindInodeIdForObject(obj);
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++) {
|
||||
if (!yaffsfs_inode[i].iObj)
|
||||
ret = i;
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
in = &yaffsfs_inode[ret];
|
||||
if (!in->iObj)
|
||||
in->count = 0;
|
||||
in->iObj = obj;
|
||||
in->count++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int yaffsfs_CountHandles(struct yaffs_obj *obj)
|
||||
{
|
||||
int i = yaffsfs_FindInodeIdForObject(obj);
|
||||
|
||||
if (i >= 0)
|
||||
return yaffsfs_inode[i].count;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void yaffsfs_ReleaseInode(struct yaffsfs_Inode *in)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
obj = in->iObj;
|
||||
obj->my_inode = NULL;
|
||||
in->iObj = NULL;
|
||||
|
||||
if (obj->unlinked)
|
||||
yaffs_del_obj(obj);
|
||||
}
|
||||
|
||||
static void yaffsfs_PutInode(int inodeId)
|
||||
{
|
||||
if (inodeId >= 0 && inodeId < YAFFSFS_N_HANDLES) {
|
||||
struct yaffsfs_Inode *in = &yaffsfs_inode[inodeId];
|
||||
in->count--;
|
||||
if (in->count <= 0) {
|
||||
yaffsfs_ReleaseInode(in);
|
||||
in->count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffsfs_NewHandle(struct yaffsfs_Handle **hptr)
|
||||
{
|
||||
int i;
|
||||
struct yaffsfs_Handle *h;
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
h = &yaffsfs_handle[i];
|
||||
if (h->useCount < 1) {
|
||||
memset(h, 0, sizeof(struct yaffsfs_Handle));
|
||||
h->fdId = -1;
|
||||
h->useCount = 1;
|
||||
if (hptr)
|
||||
*hptr = h;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int yaffsfs_NewHandleAndFileDes(void)
|
||||
{
|
||||
int i;
|
||||
struct yaffsfs_FileDes *fd;
|
||||
struct yaffsfs_Handle *h = NULL;
|
||||
int handle = yaffsfs_NewHandle(&h);
|
||||
|
||||
if (handle < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
fd = &yaffsfs_fd[i];
|
||||
if (fd->handleCount < 1) {
|
||||
memset(fd, 0, sizeof(struct yaffsfs_FileDes));
|
||||
fd->inodeId = -1;
|
||||
fd->handleCount = 1;
|
||||
h->fdId = i;
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump the handle because we could not get a fd */
|
||||
h->useCount = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_get_handle
|
||||
* Increase use of handle when reading/writing a file
|
||||
* Also gets the file descriptor.
|
||||
*/
|
||||
|
||||
static int yaffsfs_GetHandle(int handle)
|
||||
{
|
||||
struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle);
|
||||
|
||||
if (h && h->useCount > 0) {
|
||||
h->useCount++;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_put_handle
|
||||
* Let go of a handle when closing a file or aborting an open or
|
||||
* ending a read or write.
|
||||
*/
|
||||
|
||||
static int yaffsfs_PutFileDes(int fdId)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd;
|
||||
|
||||
if (fdId >= 0 && fdId < YAFFSFS_N_HANDLES) {
|
||||
fd = &yaffsfs_fd[fdId];
|
||||
fd->handleCount--;
|
||||
if (fd->handleCount < 1) {
|
||||
if (fd->isDir)
|
||||
yaffsfs_closedir_no_lock(fd->v.dir);
|
||||
if (fd->inodeId >= 0) {
|
||||
yaffsfs_PutInode(fd->inodeId);
|
||||
fd->inodeId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int yaffsfs_PutHandle(int handle)
|
||||
{
|
||||
struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle);
|
||||
|
||||
if (h && h->useCount > 0) {
|
||||
h->useCount--;
|
||||
if (h->useCount < 1) {
|
||||
yaffsfs_PutFileDes(h->fdId);
|
||||
h->fdId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void yaffsfs_BreakDeviceHandles(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd;
|
||||
struct yaffsfs_Handle *h;
|
||||
struct yaffs_obj *obj;
|
||||
int i;
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
h = yaffsfs_HandleToPointer(i);
|
||||
fd = yaffsfs_HandleToFileDes(i);
|
||||
obj = yaffsfs_HandleToObject(i);
|
||||
if (h && h->useCount > 0) {
|
||||
h->useCount = 0;
|
||||
h->fdId = 0;
|
||||
}
|
||||
if (fd && fd->handleCount > 0 && obj && obj->my_dev == dev) {
|
||||
fd->handleCount = 0;
|
||||
yaffsfs_PutInode(fd->inodeId);
|
||||
fd->inodeId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Stuff to handle names.
|
||||
*/
|
||||
#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
|
||||
#ifndef CONFIG_YAFFS_WINCE
|
||||
static int yaffs_toupper(YCHAR a)
|
||||
{
|
||||
if (a >= 'a' && a <= 'z')
|
||||
return (a - 'a') + 'A';
|
||||
else
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int yaffsfs_Match(YCHAR a, YCHAR b)
|
||||
{
|
||||
return (yaffs_toupper(a) == yaffs_toupper(b));
|
||||
}
|
||||
#else
|
||||
static int yaffsfs_Match(YCHAR a, YCHAR b)
|
||||
{
|
||||
/* case sensitive */
|
||||
return (a == b);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int yaffsfs_IsPathDivider(YCHAR ch)
|
||||
{
|
||||
const YCHAR *str = YAFFS_PATH_DIVIDERS;
|
||||
|
||||
while (*str) {
|
||||
if (*str == ch)
|
||||
return 1;
|
||||
str++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int yaffsfs_CheckNameLength(const YCHAR *name)
|
||||
{
|
||||
int retVal = 0;
|
||||
|
||||
int nameLength = yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH + 1);
|
||||
|
||||
if (nameLength == 0) {
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
retVal = -1;
|
||||
} else if (nameLength > YAFFS_MAX_NAME_LENGTH) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
retVal = -1;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffsfs_alt_dir_path(const YCHAR *path, YCHAR **ret_path)
|
||||
{
|
||||
YCHAR *alt_path = NULL;
|
||||
int path_length;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We don't have a definition for max path length.
|
||||
* We will use 3 * max name length instead.
|
||||
*/
|
||||
*ret_path = NULL;
|
||||
path_length = yaffs_strnlen(path, (YAFFS_MAX_NAME_LENGTH + 1) * 3 + 1);
|
||||
|
||||
/* If the last character is a path divider, then we need to
|
||||
* trim it back so that the name look-up works properly.
|
||||
* eg. /foo/new_dir/ -> /foo/newdir
|
||||
* Curveball: Need to handle multiple path dividers:
|
||||
* eg. /foof/sdfse///// -> /foo/sdfse
|
||||
*/
|
||||
if (path_length > 0 && yaffsfs_IsPathDivider(path[path_length - 1])) {
|
||||
alt_path = kmalloc(path_length + 1, 0);
|
||||
if (!alt_path)
|
||||
return -1;
|
||||
yaffs_strcpy(alt_path, path);
|
||||
for (i = path_length - 1;
|
||||
i >= 0 && yaffsfs_IsPathDivider(alt_path[i]); i--)
|
||||
alt_path[i] = (YCHAR) 0;
|
||||
}
|
||||
*ret_path = alt_path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIST_HEAD(yaffsfs_deviceList);
|
||||
|
||||
/*
|
||||
* yaffsfs_FindDevice
|
||||
* yaffsfs_FindRoot
|
||||
* Scan the configuration list to find the device
|
||||
* Curveballs: Should match paths that end in '/' too
|
||||
* Curveball2 Might have "/x/ and "/x/y". Need to return the longest match
|
||||
*/
|
||||
static struct yaffs_dev *yaffsfs_FindDevice(const YCHAR *path,
|
||||
YCHAR **restOfPath)
|
||||
{
|
||||
struct list_head *cfg;
|
||||
const YCHAR *leftOver;
|
||||
const YCHAR *p;
|
||||
struct yaffs_dev *retval = NULL;
|
||||
struct yaffs_dev *dev = NULL;
|
||||
int thisMatchLength;
|
||||
int longestMatch = -1;
|
||||
int matching;
|
||||
|
||||
/*
|
||||
* Check all configs, choose the one that:
|
||||
* 1) Actually matches a prefix (ie /a amd /abc will not match
|
||||
* 2) Matches the longest.
|
||||
*/
|
||||
list_for_each(cfg, &yaffsfs_deviceList) {
|
||||
dev = list_entry(cfg, struct yaffs_dev, dev_list);
|
||||
leftOver = path;
|
||||
p = dev->param.name;
|
||||
thisMatchLength = 0;
|
||||
matching = 1;
|
||||
|
||||
if(!p)
|
||||
continue;
|
||||
|
||||
while (matching && *p && *leftOver) {
|
||||
/* Skip over any /s */
|
||||
while (yaffsfs_IsPathDivider(*p))
|
||||
p++;
|
||||
|
||||
/* Skip over any /s */
|
||||
while (yaffsfs_IsPathDivider(*leftOver))
|
||||
leftOver++;
|
||||
|
||||
/* Now match the text part */
|
||||
while (matching &&
|
||||
*p && !yaffsfs_IsPathDivider(*p) &&
|
||||
*leftOver && !yaffsfs_IsPathDivider(*leftOver)) {
|
||||
if (yaffsfs_Match(*p, *leftOver)) {
|
||||
p++;
|
||||
leftOver++;
|
||||
thisMatchLength++;
|
||||
} else {
|
||||
matching = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip over any /s in leftOver */
|
||||
while (yaffsfs_IsPathDivider(*leftOver))
|
||||
leftOver++;
|
||||
|
||||
/*Skip over any /s in p */
|
||||
while (yaffsfs_IsPathDivider(*p))
|
||||
p++;
|
||||
|
||||
/* p should now be at the end of the string if fully matched */
|
||||
if (*p)
|
||||
matching = 0;
|
||||
|
||||
if (matching && (thisMatchLength > longestMatch)) {
|
||||
/* Matched prefix */
|
||||
*restOfPath = (YCHAR *) leftOver;
|
||||
retval = dev;
|
||||
longestMatch = thisMatchLength;
|
||||
}
|
||||
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int yaffsfs_CheckPath(const YCHAR *path)
|
||||
{
|
||||
int n = 0;
|
||||
int divs = 0;
|
||||
|
||||
while (*path && n < YAFFS_MAX_NAME_LENGTH && divs < 100) {
|
||||
if (yaffsfs_IsPathDivider(*path)) {
|
||||
n = 0;
|
||||
divs++;
|
||||
} else
|
||||
n++;
|
||||
path++;
|
||||
}
|
||||
|
||||
return (*path) ? -1 : 0;
|
||||
}
|
||||
|
||||
/* FindMountPoint only returns a dev entry if the path is a mount point */
|
||||
static struct yaffs_dev *yaffsfs_FindMountPoint(const YCHAR *path)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
YCHAR *restOfPath = NULL;
|
||||
|
||||
dev = yaffsfs_FindDevice(path, &restOfPath);
|
||||
if (dev && restOfPath && *restOfPath)
|
||||
dev = NULL;
|
||||
return dev;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffsfs_FindRoot(const YCHAR *path,
|
||||
YCHAR **restOfPath)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
dev = yaffsfs_FindDevice(path, restOfPath);
|
||||
if (dev && dev->is_mounted)
|
||||
return dev->root_dir;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffsfs_FollowLink(struct yaffs_obj *obj,
|
||||
int symDepth, int *loop)
|
||||
{
|
||||
|
||||
if (obj)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
while (obj && obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {
|
||||
YCHAR *alias = obj->variant.symlink_variant.alias;
|
||||
|
||||
if (yaffsfs_IsPathDivider(*alias))
|
||||
/*
|
||||
* Starts with a /, need to scan from root up */
|
||||
/* NB Might not work if this is called with root != NULL
|
||||
*/
|
||||
obj = yaffsfs_FindObject(NULL, alias, symDepth++,
|
||||
1, NULL, NULL, loop);
|
||||
else
|
||||
/*
|
||||
* Relative to here so use the parent of the
|
||||
* symlink as a start
|
||||
*/
|
||||
obj = yaffsfs_FindObject(obj->parent, alias, symDepth++,
|
||||
1, NULL, NULL, loop);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_FindDirectory
|
||||
* Parse a path to determine the directory and the name within the directory.
|
||||
*
|
||||
* eg. "/data/xx/ff" --> puts name="ff" and returns the directory "/data/xx"
|
||||
*/
|
||||
static struct yaffs_obj *yaffsfs_DoFindDirectory(struct yaffs_obj *startDir,
|
||||
const YCHAR *path,
|
||||
YCHAR **name, int symDepth,
|
||||
int *notDir, int *loop)
|
||||
{
|
||||
struct yaffs_obj *dir;
|
||||
YCHAR *restOfPath;
|
||||
YCHAR str[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
int i;
|
||||
|
||||
if (symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES) {
|
||||
if (loop)
|
||||
*loop = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (startDir) {
|
||||
dir = startDir;
|
||||
restOfPath = (YCHAR *) path;
|
||||
} else
|
||||
dir = yaffsfs_FindRoot(path, &restOfPath);
|
||||
|
||||
while (dir) {
|
||||
/*
|
||||
* parse off /.
|
||||
* curve ball: also throw away surplus '/'
|
||||
* eg. "/ram/x////ff" gets treated the same as "/ram/x/ff"
|
||||
*/
|
||||
while (yaffsfs_IsPathDivider(*restOfPath))
|
||||
restOfPath++; /* get rid of '/' */
|
||||
|
||||
*name = restOfPath;
|
||||
i = 0;
|
||||
|
||||
while (*restOfPath && !yaffsfs_IsPathDivider(*restOfPath)) {
|
||||
if (i < YAFFS_MAX_NAME_LENGTH) {
|
||||
str[i] = *restOfPath;
|
||||
str[i + 1] = '\0';
|
||||
i++;
|
||||
}
|
||||
restOfPath++;
|
||||
}
|
||||
|
||||
if (!*restOfPath)
|
||||
/* got to the end of the string */
|
||||
return dir;
|
||||
else {
|
||||
if (yaffs_strcmp(str, _Y(".")) == 0) {
|
||||
/* Do nothing */
|
||||
} else if (yaffs_strcmp(str, _Y("..")) == 0) {
|
||||
dir = dir->parent;
|
||||
} else {
|
||||
dir = yaffs_find_by_name(dir, str);
|
||||
|
||||
dir = yaffsfs_FollowLink(dir, symDepth, loop);
|
||||
|
||||
if (dir && dir->variant_type !=
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
if (notDir)
|
||||
*notDir = 1;
|
||||
dir = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
/* directory did not exist. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffsfs_FindDirectory(struct yaffs_obj *relDir,
|
||||
const YCHAR *path,
|
||||
YCHAR **name,
|
||||
int symDepth,
|
||||
int *notDir, int *loop)
|
||||
{
|
||||
return yaffsfs_DoFindDirectory(relDir, path, name, symDepth, notDir,
|
||||
loop);
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_FindObject turns a path for an existing object into the object
|
||||
*/
|
||||
static struct yaffs_obj *yaffsfs_FindObject(struct yaffs_obj *relDir,
|
||||
const YCHAR *path, int symDepth,
|
||||
int getEquiv,
|
||||
struct yaffs_obj **dirOut,
|
||||
int *notDir, int *loop)
|
||||
{
|
||||
struct yaffs_obj *dir;
|
||||
struct yaffs_obj *obj;
|
||||
YCHAR *name;
|
||||
|
||||
dir =
|
||||
yaffsfs_FindDirectory(relDir, path, &name, symDepth, notDir, loop);
|
||||
|
||||
if (dirOut)
|
||||
*dirOut = dir;
|
||||
|
||||
if (dir && *name)
|
||||
obj = yaffs_find_by_name(dir, name);
|
||||
else
|
||||
obj = dir;
|
||||
|
||||
if (getEquiv)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Start of yaffsfs visible functions.
|
||||
*************************************************************************/
|
||||
|
||||
int yaffs_dup(int handle)
|
||||
{
|
||||
int newHandleNumber = -1;
|
||||
struct yaffsfs_FileDes *existingFD = NULL;
|
||||
struct yaffsfs_Handle *existingHandle = NULL;
|
||||
struct yaffsfs_Handle *newHandle = NULL;
|
||||
|
||||
yaffsfs_Lock();
|
||||
existingHandle = yaffsfs_HandleToPointer(handle);
|
||||
existingFD = yaffsfs_HandleToFileDes(handle);
|
||||
if (existingFD)
|
||||
newHandleNumber = yaffsfs_NewHandle(&newHandle);
|
||||
if (newHandle) {
|
||||
newHandle->fdId = existingHandle->fdId;
|
||||
existingFD->handleCount++;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
if (!existingFD)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (!newHandle)
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
|
||||
return newHandleNumber;
|
||||
|
||||
}
|
||||
|
||||
static int yaffsfs_TooManyObjects(struct yaffs_dev *dev)
|
||||
{
|
||||
int current_objects = dev->n_obj - dev->n_deleted_files;
|
||||
|
||||
if (dev->param.max_objects && current_objects > dev->param.max_objects)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_open_sharing_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
int oflag, int mode, int sharing)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
YCHAR *name;
|
||||
int handle = -1;
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
int openDenied = 0;
|
||||
int symDepth = 0;
|
||||
int errorReported = 0;
|
||||
int rwflags = oflag & (O_RDWR | O_RDONLY | O_WRONLY);
|
||||
u8 shareRead = (sharing & YAFFS_SHARE_READ) ? 1 : 0;
|
||||
u8 shareWrite = (sharing & YAFFS_SHARE_WRITE) ? 1 : 0;
|
||||
u8 sharedReadAllowed;
|
||||
u8 sharedWriteAllowed;
|
||||
u8 alreadyReading;
|
||||
u8 alreadyWriting;
|
||||
u8 readRequested;
|
||||
u8 writeRequested;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
int is_dir = 0;
|
||||
yaffs_DIR *dsc;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0)< 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* O_EXCL only has meaning if O_CREAT is specified */
|
||||
if (!(oflag & O_CREAT))
|
||||
oflag &= ~(O_EXCL);
|
||||
|
||||
/* O_TRUNC has no meaning if (O_CREAT | O_EXCL) is specified */
|
||||
if ((oflag & O_CREAT) & (oflag & O_EXCL))
|
||||
oflag &= ~(O_TRUNC);
|
||||
|
||||
/* Todo: Are there any more flag combos to sanitise ? */
|
||||
|
||||
/* Figure out if reading or writing is requested */
|
||||
|
||||
readRequested = (rwflags == O_RDWR || rwflags == O_RDONLY) ? 1 : 0;
|
||||
writeRequested = (rwflags == O_RDWR || rwflags == O_WRONLY) ? 1 : 0;
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
handle = yaffsfs_NewHandleAndFileDes();
|
||||
|
||||
if (handle < 0) {
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
errorReported = 1;
|
||||
} else {
|
||||
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
|
||||
/* try to find the exisiting object */
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, NULL, NULL, NULL);
|
||||
|
||||
obj = yaffsfs_FollowLink(obj, symDepth++, &loop);
|
||||
|
||||
if (obj &&
|
||||
obj->variant_type != YAFFS_OBJECT_TYPE_FILE &&
|
||||
obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
obj = NULL;
|
||||
|
||||
if (obj) {
|
||||
|
||||
/* The file already exists or it might be a directory */
|
||||
is_dir = (obj->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
|
||||
/* A directory can't be opened except for read */
|
||||
if ( is_dir &&
|
||||
(writeRequested || !readRequested ||
|
||||
(oflag & ~O_RDONLY))) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-EISDIR);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
if(is_dir) {
|
||||
dsc = yaffsfs_opendir_reldir_no_lock(reldir, path);
|
||||
if (!dsc) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
errorReported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Open should fail if O_CREAT and O_EXCL are specified
|
||||
* for a file that exists.
|
||||
*/
|
||||
if (!errorReported &&
|
||||
(oflag & O_EXCL) && (oflag & O_CREAT)) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-EEXIST);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
/* Check file permissions */
|
||||
if (readRequested && !(obj->yst_mode & S_IREAD))
|
||||
openDenied = 1;
|
||||
|
||||
if (writeRequested && !(obj->yst_mode & S_IWRITE))
|
||||
openDenied = 1;
|
||||
|
||||
if (!errorReported && writeRequested &&
|
||||
obj->my_dev->read_only) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-EROFS);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
if (openDenied && !errorReported) {
|
||||
yaffsfs_SetError(-EACCES);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
/* Check sharing of an existing object. */
|
||||
if (!openDenied) {
|
||||
struct yaffsfs_FileDes *fdx;
|
||||
int i;
|
||||
|
||||
sharedReadAllowed = 1;
|
||||
sharedWriteAllowed = 1;
|
||||
alreadyReading = 0;
|
||||
alreadyWriting = 0;
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
fdx = &yaffsfs_fd[i];
|
||||
if (fdx->handleCount > 0 &&
|
||||
fdx->inodeId >= 0 &&
|
||||
yaffsfs_inode[fdx->inodeId].iObj
|
||||
== obj) {
|
||||
if (!fdx->shareRead)
|
||||
sharedReadAllowed = 0;
|
||||
if (!fdx->shareWrite)
|
||||
sharedWriteAllowed = 0;
|
||||
if (fdx->reading)
|
||||
alreadyReading = 1;
|
||||
if (fdx->writing)
|
||||
alreadyWriting = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!sharedReadAllowed && readRequested) ||
|
||||
(!shareRead && alreadyReading) ||
|
||||
(!sharedWriteAllowed && writeRequested) ||
|
||||
(!shareWrite && alreadyWriting)) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
errorReported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* If we could not open an existing object, then let's see if
|
||||
* the directory exists. If not, error.
|
||||
*/
|
||||
if (!obj && !errorReported) {
|
||||
dir = yaffsfs_FindDirectory(reldir, path, &name, 0,
|
||||
¬Dir, &loop);
|
||||
if (!dir && notDir) {
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
errorReported = 1;
|
||||
} else if (loop) {
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
errorReported = 1;
|
||||
} else if (!dir) {
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
errorReported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj && dir && !errorReported && (oflag & O_CREAT)) {
|
||||
/* Let's see if we can create this file */
|
||||
if (dir->my_dev->read_only) {
|
||||
yaffsfs_SetError(-EROFS);
|
||||
errorReported = 1;
|
||||
} else if (yaffsfs_TooManyObjects(dir->my_dev)) {
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
errorReported = 1;
|
||||
} else
|
||||
obj = yaffs_create_file(dir, name, mode, 0, 0);
|
||||
|
||||
if (!obj && !errorReported) {
|
||||
yaffsfs_SetError(-ENOSPC);
|
||||
errorReported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj && dir && !errorReported && !(oflag & O_CREAT)) {
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
if (obj && !openDenied) {
|
||||
int inodeId = yaffsfs_GetInodeIdForObject(obj);
|
||||
|
||||
if (inodeId < 0) {
|
||||
/*
|
||||
* Todo: Fix any problem if inodes run out,
|
||||
* That can't happen if the number of inode
|
||||
* items >= number of handles.
|
||||
*/
|
||||
}
|
||||
|
||||
fd->inodeId = inodeId;
|
||||
fd->reading = readRequested;
|
||||
fd->writing = writeRequested;
|
||||
fd->append = (oflag & O_APPEND) ? 1 : 0;
|
||||
fd->shareRead = shareRead;
|
||||
fd->shareWrite = shareWrite;
|
||||
fd->isDir = is_dir;
|
||||
|
||||
if(is_dir)
|
||||
fd->v.dir = dsc;
|
||||
else
|
||||
fd->v.position = 0;
|
||||
|
||||
/* Hook inode to object */
|
||||
obj->my_inode = (void *)&yaffsfs_inode[inodeId];
|
||||
|
||||
if (!is_dir && (oflag & O_TRUNC) && fd->writing)
|
||||
yaffs_resize_file(obj, 0);
|
||||
} else {
|
||||
yaffsfs_PutHandle(handle);
|
||||
if (!errorReported)
|
||||
yaffsfs_SetError(0); /* Problem */
|
||||
handle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
int yaffs_open_sharing_reldev(struct yaffs_dev *dev, const YCHAR *path, int oflag,
|
||||
int mode, int sharing)
|
||||
{
|
||||
return yaffs_open_sharing_reldir(ROOT_DIR(dev), path,
|
||||
oflag, mode, sharing);
|
||||
}
|
||||
|
||||
int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int sharing)
|
||||
{
|
||||
return yaffs_open_sharing_reldir(NULL, path, oflag, mode, sharing);
|
||||
}
|
||||
|
||||
int yaffs_open_reldir(struct yaffs_obj *reldir,const YCHAR *path, int oflag, int mode)
|
||||
{
|
||||
return yaffs_open_sharing_reldir(reldir, path, oflag, mode,
|
||||
YAFFS_SHARE_READ | YAFFS_SHARE_WRITE);
|
||||
}
|
||||
|
||||
int yaffs_open_reldev(struct yaffs_dev *dev,const YCHAR *path, int oflag, int mode)
|
||||
{
|
||||
return yaffs_open_sharing_reldir(ROOT_DIR(dev), path, oflag, mode,
|
||||
YAFFS_SHARE_READ | YAFFS_SHARE_WRITE);
|
||||
}
|
||||
|
||||
int yaffs_open(const YCHAR *path, int oflag, int mode)
|
||||
{
|
||||
return yaffs_open_reldir(NULL, path, oflag, mode);
|
||||
}
|
||||
|
||||
static int yaffs_Dofsync(int handle, int datasync)
|
||||
{
|
||||
int retVal = -1;
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!obj)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else {
|
||||
yaffs_flush_file(obj, 1, datasync, 0);
|
||||
retVal = 0;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_fsync(int handle)
|
||||
{
|
||||
return yaffs_Dofsync(handle, 0);
|
||||
}
|
||||
|
||||
int yaffs_flush(int handle)
|
||||
{
|
||||
return yaffs_fsync(handle);
|
||||
}
|
||||
|
||||
int yaffs_fdatasync(int handle)
|
||||
{
|
||||
return yaffs_Dofsync(handle, 1);
|
||||
}
|
||||
|
||||
int yaffs_close(int handle)
|
||||
{
|
||||
struct yaffsfs_Handle *h = NULL;
|
||||
struct yaffsfs_FileDes *f;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
int retVal = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
h = yaffsfs_HandleToPointer(handle);
|
||||
f = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!h || !obj || !f)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else {
|
||||
/* clean up */
|
||||
if(!f->isDir)
|
||||
yaffs_flush_file(obj, 1, 0, 1);
|
||||
yaffsfs_PutHandle(handle);
|
||||
retVal = 0;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffsfs_do_read(int handle, void *vbuf, unsigned int nbyte,
|
||||
int isPread, Y_LOFF_T offset)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
Y_LOFF_T pos = 0;
|
||||
Y_LOFF_T startPos = 0;
|
||||
Y_LOFF_T endPos = 0;
|
||||
int nRead = 0;
|
||||
int nToRead = 0;
|
||||
int totalRead = 0;
|
||||
Y_LOFF_T maxRead;
|
||||
u8 *buf = (u8 *) vbuf;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(vbuf, nbyte, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!fd || !obj) {
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
totalRead = -1;
|
||||
} else if (!fd->reading) {
|
||||
/* Not a reading handle */
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
totalRead = -1;
|
||||
}
|
||||
else if (nbyte > YAFFS_MAX_FILE_SIZE) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
totalRead = -1;
|
||||
}
|
||||
else {
|
||||
if (isPread)
|
||||
startPos = offset;
|
||||
else
|
||||
startPos = fd->v.position;
|
||||
|
||||
pos = startPos;
|
||||
|
||||
if (yaffs_get_obj_length(obj) > pos)
|
||||
maxRead = yaffs_get_obj_length(obj) - pos;
|
||||
else
|
||||
maxRead = 0;
|
||||
|
||||
if ((s32)nbyte > maxRead)
|
||||
nbyte = maxRead;
|
||||
|
||||
yaffsfs_GetHandle(handle);
|
||||
|
||||
endPos = pos + nbyte;
|
||||
if (pos < 0 || pos > YAFFS_MAX_FILE_SIZE ||
|
||||
nbyte > YAFFS_MAX_FILE_SIZE ||
|
||||
endPos < 0 ||
|
||||
endPos > YAFFS_MAX_FILE_SIZE) {
|
||||
totalRead = -1;
|
||||
nbyte = 0;
|
||||
}
|
||||
|
||||
while (nbyte > 0) {
|
||||
nToRead = YAFFSFS_RW_SIZE -
|
||||
(pos & (YAFFSFS_RW_SIZE - 1));
|
||||
if (nToRead > (s32)nbyte)
|
||||
nToRead = nbyte;
|
||||
|
||||
/* Tricky bit...
|
||||
* Need to reverify object in case the device was
|
||||
* unmounted in another thread.
|
||||
*/
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
if (!obj)
|
||||
nRead = 0;
|
||||
else
|
||||
nRead = yaffs_file_rd(obj, buf, pos, nToRead);
|
||||
|
||||
if (nRead > 0) {
|
||||
totalRead += nRead;
|
||||
pos += nRead;
|
||||
buf += nRead;
|
||||
}
|
||||
|
||||
if (nRead == nToRead)
|
||||
nbyte -= nRead;
|
||||
else
|
||||
nbyte = 0; /* no more to read */
|
||||
|
||||
if (nbyte > 0) {
|
||||
yaffsfs_Unlock();
|
||||
yaffsfs_Lock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
yaffsfs_PutHandle(handle);
|
||||
|
||||
if (!isPread) {
|
||||
if (totalRead >= 0)
|
||||
fd->v.position = startPos + totalRead;
|
||||
else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (totalRead >= 0) ? totalRead : -1;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_read(int handle, void *buf, unsigned int nbyte)
|
||||
{
|
||||
return yaffsfs_do_read(handle, buf, nbyte, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_pread(int handle, void *buf, unsigned int nbyte, Y_LOFF_T offset)
|
||||
{
|
||||
return yaffsfs_do_read(handle, buf, nbyte, 1, offset);
|
||||
}
|
||||
|
||||
static int yaffsfs_do_write(int handle, const void *vbuf, unsigned int nbyte,
|
||||
int isPwrite, Y_LOFF_T offset)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
Y_LOFF_T pos = 0;
|
||||
Y_LOFF_T startPos = 0;
|
||||
Y_LOFF_T endPos;
|
||||
int nWritten = 0;
|
||||
int totalWritten = 0;
|
||||
int write_trhrough = 0;
|
||||
int nToWrite = 0;
|
||||
const u8 *buf = (const u8 *)vbuf;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(vbuf, nbyte, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!fd || !obj) {
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
totalWritten = -1;
|
||||
} else if (!fd->writing) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
totalWritten = -1;
|
||||
} else if (obj->my_dev->read_only) {
|
||||
yaffsfs_SetError(-EROFS);
|
||||
totalWritten = -1;
|
||||
} else {
|
||||
if (fd->append)
|
||||
startPos = yaffs_get_obj_length(obj);
|
||||
else if (isPwrite)
|
||||
startPos = offset;
|
||||
else
|
||||
startPos = fd->v.position;
|
||||
|
||||
yaffsfs_GetHandle(handle);
|
||||
pos = startPos;
|
||||
endPos = pos + nbyte;
|
||||
|
||||
if (pos < 0 || pos > YAFFS_MAX_FILE_SIZE ||
|
||||
nbyte > YAFFS_MAX_FILE_SIZE ||
|
||||
endPos < 0 ||
|
||||
endPos > YAFFS_MAX_FILE_SIZE) {
|
||||
totalWritten = -1;
|
||||
nbyte = 0;
|
||||
}
|
||||
|
||||
while (nbyte > 0) {
|
||||
|
||||
nToWrite = YAFFSFS_RW_SIZE -
|
||||
(pos & (YAFFSFS_RW_SIZE - 1));
|
||||
if (nToWrite > (s32)nbyte)
|
||||
nToWrite = nbyte;
|
||||
|
||||
/* Tricky bit...
|
||||
* Need to reverify object in case the device was
|
||||
* remounted or unmounted in another thread.
|
||||
*/
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
if (!obj || obj->my_dev->read_only)
|
||||
nWritten = 0;
|
||||
else
|
||||
nWritten =
|
||||
yaffs_wr_file(obj, buf, pos, nToWrite,
|
||||
write_trhrough);
|
||||
if (nWritten > 0) {
|
||||
totalWritten += nWritten;
|
||||
pos += nWritten;
|
||||
buf += nWritten;
|
||||
}
|
||||
|
||||
if (nWritten == nToWrite)
|
||||
nbyte -= nToWrite;
|
||||
else
|
||||
nbyte = 0;
|
||||
|
||||
if (nWritten < 1 && totalWritten < 1) {
|
||||
yaffsfs_SetError(-ENOSPC);
|
||||
totalWritten = -1;
|
||||
}
|
||||
|
||||
if (nbyte > 0) {
|
||||
yaffsfs_Unlock();
|
||||
yaffsfs_Lock();
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_PutHandle(handle);
|
||||
|
||||
if (!isPwrite) {
|
||||
if (totalWritten > 0)
|
||||
fd->v.position = startPos + totalWritten;
|
||||
else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (totalWritten >= 0) ? totalWritten : -1;
|
||||
}
|
||||
|
||||
int yaffs_write(int fd, const void *buf, unsigned int nbyte)
|
||||
{
|
||||
return yaffsfs_do_write(fd, buf, nbyte, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, Y_LOFF_T offset)
|
||||
{
|
||||
return yaffsfs_do_write(fd, buf, nbyte, 1, offset);
|
||||
}
|
||||
|
||||
int yaffs_truncate_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
Y_LOFF_T new_size)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int result = YAFFS_FAIL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
|
||||
yaffsfs_SetError(-EISDIR);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (new_size < 0 || new_size > YAFFS_MAX_FILE_SIZE)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else
|
||||
result = yaffs_resize_file(obj, new_size);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (result) ? 0 : -1;
|
||||
}
|
||||
|
||||
int yaffs_truncate_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
Y_LOFF_T new_size)
|
||||
{
|
||||
return yaffs_truncate_reldir(ROOT_DIR(dev), path, new_size);
|
||||
}
|
||||
|
||||
int yaffs_truncate(const YCHAR *path, Y_LOFF_T new_size)
|
||||
{
|
||||
return yaffs_truncate_reldir(NULL, path, new_size);
|
||||
}
|
||||
|
||||
int yaffs_ftruncate(int handle, Y_LOFF_T new_size)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
int result = 0;
|
||||
|
||||
yaffsfs_Lock();
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!fd || !obj)
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (!fd->writing)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (new_size < 0 || new_size > YAFFS_MAX_FILE_SIZE)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else
|
||||
/* resize the file */
|
||||
result = yaffs_resize_file(obj, new_size);
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (result) ? 0 : -1;
|
||||
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_lseek(int handle, Y_LOFF_T offset, int whence)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
Y_LOFF_T pos = -1;
|
||||
Y_LOFF_T fSize = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!fd || !obj)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (offset > YAFFS_MAX_FILE_SIZE)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else {
|
||||
if (whence == SEEK_SET) {
|
||||
if (offset >= 0)
|
||||
pos = offset;
|
||||
} else if (whence == SEEK_CUR) {
|
||||
if ((fd->v.position + offset) >= 0)
|
||||
pos = (fd->v.position + offset);
|
||||
} else if (whence == SEEK_END) {
|
||||
fSize = yaffs_get_obj_length(obj);
|
||||
if (fSize >= 0 && (fSize + offset) >= 0)
|
||||
pos = fSize + offset;
|
||||
}
|
||||
|
||||
if (pos >= 0 && pos <= YAFFS_MAX_FILE_SIZE)
|
||||
fd->v.position = pos;
|
||||
else {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
pos = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int yaffsfs_DoUnlink_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *path, int isDirectory)
|
||||
{
|
||||
struct yaffs_obj *dir = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
YCHAR *name;
|
||||
int result = YAFFS_FAIL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 0, NULL, NULL, NULL);
|
||||
dir = yaffsfs_FindDirectory(reldir, path, &name, 0, ¬Dir, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (yaffs_strncmp(name, _Y("."), 2) == 0)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else if (!obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (!isDirectory &&
|
||||
obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
yaffsfs_SetError(-EISDIR);
|
||||
else if (isDirectory &&
|
||||
obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (isDirectory && obj == obj->my_dev->root_dir)
|
||||
yaffsfs_SetError(-EBUSY); /* Can't rmdir a root */
|
||||
else {
|
||||
result = yaffs_unlinker(dir, name);
|
||||
|
||||
if (result == YAFFS_FAIL && isDirectory)
|
||||
yaffsfs_SetError(-ENOTEMPTY);
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (result == YAFFS_FAIL) ? -1 : 0;
|
||||
}
|
||||
|
||||
int yaffs_unlink_reldir(struct yaffs_obj *reldir, const YCHAR *path)
|
||||
{
|
||||
return yaffsfs_DoUnlink_reldir(reldir, path, 0);
|
||||
}
|
||||
|
||||
int yaffs_unlink_reldev(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
return yaffsfs_DoUnlink_reldir(ROOT_DIR(dev), path, 0);
|
||||
}
|
||||
|
||||
int yaffs_unlink(const YCHAR *path)
|
||||
{
|
||||
return yaffs_unlink_reldir(NULL, path);
|
||||
}
|
||||
|
||||
static int rename_file_over_dir(struct yaffs_obj *obj, struct yaffs_obj *newobj)
|
||||
{
|
||||
if (obj && obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY &&
|
||||
newobj && newobj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rename_dir_over_file(struct yaffs_obj *obj, struct yaffs_obj *newobj)
|
||||
{
|
||||
if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY &&
|
||||
newobj && newobj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_rename_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldPath, const YCHAR *newPath)
|
||||
{
|
||||
struct yaffs_obj *olddir = NULL;
|
||||
struct yaffs_obj *newdir = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *newobj = NULL;
|
||||
YCHAR *oldname;
|
||||
YCHAR *newname;
|
||||
int result = YAFFS_FAIL;
|
||||
int rename_allowed = 1;
|
||||
int notOldDir = 0;
|
||||
int notNewDir = 0;
|
||||
int oldLoop = 0;
|
||||
int newLoop = 0;
|
||||
|
||||
YCHAR *alt_newpath = NULL;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(oldPath, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(newPath, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(oldPath) < 0 || yaffsfs_CheckPath(newPath) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_alt_dir_path(newPath, &alt_newpath) < 0) {
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
return -1;
|
||||
}
|
||||
if (alt_newpath)
|
||||
newPath = alt_newpath;
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
olddir = yaffsfs_FindDirectory(reldir, oldPath, &oldname, 0,
|
||||
¬OldDir, &oldLoop);
|
||||
newdir = yaffsfs_FindDirectory(reldir, newPath, &newname, 0,
|
||||
¬NewDir, &newLoop);
|
||||
obj = yaffsfs_FindObject(reldir, oldPath, 0, 0, NULL, NULL, NULL);
|
||||
newobj = yaffsfs_FindObject(reldir, newPath, 0, 0, NULL, NULL, NULL);
|
||||
|
||||
/* If the object being renamed is a directory and the
|
||||
* path ended with a "/" then the olddir == obj.
|
||||
* We pass through NULL for the old name to tell the lower layers
|
||||
* to use olddir as the object.
|
||||
*/
|
||||
|
||||
if (olddir == obj)
|
||||
oldname = NULL;
|
||||
|
||||
if ((!olddir && notOldDir) || (!newdir && notNewDir)) {
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
rename_allowed = 0;
|
||||
} else if (oldLoop || newLoop) {
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
rename_allowed = 0;
|
||||
} else if (olddir && oldname &&
|
||||
yaffs_strncmp(oldname, _Y("."), 2) == 0) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
rename_allowed = 0;
|
||||
} else if (!olddir || !newdir || !obj) {
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
rename_allowed = 0;
|
||||
} else if (obj->my_dev->read_only) {
|
||||
yaffsfs_SetError(-EROFS);
|
||||
rename_allowed = 0;
|
||||
} else if (rename_file_over_dir(obj, newobj)) {
|
||||
yaffsfs_SetError(-EISDIR);
|
||||
rename_allowed = 0;
|
||||
} else if (rename_dir_over_file(obj, newobj)) {
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
rename_allowed = 0;
|
||||
} else if (yaffs_is_non_empty_dir(newobj)) {
|
||||
yaffsfs_SetError(-ENOTEMPTY);
|
||||
rename_allowed = 0;
|
||||
} else if (olddir->my_dev != newdir->my_dev) {
|
||||
/* Rename must be on same device */
|
||||
yaffsfs_SetError(-EXDEV);
|
||||
rename_allowed = 0;
|
||||
} else if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
/*
|
||||
* It is a directory, check that it is not being renamed to
|
||||
* being its own decendent.
|
||||
* Do this by tracing from the new directory back to the root,
|
||||
* checking for obj
|
||||
*/
|
||||
|
||||
struct yaffs_obj *xx = newdir;
|
||||
|
||||
while (rename_allowed && xx) {
|
||||
if (xx == obj)
|
||||
rename_allowed = 0;
|
||||
xx = xx->parent;
|
||||
}
|
||||
if (!rename_allowed)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
}
|
||||
|
||||
if (rename_allowed)
|
||||
result = yaffs_rename_obj(olddir, oldname, newdir, newname);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
kfree(alt_newpath);
|
||||
|
||||
return (result == YAFFS_FAIL) ? -1 : 0;
|
||||
}
|
||||
|
||||
int yaffs_rename_reldev(struct yaffs_dev *dev, const YCHAR *oldPath,
|
||||
const YCHAR *newPath)
|
||||
{
|
||||
return yaffs_rename_reldir(ROOT_DIR(dev), oldPath, newPath);
|
||||
}
|
||||
int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath)
|
||||
{
|
||||
return yaffs_rename_reldir(NULL, oldPath, newPath);
|
||||
}
|
||||
|
||||
static int yaffsfs_DoStat(struct yaffs_obj *obj, struct yaffs_stat *buf)
|
||||
{
|
||||
int retVal = -1;
|
||||
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
if (obj && buf) {
|
||||
buf->st_dev = (int)obj->my_dev->os_context;
|
||||
buf->st_ino = obj->obj_id;
|
||||
buf->st_mode = obj->yst_mode & ~S_IFMT;
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
buf->st_mode |= S_IFDIR;
|
||||
else if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK)
|
||||
buf->st_mode |= S_IFLNK;
|
||||
else if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
buf->st_mode |= S_IFREG;
|
||||
|
||||
buf->st_nlink = yaffs_get_obj_link_count(obj);
|
||||
buf->st_uid = 0;
|
||||
buf->st_gid = 0;
|
||||
buf->st_rdev = obj->yst_rdev;
|
||||
buf->st_size = yaffs_get_obj_length(obj);
|
||||
buf->st_blksize = obj->my_dev->data_bytes_per_chunk;
|
||||
buf->st_blocks = (buf->st_size + buf->st_blksize - 1) /
|
||||
buf->st_blksize;
|
||||
#if CONFIG_YAFFS_WINCE
|
||||
buf->yst_wince_atime[0] = obj->win_atime[0];
|
||||
buf->yst_wince_atime[1] = obj->win_atime[1];
|
||||
buf->yst_wince_ctime[0] = obj->win_ctime[0];
|
||||
buf->yst_wince_ctime[1] = obj->win_ctime[1];
|
||||
buf->yst_wince_mtime[0] = obj->win_mtime[0];
|
||||
buf->yst_wince_mtime[1] = obj->win_mtime[1];
|
||||
#else
|
||||
buf->yst_atime = obj->yst_atime;
|
||||
buf->yst_ctime = obj->yst_ctime;
|
||||
buf->yst_mtime = obj->yst_mtime;
|
||||
#endif
|
||||
retVal = 0;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffsfs_DoStatOrLStat_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
struct yaffs_stat *buf, int doLStat)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(buf, sizeof(*buf), 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (!doLStat && obj)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else
|
||||
retVal = yaffsfs_DoStat(obj, buf);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_stat_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffsfs_DoStatOrLStat_reldir(reldir, path, buf, 0);
|
||||
}
|
||||
|
||||
int yaffs_lstat_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffsfs_DoStatOrLStat_reldir(reldir, path, buf, 1);
|
||||
}
|
||||
|
||||
int yaffs_stat_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffsfs_DoStatOrLStat_reldir(ROOT_DIR(dev), path, buf, 0);
|
||||
}
|
||||
|
||||
int yaffs_lstat_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffsfs_DoStatOrLStat_reldir(ROOT_DIR(dev), path, buf, 1);
|
||||
}
|
||||
|
||||
int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffs_stat_reldir(NULL, path, buf);
|
||||
}
|
||||
|
||||
int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffs_lstat_reldir(NULL, path, buf);
|
||||
}
|
||||
|
||||
int yaffs_fstat(int fd, struct yaffs_stat *buf)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(buf, sizeof(*buf), 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj)
|
||||
retVal = yaffsfs_DoStat(obj, buf);
|
||||
else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffsfs_DoUtime(struct yaffs_obj *obj,
|
||||
const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
int retVal = -1;
|
||||
struct yaffs_utimbuf local;
|
||||
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
if (obj && obj->my_dev->read_only) {
|
||||
yaffsfs_SetError(-EROFS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if !CONFIG_YAFFS_WINCE
|
||||
if (!buf) {
|
||||
local.actime = Y_CURRENT_TIME;
|
||||
local.modtime = local.actime;
|
||||
buf = &local;
|
||||
}
|
||||
|
||||
if (obj) {
|
||||
int result;
|
||||
|
||||
obj->yst_atime = buf->actime;
|
||||
obj->yst_mtime = buf->modtime;
|
||||
obj->dirty = 1;
|
||||
result = yaffs_flush_file(obj, 0, 0, 0);
|
||||
retVal = result == YAFFS_OK ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_utime_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (!path) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else
|
||||
retVal = yaffsfs_DoUtime(obj, buf);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_utime_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
return yaffs_utime_reldir(ROOT_DIR(dev), path, buf);
|
||||
}
|
||||
|
||||
int yaffs_utime(const YCHAR *path, const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
return yaffs_utime_reldir(NULL, path, buf);
|
||||
}
|
||||
|
||||
int yaffs_futime(int fd, const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj)
|
||||
retVal = yaffsfs_DoUtime(obj, buf);
|
||||
else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_YAFFS_WINCE
|
||||
/* xattrib functions */
|
||||
|
||||
static int yaffs_do_setxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, const void *data, int size,
|
||||
int flags, int follow)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_obj *dir;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(name, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (follow)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else {
|
||||
retVal = yaffs_set_xattrib(obj, name, data, size, flags);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_setxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_do_setxattr_reldir(reldir, path, name, data, size, flags, 1);
|
||||
}
|
||||
|
||||
int yaffs_setxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_setxattr_reldir(ROOT_DIR(dev), path, name, data, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_setxattr(const YCHAR *path, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_setxattr_reldir(NULL, path, name, data, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_lsetxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_do_setxattr_reldir(reldir, path, name, data, size, flags, 0);
|
||||
}
|
||||
|
||||
int yaffs_lsetxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_lsetxattr_reldir(ROOT_DIR(dev), path, name, data, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_lsetxattr(const YCHAR *path, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_lsetxattr_reldir(NULL, path, name, data, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_fsetxattr(int fd, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(name, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (!obj)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else {
|
||||
retVal = yaffs_set_xattrib(obj, name, data, size, flags);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffs_do_getxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size, int follow)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_obj *dir;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(name, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (follow)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else {
|
||||
retVal = yaffs_get_xattrib(obj, name, data, size);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_getxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_do_getxattr_reldir(reldir, path, name, data, size, 1);
|
||||
}
|
||||
|
||||
int yaffs_getxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_getxattr_reldir(ROOT_DIR(dev), path, name, data, size);
|
||||
}
|
||||
|
||||
int yaffs_getxattr(const YCHAR *path, const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_getxattr_reldir(NULL, path, name, data, size);
|
||||
}
|
||||
|
||||
int yaffs_lgetxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_do_getxattr_reldir(reldir, path, name, data, size, 0);
|
||||
}
|
||||
|
||||
int yaffs_lgetxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_lgetxattr_reldir(ROOT_DIR(dev), path, name, data, size);
|
||||
}
|
||||
|
||||
int yaffs_lgetxattr(const YCHAR *path, const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_lgetxattr_reldir(NULL, path, name, data, size);
|
||||
}
|
||||
|
||||
int yaffs_fgetxattr(int fd, const char *name, void *data, int size)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(name, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
retVal = yaffs_get_xattrib(obj, name, data, size);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffs_do_listxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size, int follow)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (follow)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else {
|
||||
retVal = yaffs_list_xattrib(obj, data, size);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_listxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size)
|
||||
{
|
||||
return yaffs_do_listxattr_reldir(reldir, path, data, size, 1);
|
||||
}
|
||||
|
||||
int yaffs_listxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, char *data, int size)
|
||||
{
|
||||
return yaffs_listxattr_reldir(ROOT_DIR(dev), path, data, size);
|
||||
}
|
||||
|
||||
int yaffs_listxattr(const YCHAR *path, char *data, int size)
|
||||
{
|
||||
return yaffs_listxattr_reldir(NULL, path, data, size);
|
||||
}
|
||||
|
||||
int yaffs_llistxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size)
|
||||
{
|
||||
return yaffs_do_listxattr_reldir(reldir, path, data, size, 0);
|
||||
}
|
||||
|
||||
int yaffs_llistxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, char *data, int size)
|
||||
{
|
||||
return yaffs_llistxattr_reldir(ROOT_DIR(dev), path, data, size);
|
||||
}
|
||||
|
||||
int yaffs_llistxattr(const YCHAR *path, char *data, int size)
|
||||
{
|
||||
return yaffs_llistxattr_reldir(NULL, path, data, size);
|
||||
}
|
||||
|
||||
int yaffs_flistxattr(int fd, char *data, int size)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(data, size, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
retVal = yaffs_list_xattrib(obj, data, size);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffs_do_removexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, int follow)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(name, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (follow)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else {
|
||||
retVal = yaffs_remove_xattrib(obj, name);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_removexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name)
|
||||
{
|
||||
return yaffs_do_removexattr_reldir(reldir, path, name, 1);
|
||||
}
|
||||
|
||||
int yaffs_removexattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name)
|
||||
{
|
||||
return yaffs_removexattr_reldir(ROOT_DIR(dev), path, name);
|
||||
}
|
||||
|
||||
int yaffs_removexattr(const YCHAR *path, const char *name)
|
||||
{
|
||||
return yaffs_removexattr_reldir(NULL, path, name);
|
||||
}
|
||||
|
||||
int yaffs_lremovexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name)
|
||||
{
|
||||
return yaffs_do_removexattr_reldir(reldir, path, name, 0);
|
||||
}
|
||||
|
||||
int yaffs_lremovexattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name)
|
||||
{
|
||||
return yaffs_lremovexattr_reldir(ROOT_DIR(dev), path, name);
|
||||
}
|
||||
|
||||
int yaffs_lremovexattr(const YCHAR *path, const char *name)
|
||||
{
|
||||
return yaffs_lremovexattr_reldir(NULL, path, name);
|
||||
}
|
||||
|
||||
int yaffs_fremovexattr(int fd, const char *name)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(name, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
retVal = yaffs_remove_xattrib(obj, name);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
int yaffs_get_wince_times(int fd, unsigned *wctime,
|
||||
unsigned *watime, unsigned *wmtime)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
|
||||
if (wctime) {
|
||||
wctime[0] = obj->win_ctime[0];
|
||||
wctime[1] = obj->win_ctime[1];
|
||||
}
|
||||
if (watime) {
|
||||
watime[0] = obj->win_atime[0];
|
||||
watime[1] = obj->win_atime[1];
|
||||
}
|
||||
if (wmtime) {
|
||||
wmtime[0] = obj->win_mtime[0];
|
||||
wmtime[1] = obj->win_mtime[1];
|
||||
}
|
||||
|
||||
retVal = 0;
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_set_wince_times(int fd,
|
||||
const unsigned *wctime,
|
||||
const unsigned *watime, const unsigned *wmtime)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
int result;
|
||||
int retVal = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
|
||||
if (wctime) {
|
||||
obj->win_ctime[0] = wctime[0];
|
||||
obj->win_ctime[1] = wctime[1];
|
||||
}
|
||||
if (watime) {
|
||||
obj->win_atime[0] = watime[0];
|
||||
obj->win_atime[1] = watime[1];
|
||||
}
|
||||
if (wmtime) {
|
||||
obj->win_mtime[0] = wmtime[0];
|
||||
obj->win_mtime[1] = wmtime[1];
|
||||
}
|
||||
|
||||
obj->dirty = 1;
|
||||
result = yaffs_flush_file(obj, 0, 0, 0);
|
||||
retVal = 0;
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int yaffsfs_DoChMod(struct yaffs_obj *obj, mode_t mode)
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
if (obj)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
if (obj) {
|
||||
obj->yst_mode = mode;
|
||||
obj->dirty = 1;
|
||||
result = yaffs_flush_file(obj, 0, 0, 0);
|
||||
}
|
||||
|
||||
return result == YAFFS_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
int yaffs_access_reldir(struct yaffs_obj *reldir, const YCHAR *path, int amode)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
int retval = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (amode & ~(R_OK | W_OK | X_OK)) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if ((amode & W_OK) && obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else {
|
||||
int access_ok = 1;
|
||||
|
||||
if ((amode & R_OK) && !(obj->yst_mode & S_IREAD))
|
||||
access_ok = 0;
|
||||
if ((amode & W_OK) && !(obj->yst_mode & S_IWRITE))
|
||||
access_ok = 0;
|
||||
if ((amode & X_OK) && !(obj->yst_mode & S_IEXEC))
|
||||
access_ok = 0;
|
||||
|
||||
if (!access_ok)
|
||||
yaffsfs_SetError(-EACCES);
|
||||
else
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_access_reldev(struct yaffs_dev *dev, const YCHAR *path, int amode)
|
||||
{
|
||||
return yaffs_access_reldir(ROOT_DIR(dev), path, amode);
|
||||
}
|
||||
|
||||
int yaffs_access(const YCHAR *path, int amode)
|
||||
{
|
||||
return yaffs_access_reldir(NULL, path, amode);
|
||||
}
|
||||
|
||||
int yaffs_chmod_reldir(struct yaffs_obj *reldir, const YCHAR *path, mode_t mode)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mode & ~(0777)) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else
|
||||
retVal = yaffsfs_DoChMod(obj, mode);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_chmod_reldev(struct yaffs_dev *dev, const YCHAR *path, mode_t mode)
|
||||
{
|
||||
return yaffs_chmod_reldir(ROOT_DIR(dev), path, mode);
|
||||
}
|
||||
|
||||
int yaffs_chmod(const YCHAR *path, mode_t mode)
|
||||
{
|
||||
return yaffs_chmod_reldir(NULL, path, mode);
|
||||
}
|
||||
|
||||
int yaffs_fchmod(int fd, mode_t mode)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
int retVal = -1;
|
||||
|
||||
if (mode & ~(0777)) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (!obj)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else
|
||||
retVal = yaffsfs_DoChMod(obj, mode);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_mkdir_reldir(struct yaffs_obj *reldir, const YCHAR *path, mode_t mode)
|
||||
{
|
||||
struct yaffs_obj *parent = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
YCHAR *name;
|
||||
YCHAR *alt_path = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_alt_dir_path(path, &alt_path) < 0) {
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
return -1;
|
||||
}
|
||||
if (alt_path)
|
||||
path = alt_path;
|
||||
|
||||
yaffsfs_Lock();
|
||||
parent = yaffsfs_FindDirectory(reldir, path, &name, 0, ¬Dir, &loop);
|
||||
if (!parent && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!parent)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (yaffsfs_TooManyObjects(parent->my_dev))
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
else if (yaffs_strnlen(name, 5) == 0) {
|
||||
/* Trying to make the root itself */
|
||||
yaffsfs_SetError(-EEXIST);
|
||||
} else if (parent->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else {
|
||||
dir = yaffs_create_dir(parent, name, mode, 0, 0);
|
||||
if (dir)
|
||||
retVal = 0;
|
||||
else if (yaffs_find_by_name(parent, name))
|
||||
yaffsfs_SetError(-EEXIST); /* name exists */
|
||||
else
|
||||
yaffsfs_SetError(-ENOSPC); /* assume no space */
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
kfree(alt_path);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_mkdir_reldev(struct yaffs_dev *dev, const YCHAR *path, mode_t mode)
|
||||
{
|
||||
return yaffs_mkdir_reldir(ROOT_DIR(dev), path, mode);
|
||||
}
|
||||
|
||||
int yaffs_mkdir(const YCHAR *path, mode_t mode)
|
||||
{
|
||||
return yaffs_mkdir_reldir(NULL, path, mode);
|
||||
}
|
||||
|
||||
int yaffs_rmdir_reldir(struct yaffs_obj *reldir, const YCHAR *path)
|
||||
{
|
||||
int result;
|
||||
YCHAR *alt_path;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_alt_dir_path(path, &alt_path) < 0) {
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
return -1;
|
||||
}
|
||||
if (alt_path)
|
||||
path = alt_path;
|
||||
result = yaffsfs_DoUnlink_reldir(reldir, path, 1);
|
||||
|
||||
kfree(alt_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_rmdir_reldev(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
return yaffs_rmdir_reldir(ROOT_DIR(dev), path);
|
||||
}
|
||||
|
||||
int yaffs_rmdir(const YCHAR *path)
|
||||
{
|
||||
return yaffs_rmdir_reldir(NULL, path);
|
||||
}
|
||||
|
||||
/*
|
||||
* The mount/unmount/sync functions act on devices rather than reldirs.
|
||||
*/
|
||||
void *yaffs_getdev(const YCHAR *path)
|
||||
{
|
||||
struct yaffs_dev *dev = NULL;
|
||||
YCHAR *dummy;
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
return (void *)dev;
|
||||
}
|
||||
|
||||
int yaffs_mount_common(struct yaffs_dev *dev, const YCHAR *path,
|
||||
int read_only, int skip_checkpt)
|
||||
{
|
||||
int retVal = -1;
|
||||
int result = YAFFS_FAIL;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MOUNT, "yaffs: Mounting %s", path);
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
yaffsfs_InitHandles();
|
||||
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindMountPoint(path);
|
||||
|
||||
if (dev) {
|
||||
if (!dev->is_mounted) {
|
||||
dev->read_only = read_only ? 1 : 0;
|
||||
if (skip_checkpt) {
|
||||
u8 skip = dev->param.skip_checkpt_rd;
|
||||
dev->param.skip_checkpt_rd = 1;
|
||||
result = yaffs_guts_initialise(dev);
|
||||
dev->param.skip_checkpt_rd = skip;
|
||||
} else {
|
||||
result = yaffs_guts_initialise(dev);
|
||||
}
|
||||
|
||||
if (result == YAFFS_FAIL)
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
retVal = result ? 0 : -1;
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_mount3_reldev(struct yaffs_dev *dev, int read_only, int skip_checkpt)
|
||||
{
|
||||
return yaffs_mount_common(dev, NULL, read_only, skip_checkpt);
|
||||
}
|
||||
|
||||
int yaffs_mount3(const YCHAR *path, int read_only, int skip_checkpt)
|
||||
{
|
||||
return yaffs_mount_common(NULL, path, read_only, skip_checkpt);
|
||||
}
|
||||
|
||||
int yaffs_mount2_reldev(struct yaffs_dev *dev, int readonly)
|
||||
{
|
||||
return yaffs_mount_common(dev, NULL, readonly, 0);
|
||||
}
|
||||
|
||||
int yaffs_mount2(const YCHAR *path, int readonly)
|
||||
{
|
||||
return yaffs_mount_common(NULL, path, readonly, 0);
|
||||
}
|
||||
|
||||
int yaffs_mount_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_mount_common(dev, NULL, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_mount(const YCHAR *path)
|
||||
{
|
||||
return yaffs_mount_common(NULL, path, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_sync_common(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
int retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
|
||||
if (dev) {
|
||||
if (!dev->is_mounted)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else if (dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else {
|
||||
|
||||
yaffs_flush_whole_cache(dev, 0);
|
||||
yaffs_checkpoint_save(dev);
|
||||
retVal = 0;
|
||||
|
||||
}
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_sync_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_sync_common(dev, NULL);
|
||||
}
|
||||
|
||||
int yaffs_sync(const YCHAR *path)
|
||||
{
|
||||
return yaffs_sync_common(NULL, path);
|
||||
}
|
||||
|
||||
|
||||
static int yaffsfs_bg_gc_common(struct yaffs_dev *dev,
|
||||
const YCHAR *path,
|
||||
int urgency)
|
||||
{
|
||||
int retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
|
||||
if (dev) {
|
||||
if (!dev->is_mounted)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else
|
||||
retVal = yaffs_bg_gc(dev, urgency);
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/* Background gc functions.
|
||||
* These return 0 when bg done or greater than 0 when gc has been
|
||||
* done and there is still a lot of garbage to be cleaned up.
|
||||
*/
|
||||
|
||||
int yaffs_do_background_gc(const YCHAR *path, int urgency)
|
||||
{
|
||||
return yaffsfs_bg_gc_common(NULL, path, urgency);
|
||||
}
|
||||
|
||||
int yaffs_do_background_gc_reldev(struct yaffs_dev *dev, int urgency)
|
||||
{
|
||||
return yaffsfs_bg_gc_common(dev, NULL, urgency);
|
||||
}
|
||||
|
||||
static int yaffsfs_IsDevBusy(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
obj = yaffsfs_HandleToObject(i);
|
||||
if (obj && obj->my_dev == dev)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_remount_common(struct yaffs_dev *dev, const YCHAR *path,
|
||||
int force, int read_only)
|
||||
{
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindMountPoint(path);
|
||||
|
||||
if (dev) {
|
||||
if (dev->is_mounted) {
|
||||
yaffs_flush_whole_cache(dev, 0);
|
||||
|
||||
if (force || !yaffsfs_IsDevBusy(dev)) {
|
||||
if (read_only)
|
||||
yaffs_checkpoint_save(dev);
|
||||
dev->read_only = read_only ? 1 : 0;
|
||||
retVal = 0;
|
||||
} else
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_remount_reldev(struct yaffs_dev *dev, int force, int read_only)
|
||||
{
|
||||
return yaffs_remount_common(dev, NULL, force, read_only);
|
||||
}
|
||||
int yaffs_remount(const YCHAR *path, int force, int read_only)
|
||||
{
|
||||
return yaffs_remount_common(NULL, path, force, read_only);
|
||||
}
|
||||
|
||||
int yaffs_unmount2_common(struct yaffs_dev *dev, const YCHAR *path, int force)
|
||||
{
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindMountPoint(path);
|
||||
|
||||
if (dev) {
|
||||
if (dev->is_mounted) {
|
||||
int inUse;
|
||||
yaffs_flush_whole_cache(dev, 0);
|
||||
yaffs_checkpoint_save(dev);
|
||||
inUse = yaffsfs_IsDevBusy(dev);
|
||||
if (!inUse || force) {
|
||||
if (inUse)
|
||||
yaffsfs_BreakDeviceHandles(dev);
|
||||
yaffs_deinitialise(dev);
|
||||
|
||||
retVal = 0;
|
||||
} else
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_unmount2_reldev(struct yaffs_dev *dev, int force)
|
||||
{
|
||||
return yaffs_unmount2_common(dev, NULL, force);
|
||||
}
|
||||
|
||||
int yaffs_unmount2(const YCHAR *path, int force)
|
||||
{
|
||||
return yaffs_unmount2_common(NULL, path, force);
|
||||
}
|
||||
|
||||
int yaffs_unmount_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_unmount2_reldev(dev, 0);
|
||||
}
|
||||
|
||||
int yaffs_unmount(const YCHAR *path)
|
||||
{
|
||||
return yaffs_unmount2(path, 0);
|
||||
}
|
||||
|
||||
int yaffs_format_common(struct yaffs_dev *dev,
|
||||
const YCHAR *path,
|
||||
int unmount_flag,
|
||||
int force_unmount_flag,
|
||||
int remount_flag)
|
||||
{
|
||||
int retVal = 0;
|
||||
int result;
|
||||
|
||||
if (!dev) {
|
||||
if (!path) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindMountPoint(path);
|
||||
|
||||
if (dev) {
|
||||
int was_mounted = dev->is_mounted;
|
||||
|
||||
if (dev->is_mounted && unmount_flag) {
|
||||
int inUse;
|
||||
yaffs_flush_whole_cache(dev, 0);
|
||||
yaffs_checkpoint_save(dev);
|
||||
inUse = yaffsfs_IsDevBusy(dev);
|
||||
if (!inUse || force_unmount_flag) {
|
||||
if (inUse)
|
||||
yaffsfs_BreakDeviceHandles(dev);
|
||||
yaffs_deinitialise(dev);
|
||||
}
|
||||
}
|
||||
|
||||
if(dev->is_mounted) {
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
retVal = -1;
|
||||
} else {
|
||||
yaffs_guts_format_dev(dev);
|
||||
if(was_mounted && remount_flag) {
|
||||
result = yaffs_guts_initialise(dev);
|
||||
if (result == YAFFS_FAIL) {
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
retVal = -1;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_format_reldev(struct yaffs_dev *dev,
|
||||
int unmount_flag,
|
||||
int force_unmount_flag,
|
||||
int remount_flag)
|
||||
{
|
||||
return yaffs_format_common(dev, NULL, unmount_flag,
|
||||
force_unmount_flag, remount_flag);
|
||||
}
|
||||
|
||||
int yaffs_format(const YCHAR *path,
|
||||
int unmount_flag,
|
||||
int force_unmount_flag,
|
||||
int remount_flag)
|
||||
{
|
||||
return yaffs_format_common(NULL, path, unmount_flag,
|
||||
force_unmount_flag, remount_flag);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_freespace_common(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
Y_LOFF_T retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
if (dev && dev->is_mounted) {
|
||||
retVal = yaffs_get_n_free_chunks(dev);
|
||||
retVal *= dev->data_bytes_per_chunk;
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_freespace_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_freespace_common(dev, NULL);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_freespace(const YCHAR *path)
|
||||
{
|
||||
return yaffs_freespace_common(NULL, path);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_totalspace_common(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
Y_LOFF_T retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
if (dev && dev->is_mounted) {
|
||||
retVal = (dev->param.end_block - dev->param.start_block + 1) -
|
||||
dev->param.n_reserved_blocks;
|
||||
retVal *= dev->param.chunks_per_block;
|
||||
retVal *= dev->data_bytes_per_chunk;
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_totalspace_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_totalspace_common(dev, NULL);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_totalspace(const YCHAR *path)
|
||||
{
|
||||
return yaffs_totalspace_common(NULL, path);
|
||||
}
|
||||
|
||||
int yaffs_inodecount_common(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
Y_LOFF_T retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
if (dev && dev->is_mounted) {
|
||||
int n_obj = dev->n_obj;
|
||||
if (n_obj > dev->n_hardlinks)
|
||||
retVal = n_obj - dev->n_hardlinks;
|
||||
}
|
||||
|
||||
if (retVal < 0)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_inodecount_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_inodecount_common(dev, NULL);
|
||||
}
|
||||
|
||||
int yaffs_inodecount(const YCHAR *path)
|
||||
{
|
||||
return yaffs_inodecount_common(NULL, path);
|
||||
}
|
||||
|
||||
void yaffs_add_device(struct yaffs_dev *dev)
|
||||
{
|
||||
struct list_head *cfg;
|
||||
/* First check that the device is not in the list. */
|
||||
|
||||
list_for_each(cfg, &yaffsfs_deviceList) {
|
||||
if (dev == list_entry(cfg, struct yaffs_dev, dev_list))
|
||||
return;
|
||||
}
|
||||
|
||||
dev->is_mounted = 0;
|
||||
dev->param.remove_obj_fn = yaffsfs_RemoveObjectCallback;
|
||||
|
||||
if (!dev->dev_list.next)
|
||||
INIT_LIST_HEAD(&dev->dev_list);
|
||||
|
||||
list_add(&dev->dev_list, &yaffsfs_deviceList);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void yaffs_remove_device(struct yaffs_dev *dev)
|
||||
{
|
||||
list_del_init(&dev->dev_list);
|
||||
}
|
||||
|
||||
/* Functions to iterate through devices. NB Use with extreme care! */
|
||||
|
||||
static struct list_head *dev_iterator;
|
||||
void yaffs_dev_rewind(void)
|
||||
{
|
||||
dev_iterator = yaffsfs_deviceList.next;
|
||||
}
|
||||
|
||||
struct yaffs_dev *yaffs_next_dev(void)
|
||||
{
|
||||
struct yaffs_dev *retval;
|
||||
|
||||
if (!dev_iterator)
|
||||
return NULL;
|
||||
if (dev_iterator == &yaffsfs_deviceList)
|
||||
return NULL;
|
||||
|
||||
retval = list_entry(dev_iterator, struct yaffs_dev, dev_list);
|
||||
dev_iterator = dev_iterator->next;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Directory search stuff. */
|
||||
|
||||
static struct list_head search_contexts;
|
||||
|
||||
static void yaffsfs_SetDirRewound(struct yaffsfs_DirSearchContext *dsc)
|
||||
{
|
||||
if (dsc &&
|
||||
dsc->dirObj &&
|
||||
dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
|
||||
dsc->offset = 0;
|
||||
|
||||
if (list_empty(&dsc->dirObj->variant.dir_variant.children))
|
||||
dsc->nextReturn = NULL;
|
||||
else
|
||||
dsc->nextReturn =
|
||||
list_entry(dsc->dirObj->variant.dir_variant.
|
||||
children.next, struct yaffs_obj,
|
||||
siblings);
|
||||
} else {
|
||||
/* Hey someone isn't playing nice! */
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffsfs_DirAdvance(struct yaffsfs_DirSearchContext *dsc)
|
||||
{
|
||||
if (dsc &&
|
||||
dsc->dirObj &&
|
||||
dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
|
||||
if (dsc->nextReturn == NULL ||
|
||||
list_empty(&dsc->dirObj->variant.dir_variant.children))
|
||||
dsc->nextReturn = NULL;
|
||||
else {
|
||||
struct list_head *next = dsc->nextReturn->siblings.next;
|
||||
|
||||
if (next == &dsc->dirObj->variant.dir_variant.children)
|
||||
dsc->nextReturn = NULL; /* end of list */
|
||||
else
|
||||
dsc->nextReturn = list_entry(next,
|
||||
struct yaffs_obj,
|
||||
siblings);
|
||||
}
|
||||
} else {
|
||||
/* Hey someone isn't playing nice! */
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj)
|
||||
{
|
||||
|
||||
struct list_head *i;
|
||||
struct yaffsfs_DirSearchContext *dsc;
|
||||
|
||||
/* if search contexts not initilised then skip */
|
||||
if (!search_contexts.next)
|
||||
return;
|
||||
|
||||
/* Iterate through the directory search contexts.
|
||||
* If any are the one being removed, then advance the dsc to
|
||||
* the next one to prevent a hanging ptr.
|
||||
*/
|
||||
list_for_each(i, &search_contexts) {
|
||||
if (i) {
|
||||
dsc = list_entry(i, struct yaffsfs_DirSearchContext,
|
||||
others);
|
||||
if (dsc->nextReturn == obj)
|
||||
yaffsfs_DirAdvance(dsc);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static yaffs_DIR *yaffsfs_opendir_reldir_no_lock(struct yaffs_obj *reldir,
|
||||
const YCHAR *dirname)
|
||||
{
|
||||
yaffs_DIR *dir = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffsfs_DirSearchContext *dsc = NULL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(dirname, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(dirname) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, dirname, 0, 1, NULL, ¬Dir, &loop);
|
||||
|
||||
if (!obj && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else {
|
||||
int i;
|
||||
|
||||
for (i = 0, dsc = NULL; i < YAFFSFS_N_DSC && !dsc; i++) {
|
||||
if (!yaffsfs_dsc[i].inUse)
|
||||
dsc = &yaffsfs_dsc[i];
|
||||
}
|
||||
|
||||
dir = (yaffs_DIR *) dsc;
|
||||
|
||||
if (dsc) {
|
||||
memset(dsc, 0, sizeof(struct yaffsfs_DirSearchContext));
|
||||
dsc->inUse = 1;
|
||||
dsc->dirObj = obj;
|
||||
yaffs_strncpy(dsc->name, dirname, NAME_MAX);
|
||||
INIT_LIST_HEAD(&dsc->others);
|
||||
|
||||
if (!search_contexts.next)
|
||||
INIT_LIST_HEAD(&search_contexts);
|
||||
|
||||
list_add(&dsc->others, &search_contexts);
|
||||
yaffsfs_SetDirRewound(dsc);
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
yaffs_DIR *yaffs_opendir_reldir(struct yaffs_obj *reldir, const YCHAR *dirname)
|
||||
{
|
||||
yaffs_DIR *ret;
|
||||
|
||||
yaffsfs_Lock();
|
||||
ret = yaffsfs_opendir_reldir_no_lock(reldir, dirname);
|
||||
yaffsfs_Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
yaffs_DIR *yaffs_opendir_reldev(struct yaffs_dev *dev, const YCHAR *dirname)
|
||||
{
|
||||
return yaffs_opendir_reldir(ROOT_DIR(dev), dirname);
|
||||
}
|
||||
|
||||
yaffs_DIR *yaffs_opendir(const YCHAR *dirname)
|
||||
{
|
||||
return yaffs_opendir_reldir(NULL, dirname);
|
||||
}
|
||||
|
||||
struct yaffs_dirent *yaffsfs_readdir_no_lock(yaffs_DIR * dirp)
|
||||
{
|
||||
struct yaffsfs_DirSearchContext *dsc;
|
||||
struct yaffs_dirent *retVal = NULL;
|
||||
|
||||
dsc = (struct yaffsfs_DirSearchContext *) dirp;
|
||||
|
||||
|
||||
if (dsc && dsc->inUse) {
|
||||
yaffsfs_SetError(0);
|
||||
if (dsc->nextReturn) {
|
||||
dsc->de.d_ino =
|
||||
yaffs_get_equivalent_obj(dsc->nextReturn)->obj_id;
|
||||
dsc->de.d_dont_use = (unsigned)dsc->nextReturn;
|
||||
dsc->de.d_off = dsc->offset++;
|
||||
yaffs_get_obj_name(dsc->nextReturn,
|
||||
dsc->de.d_name, NAME_MAX);
|
||||
if (yaffs_strnlen(dsc->de.d_name, NAME_MAX + 1) == 0) {
|
||||
/* this should not happen! */
|
||||
yaffs_strcpy(dsc->de.d_name, _Y("zz"));
|
||||
}
|
||||
dsc->de.d_reclen = sizeof(struct yaffs_dirent);
|
||||
retVal = &dsc->de;
|
||||
yaffsfs_DirAdvance(dsc);
|
||||
} else
|
||||
retVal = NULL;
|
||||
} else
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
struct yaffs_dirent *yaffs_readdir(yaffs_DIR * dirp)
|
||||
{
|
||||
struct yaffs_dirent *ret;
|
||||
|
||||
yaffsfs_Lock();
|
||||
ret = yaffsfs_readdir_no_lock(dirp);
|
||||
yaffsfs_Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void yaffsfs_rewinddir_no_lock(yaffs_DIR *dirp)
|
||||
{
|
||||
struct yaffsfs_DirSearchContext *dsc;
|
||||
|
||||
dsc = (struct yaffsfs_DirSearchContext *) dirp;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(dirp, sizeof(*dsc), 0) < 0)
|
||||
return;
|
||||
|
||||
yaffsfs_SetDirRewound(dsc);
|
||||
|
||||
}
|
||||
|
||||
void yaffs_rewinddir(yaffs_DIR *dirp)
|
||||
{
|
||||
yaffsfs_Lock();
|
||||
yaffsfs_rewinddir_no_lock(dirp);
|
||||
yaffsfs_Unlock();
|
||||
}
|
||||
|
||||
struct yaffs_dirent *yaffs_readdir_fd(int fd)
|
||||
{
|
||||
struct yaffs_dirent *ret = NULL;
|
||||
struct yaffsfs_FileDes *f;
|
||||
|
||||
yaffsfs_Lock();
|
||||
f = yaffsfs_HandleToFileDes(fd);
|
||||
if(f && f->isDir)
|
||||
ret = yaffsfs_readdir_no_lock(f->v.dir);
|
||||
yaffsfs_Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void yaffs_rewinddir_fd(int fd)
|
||||
{
|
||||
struct yaffsfs_FileDes *f;
|
||||
|
||||
yaffsfs_Lock();
|
||||
f = yaffsfs_HandleToFileDes(fd);
|
||||
if(f && f->isDir)
|
||||
yaffsfs_rewinddir_no_lock(f->v.dir);
|
||||
yaffsfs_Unlock();
|
||||
}
|
||||
|
||||
|
||||
static int yaffsfs_closedir_no_lock(yaffs_DIR *dirp)
|
||||
{
|
||||
struct yaffsfs_DirSearchContext *dsc;
|
||||
|
||||
dsc = (struct yaffsfs_DirSearchContext *) dirp;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(dirp, sizeof(*dsc), 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dsc->inUse = 0;
|
||||
list_del(&dsc->others); /* unhook from list */
|
||||
|
||||
return 0;
|
||||
}
|
||||
int yaffs_closedir(yaffs_DIR *dirp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
yaffsfs_Lock();
|
||||
ret = yaffsfs_closedir_no_lock(dirp);
|
||||
yaffsfs_Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* End of directory stuff */
|
||||
|
||||
int yaffs_symlink_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldpath, const YCHAR *newpath)
|
||||
{
|
||||
struct yaffs_obj *parent = NULL;
|
||||
struct yaffs_obj *obj;
|
||||
YCHAR *name;
|
||||
int retVal = -1;
|
||||
int mode = 0; /* ignore for now */
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(oldpath, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(newpath, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(newpath) < 0 || yaffsfs_CheckPath(oldpath) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
parent = yaffsfs_FindDirectory(reldir, newpath, &name, 0, ¬Dir, &loop);
|
||||
if (!parent && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!parent || yaffs_strnlen(name, 5) < 1)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (yaffsfs_TooManyObjects(parent->my_dev))
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
else if (parent->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (parent) {
|
||||
obj = yaffs_create_symlink(parent, name, mode, 0, 0, oldpath);
|
||||
if (obj)
|
||||
retVal = 0;
|
||||
else if (yaffsfs_FindObject(reldir, newpath, 0, 0,
|
||||
NULL, NULL, NULL))
|
||||
yaffsfs_SetError(-EEXIST);
|
||||
else
|
||||
yaffsfs_SetError(-ENOSPC);
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath)
|
||||
{
|
||||
return yaffs_symlink_reldir(NULL, oldpath, newpath);
|
||||
}
|
||||
|
||||
int yaffs_readlink_reldir(struct yaffs_obj *reldir,const YCHAR *path,
|
||||
YCHAR *buf, int bufsiz)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(buf, bufsiz, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->variant_type != YAFFS_OBJECT_TYPE_SYMLINK)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else {
|
||||
YCHAR *alias = obj->variant.symlink_variant.alias;
|
||||
memset(buf, 0, bufsiz);
|
||||
yaffs_strncpy(buf, alias, bufsiz - 1);
|
||||
retVal = 0;
|
||||
}
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz)
|
||||
{
|
||||
return yaffs_readlink_reldir(NULL, path, buf, bufsiz);
|
||||
}
|
||||
|
||||
int yaffs_link_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldpath, const YCHAR *linkpath)
|
||||
{
|
||||
/* Creates a link called newpath to existing oldpath */
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *lnk = NULL;
|
||||
struct yaffs_obj *obj_dir = NULL;
|
||||
struct yaffs_obj *lnk_dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDirObj = 0;
|
||||
int notDirLnk = 0;
|
||||
int objLoop = 0;
|
||||
int lnkLoop = 0;
|
||||
YCHAR *newname;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(oldpath, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(linkpath, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(linkpath) < 0 || yaffsfs_CheckPath(oldpath) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, oldpath, 0, 1,
|
||||
&obj_dir, ¬DirObj, &objLoop);
|
||||
lnk = yaffsfs_FindObject(reldir, linkpath, 0, 0, NULL, NULL, NULL);
|
||||
lnk_dir = yaffsfs_FindDirectory(reldir, linkpath, &newname,
|
||||
0, ¬DirLnk, &lnkLoop);
|
||||
|
||||
if ((!obj_dir && notDirObj) || (!lnk_dir && notDirLnk))
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (objLoop || lnkLoop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!obj_dir || !lnk_dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (yaffsfs_TooManyObjects(obj->my_dev))
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
else if (lnk)
|
||||
yaffsfs_SetError(-EEXIST);
|
||||
else if (lnk_dir->my_dev != obj->my_dev)
|
||||
yaffsfs_SetError(-EXDEV);
|
||||
else {
|
||||
retVal = yaffsfs_CheckNameLength(newname);
|
||||
|
||||
if (retVal == 0) {
|
||||
lnk = yaffs_link_obj(lnk_dir, newname, obj);
|
||||
if (lnk)
|
||||
retVal = 0;
|
||||
else {
|
||||
yaffsfs_SetError(-ENOSPC);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
int yaffs_link(const YCHAR *oldpath, const YCHAR *linkpath)
|
||||
{
|
||||
return yaffs_link_reldir(NULL, oldpath, linkpath);
|
||||
}
|
||||
|
||||
int yaffs_mknod_reldir(struct yaffs_obj *reldir, const YCHAR *pathname,
|
||||
mode_t mode, dev_t dev_val)
|
||||
{
|
||||
(void) pathname;
|
||||
(void) mode;
|
||||
(void) dev_val;
|
||||
(void) reldir;
|
||||
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int yaffs_mknod_reldev(struct yaffs_dev *dev, const YCHAR *pathname, mode_t mode, dev_t dev_val)
|
||||
{
|
||||
return yaffs_mknod_reldir(ROOT_DIR(dev), pathname, mode, dev_val);
|
||||
}
|
||||
|
||||
int yaffs_mknod(const YCHAR *pathname, mode_t mode, dev_t dev_val)
|
||||
{
|
||||
return yaffs_mknod_reldir(NULL, pathname, mode, dev_val);
|
||||
}
|
||||
|
||||
/*
|
||||
* D E B U G F U N C T I O N S
|
||||
*/
|
||||
|
||||
/*
|
||||
* yaffs_n_handles()
|
||||
* Returns number of handles attached to the object
|
||||
*/
|
||||
int yaffs_n_handles_reldir(struct yaffs_obj *reldir, const YCHAR *path)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, NULL, NULL, NULL);
|
||||
|
||||
if (obj)
|
||||
return yaffsfs_CountHandles(obj);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int yaffs_n_handles(const YCHAR *path)
|
||||
{
|
||||
return yaffs_n_handles_reldir(NULL, path);
|
||||
}
|
||||
|
||||
int yaffs_get_error(void)
|
||||
{
|
||||
return yaffsfs_GetLastError();
|
||||
}
|
||||
|
||||
int yaffs_set_error(int error)
|
||||
{
|
||||
yaffsfs_SetError(error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_dump_dev_reldir(struct yaffs_obj *reldir, const YCHAR *path)
|
||||
{
|
||||
#if 1
|
||||
(void) path;
|
||||
(void) reldir;
|
||||
#else
|
||||
YCHAR *rest;
|
||||
|
||||
|
||||
if (!reldir)
|
||||
reldir = yaffsfs_FindRoot(path, &rest);
|
||||
|
||||
if (reldir) {
|
||||
struct yaffs_dev *dev = reldir->my_dev;
|
||||
|
||||
printf("\n"
|
||||
"n_page_writes.......... %d\n"
|
||||
"n_page_reads........... %d\n"
|
||||
"n_erasures....... %d\n"
|
||||
"n_gc_copies............ %d\n"
|
||||
"garbageCollections... %d\n"
|
||||
"passiveGarbageColl'ns %d\n"
|
||||
"\n",
|
||||
dev->n_page_writes,
|
||||
dev->n_page_reads,
|
||||
dev->n_erasures,
|
||||
dev->n_gc_copies,
|
||||
dev->garbageCollections, dev->passiveGarbageCollections);
|
||||
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_dump_dev(const YCHAR *path)
|
||||
{
|
||||
return yaffs_dump_dev_reldir(NULL, path);
|
||||
}
|
@ -35,6 +35,17 @@ static xSemaphoreHandle mutex = 0;
|
||||
|
||||
struct flashfs_logfs_cfg;
|
||||
|
||||
/**
|
||||
* glue macros for file IO
|
||||
**/
|
||||
#define FILEINFO FILE *
|
||||
#define PIOS_FOPEN_READ(filename, file) (file = fopen((char *)filename, "r")) == NULL
|
||||
#define PIOS_FOPEN_WRITE(filename, file) (file = fopen((char *)filename, "w")) == NULL
|
||||
#define PIOS_FREAD(file, bufferadr, length, resultadr) (*resultadr = fread((uint8_t *)bufferadr, 1, length, *file)) != length
|
||||
#define PIOS_FWRITE(file, bufferadr, length, resultadr) *resultadr = fwrite((uint8_t *)bufferadr, 1, length, *file)
|
||||
#define PIOS_FCLOSE(file) fclose(file)
|
||||
#define PIOS_FUNLINK(file) unlink((char *)filename)
|
||||
|
||||
|
||||
/**
|
||||
* Get an 8 character (plus extension) filename for the object.
|
||||
|
301
flight/pios/common/pios_logfs.c
Normal file
301
flight/pios/common/pios_logfs.c
Normal file
@ -0,0 +1,301 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file pios_logfs.c
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2013.
|
||||
* @addtogroup PIOS PIOS Core hardware abstraction layer
|
||||
* @{
|
||||
* @brief Log Structured Filesystem leverages yaffs
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "pios.h"
|
||||
#include "pios_stdio.h"
|
||||
#include "pios_trace.h"
|
||||
|
||||
#include <openpilot.h>
|
||||
|
||||
/**
|
||||
* Get an object file name and device name and path name
|
||||
* @param[in] fs_id flash device id
|
||||
* @param[in] obj The object handle.
|
||||
* @param[in] instId The instance ID
|
||||
* @param[in] file Filename string pointer -- must be 14 bytes long and allocated
|
||||
* @param[in] path string pointer -- must be 26 bytes long and allocated
|
||||
*/
|
||||
#define OBJECTPATHNAME_LEN 26
|
||||
static void getObjectPathAndName(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, char *filename)
|
||||
{
|
||||
uint32_t prefix = obj_id + (obj_inst_id / 256) * 16; // put upper 8 bit of instance id into object id modification,
|
||||
// skip least sig nibble since that is used for meta object id
|
||||
uint8_t suffix = obj_inst_id & 0xff;
|
||||
|
||||
snprintf((char *)filename, OBJECTPATHNAME_LEN, "/dev%01u/logfs/%08X.o%02X", (unsigned)fs_id, prefix, suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an devicename
|
||||
* @param[in] fs_id flash device id
|
||||
* @param[in] devicename string pointer -- must be 8 bytes long and allocated
|
||||
*/
|
||||
#define DEVICENAME_LEN 8
|
||||
static void getDeviceName(uintptr_t fs_id, char *devicename)
|
||||
{
|
||||
snprintf((char *)devicename, DEVICENAME_LEN, "/dev%01u", (unsigned)fs_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an logfs path
|
||||
* @param[in] fs_id flash device id
|
||||
* @param[in] devicename string pointer -- must be 14 bytes long and allocated
|
||||
*/
|
||||
#define LOGFSPATH_LEN 14
|
||||
static void getLogfsPath(uintptr_t fs_id, char *devicename)
|
||||
{
|
||||
snprintf((char *)devicename, LOGFSPATH_LEN, "/dev%01u/logfs", (unsigned)fs_id);
|
||||
}
|
||||
|
||||
// Simplistic API takes the fs_id and maps to a path /dev0 eg. not really necessary
|
||||
// but allows reuse existing API plus new API in parallel without too much conflict
|
||||
|
||||
int32_t PIOS_FLASHFS_Logfs_Destroy(
|
||||
__attribute__((unused)) uintptr_t fs_id)
|
||||
{
|
||||
pios_DIR *dp;
|
||||
struct pios_dirent *ep;
|
||||
char path[LOGFSPATH_LEN];
|
||||
|
||||
getLogfsPath(fs_id, path);
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_Logfs_Destroy");
|
||||
|
||||
|
||||
dp = pios_opendir(path);
|
||||
if (dp != NULL) {
|
||||
while ((ep = pios_readdir(dp))) {
|
||||
pios_unlink(ep->d_name);
|
||||
}
|
||||
(void)pios_closedir(dp);
|
||||
} else {
|
||||
pios_trace(PIOS_TRACE_ERROR, "Couldn't open the directory %s.", path);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************
|
||||
*
|
||||
* Provide a PIOS_FLASHFS_* driver
|
||||
*
|
||||
*********************************/
|
||||
#include "pios_flashfs.h" /* API for flash filesystem */
|
||||
|
||||
/**
|
||||
* @brief Saves one object instance to the filesystem
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @param[in] obj UAVObject ID of the object to save
|
||||
* @param[in] obj_inst_id The instance number of the object being saved
|
||||
* @param[in] obj_data Contents of the object being saved
|
||||
* @param[in] obj_size Size of the object being saved
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
* @retval -2 if failed to start transaction
|
||||
* @retval -3 if failure to delete any previous versions of the object
|
||||
* @retval -4 if filesystem is entirely full and garbage collection won't help
|
||||
* @retval -5 if garbage collection failed
|
||||
* @retval -6 if filesystem is full even after garbage collection should have freed space
|
||||
* @retval -7 if writing the new object to the filesystem failed
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_ObjSave(
|
||||
__attribute__((unused)) uintptr_t fs_id,
|
||||
uint32_t obj_id,
|
||||
uint16_t obj_inst_id,
|
||||
uint8_t *obj_data,
|
||||
uint16_t obj_size)
|
||||
{
|
||||
int fd;
|
||||
char filename[OBJECTPATHNAME_LEN];
|
||||
uint32_t bytes_written = 0;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_ObjSave");
|
||||
|
||||
// Get filename
|
||||
getObjectPathAndName(fs_id, obj_id, obj_inst_id, filename);
|
||||
|
||||
// Open file
|
||||
fd = pios_open(filename, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_open (%s) retval=%d.", filename, fd);
|
||||
|
||||
if (fd < 0) {
|
||||
pios_trace(PIOS_TRACE_ERROR, "Couldn't open %s", filename);
|
||||
} else {
|
||||
// Append object
|
||||
bytes_written = pios_write(fd, obj_data, obj_size);
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_write bytes_written=%d obj_size=%d", bytes_written, obj_size);
|
||||
|
||||
// Done, close file and unlock
|
||||
pios_close(fd);
|
||||
}
|
||||
|
||||
if (bytes_written != obj_size) {
|
||||
return -7;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load one object instance from the filesystem
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @param[in] obj UAVObject ID of the object to load
|
||||
* @param[in] obj_inst_id The instance of the object to load
|
||||
* @param[in] obj_data Buffer to hold the contents of the loaded object
|
||||
* @param[in] obj_size Size of the object to be loaded
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
* @retval -2 if failed to start transaction
|
||||
* @retval -3 if object not found in filesystem
|
||||
* @retval -4 if object size in filesystem does not exactly match buffer size
|
||||
* @retval -5 if reading the object data from flash fails
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_ObjLoad(
|
||||
__attribute__((unused)) uintptr_t fs_id,
|
||||
uint32_t obj_id,
|
||||
uint16_t obj_inst_id,
|
||||
uint8_t *obj_data,
|
||||
uint16_t obj_size)
|
||||
{
|
||||
int fd;
|
||||
char filename[OBJECTPATHNAME_LEN];
|
||||
uint32_t bytes_read = 0;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_ObjLoad");
|
||||
|
||||
// Get filename
|
||||
getObjectPathAndName(fs_id, obj_id, obj_inst_id, filename);
|
||||
|
||||
fd = pios_open(filename, O_RDONLY, S_IREAD | S_IWRITE);
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_open (%s) retval=%d.", filename, fd);
|
||||
|
||||
if (fd < 0) {
|
||||
pios_trace(PIOS_TRACE_ERROR, "Couldn't open %s", filename);
|
||||
} else {
|
||||
// Load object
|
||||
bytes_read = pios_read(fd, (void *)obj_data, (uint32_t)obj_size);
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_read (%d) expected=%d.", obj_size, bytes_read);
|
||||
|
||||
// Done, close file and unlock
|
||||
pios_close(fd);
|
||||
}
|
||||
|
||||
if (bytes_read != obj_size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete one instance of an object from the filesystem
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @param[in] obj UAVObject ID of the object to delete
|
||||
* @param[in] obj_inst_id The instance of the object to delete
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
* @retval -2 if failed to start transaction
|
||||
* @retval -3 if failed to delete the object from the filesystem
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_ObjDelete(
|
||||
__attribute__((unused)) uintptr_t fs_id,
|
||||
uint32_t obj_id,
|
||||
uint16_t obj_inst_id)
|
||||
{
|
||||
char filename[OBJECTPATHNAME_LEN];
|
||||
int retval;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_ObjDelete");
|
||||
|
||||
// Get filename
|
||||
getObjectPathAndName(fs_id, obj_id, obj_inst_id, filename);
|
||||
|
||||
// Delete file
|
||||
retval = pios_unlink((char *)filename);
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_unlink(%s) retval=%d", filename, retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases all filesystem arenas and activate the first arena
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
* @retval -2 if failed to start transaction
|
||||
* @retval -3 if failed to erase all arenas
|
||||
* @retval -4 if failed to activate arena 0
|
||||
* @retval -5 if failed to mount arena 0
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_Format(
|
||||
__attribute__((unused)) uintptr_t fs_id)
|
||||
{
|
||||
int retval;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_Format");
|
||||
|
||||
// Convert fs_id into device name
|
||||
char devicename[DEVICENAME_LEN];
|
||||
getDeviceName(fs_id, devicename);
|
||||
|
||||
retval = pios_format(devicename,
|
||||
TRUE, // unmount flag
|
||||
TRUE, // force unmount flag
|
||||
TRUE); // remount
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_format (%s) retval=%d.", devicename, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returs stats for the filesystems
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_GetStats(
|
||||
__attribute__((unused)) uintptr_t fs_id,
|
||||
__attribute__((unused)) struct PIOS_FLASHFS_Stats *stats)
|
||||
{
|
||||
// pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_GetStats");
|
||||
|
||||
// Convert fs_id into device name
|
||||
char devicename[DEVICENAME_LEN];
|
||||
|
||||
getDeviceName(fs_id, devicename);
|
||||
|
||||
// Get yaffs statistics for that device
|
||||
stats->num_free_slots = yaffs_freespace(devicename);
|
||||
stats->num_active_slots = yaffs_totalspace(devicename) - stats->num_free_slots;
|
||||
|
||||
// Return device usage statistics
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
@ -558,7 +558,7 @@ void PIOS_RFM22B_SetTxPower(uint32_t rfm22b_id, enum rfm22b_tx_power tx_pwr)
|
||||
|
||||
/**
|
||||
* Sets the range and number of channels to use for the radio.
|
||||
* The channels are 0 to 255 divided across the 430-440 MHz range.
|
||||
* The channels are 0 to 250 divided across the 430-440 MHz range.
|
||||
* The number of channels configured will be spread across the selected channel range.
|
||||
* The channel spacing is 10MHz / 250 = 40kHz
|
||||
*
|
||||
@ -1618,7 +1618,7 @@ static void rfm22_setNominalCarrierFrequency(struct pios_rfm22b_dev *rfm22b_dev,
|
||||
{
|
||||
// Set the frequency channels to start at 430MHz
|
||||
uint32_t frequency_hz = RFM22B_NOMINAL_CARRIER_FREQUENCY;
|
||||
// The step size is 10MHz / 250 channels = 40khz, and the step size is specified in 10khz increments.
|
||||
// The step size is 10MHz / 250 = 40khz, and the step size is specified in 10khz increments.
|
||||
uint8_t freq_hop_step_size = 4;
|
||||
|
||||
// holds the hbsel (1 or 2)
|
||||
|
43
flight/pios/common/pios_trace.c
Normal file
43
flight/pios/common/pios_trace.c
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file pios_trace.c
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @brief PiOS trace debug function
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "pios_trace.h"
|
||||
|
||||
unsigned pios_trace_mask = 0;
|
||||
|
||||
|
||||
unsigned pios_set_trace(unsigned tm)
|
||||
{
|
||||
pios_trace_mask = tm;
|
||||
return pios_trace_mask;
|
||||
}
|
||||
|
||||
unsigned pios_get_trace(void)
|
||||
{
|
||||
return pios_trace_mask;
|
||||
}
|
@ -100,9 +100,9 @@
|
||||
|
||||
#define DSM_CHANNELS_PER_FRAME 7
|
||||
#define DSM_FRAME_LENGTH (1 + 1 + DSM_CHANNELS_PER_FRAME * 2)
|
||||
#define DSM_DSM2_RES_MASK 0x0010
|
||||
#define DSM_2ND_FRAME_MASK 0x8000
|
||||
|
||||
/* lower this value as needed if your satellite requires less delay to enter binding. */
|
||||
#define DSM_BIND_MIN_DELAY_US 90000
|
||||
/*
|
||||
* Include lost frame counter and provide it as a last channel value
|
||||
* for debugging. Currently is not used by the receiver layer.
|
||||
|
@ -34,6 +34,9 @@ struct PIOS_FLASHFS_Stats {
|
||||
uint16_t num_active_slots; /* slots in active state */
|
||||
};
|
||||
|
||||
// define logfs subdirectory of a yaffs flash device
|
||||
#define PIOS_LOGFS_DIR "logfs"
|
||||
|
||||
int32_t PIOS_FLASHFS_Format(uintptr_t fs_id);
|
||||
int32_t PIOS_FLASHFS_ObjSave(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size);
|
||||
int32_t PIOS_FLASHFS_ObjLoad(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size);
|
||||
|
93
flight/pios/inc/pios_stdio.h
Normal file
93
flight/pios/inc/pios_stdio.h
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file pios_stdio.h
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @brief PiOS stdio posix file functions
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef PIOS_STDIO_H
|
||||
#define PIOS_STDIO_H
|
||||
|
||||
#include "yaffsfs.h"
|
||||
|
||||
#define pios_open(path, oflag, mode) yaffs_open(path, oflag, mode)
|
||||
#define pios_close(fd) yaffs_close(fd)
|
||||
#define pios_fsync(fd) yaffs_fsync(fd)
|
||||
#define pios_flush(fd) yaffs_flush(fd)
|
||||
#define pios_read(fd, buf, nbyte) yaffs_read(fd, buf, nbyte)
|
||||
#define pios_write(fd, buf, nbyte) yaffs_write(fd, buf, nbyte)
|
||||
#define pios_fdatasync(fd) yaffs_fdatasync(fd)
|
||||
#define pios_access(path, amode) yaffs_access(path, amode)
|
||||
#define pios_dup(fd) yaffs_dup(fd)
|
||||
#define pios_pread(fd, buf, nbyte, offset) yaffs_pread(fd, buf, nbyte, offset)
|
||||
#define pios_pwrite(fd, buf, nbyte, offset) yaffs_pwrite(fd, buf, nbyte, offset)
|
||||
#define pios_lseek(fd, offset, whence) yaffs_lseek(fd, offset, whence)
|
||||
#define pios_truncate(path, new_size) yaffs_truncate(path, new_size)
|
||||
#define pios_ftruncate(fd, new_size) yaffs_ftruncate(fd, new_size)
|
||||
#define pios_unlink(path) yaffs_unlink(path)
|
||||
#define pios_rename(oldPath, newPath) yaffs_rename(oldPath, newPath)
|
||||
#define pios_stat(path, buf) yaffs_stat(path, buf)
|
||||
#define pios_lstat(path, buf) yaffs_lstat(path, buf)
|
||||
#define pios_fstat(fd, buf) yaffs_fstat(fd, buf)
|
||||
#define pios_utime(path, buf) yaffs_utime(path, buf)
|
||||
#define pios_futime(fd, buf) yaffs_futime(fd, buf)
|
||||
#define pios_setxattr(path, name, data, size, flags) yaffs_setxattr(path, name, data, size, flags)
|
||||
#define pios_lsetxattr(path, name, data, size, flags) yaffs_lsetxattr(path, name, data, size, flags)
|
||||
#define pios_fsetxattr(fd, name, data, size, flags) yaffs_fsetxattr(path, name, data, size, flags)
|
||||
|
||||
#define pios_getxattr(path, name, data, size) yaffs_getxattr(path, name, data, size)
|
||||
#define pios_lgetxattr(path, name, data, size) yaffs_lgetxattr(path, name, data, size)
|
||||
#define pios_fgetxattr(fd, name, data, size) yaffs_fgetxattr(fd, name, data, size)
|
||||
#define pios_removexattr(path, name) yaffs_removexattr(path, name)
|
||||
#define pios_lremoveattr(path, name) yaffs_lremovexattr(path, name)
|
||||
#define pios_fremovexattr(fd, name) yaffs_fremovexattr(fd, name)
|
||||
#define pios_listxattr(path, list, size) yaffs_listxattr(path, list, size)
|
||||
#define pios_llistxattr(path, list, size) yaffs_llistxattr(path, list, size)
|
||||
#define pios_flistxattr(fd, list, size) yaffs_flistxattr(fd, list, size)
|
||||
#define pios_chmod(path, mode) yaffs_chmod(path, mode)
|
||||
#define pios_fchmod(fd, mode) yaffs_fchmod(fd, mode)
|
||||
#define pios_mkdir(path, mode) yaffs_mkdir(path, mode)
|
||||
#define pios_rmdir(path) yaffs_rmdir(path)
|
||||
#define pios_opendir(dirname) yaffs_opendir(dirname)
|
||||
#define pios_readdir(dirp) yaffs_readdir(dirp)
|
||||
#define pios_rewinddir(dirp) yaffs_rewinddir(dirp)
|
||||
#define pios_closedir(dirp) yaffs_closedir(dirp)
|
||||
#define pios_mount(path) yaffs_mount(path)
|
||||
#define pios_mount2(path, read_only) yaffs_mount2(path, read_only)
|
||||
#define pios_mount3(path, read_only, skip_checkpt) yaffs_mount3(path, read_only, skip_checkpt)
|
||||
#define pios_umount(path) yaffs_unmount(path)
|
||||
#define pios_umount2(path, force) yaffs_unmount2(path, force)
|
||||
#define pios_remount(path, force, read_only) yaffs_remount(path, force, read_only)
|
||||
#define pios_format(path, unmount_flag, force_unmount_flag, remount_flag) yaffs_format(path, unmount_flag, force_unmount_flag, remount_flag)
|
||||
#define pios_sync(path) yaffs_sync(path)
|
||||
#define pios_symlink(oldpath, newpath) yaffs_symlink(oldpath, newpath)
|
||||
#define pios_readlink(path, buf, bufsize) yaffs_readlink(path, buf, bufsiz)
|
||||
#define pios_link(oldpath, newpath) yaffs_link(oldpath, newpath)
|
||||
#define pios_mknod(pathname, mode, dev) yaffs_mknod(pathname, mode, dev)
|
||||
#define pios_freespace(path) yaffs_freespace(path)
|
||||
#define pios_totalspace(path) yaffs_totalspace(path)
|
||||
|
||||
#define pios_DIR yaffs_DIR
|
||||
#define pios_dirent yaffs_dirent
|
||||
|
||||
|
||||
#endif /* PIOS_STDIO_H */
|
84
flight/pios/inc/pios_trace.h
Normal file
84
flight/pios/inc/pios_trace.h
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file pios_trace.h
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @brief PiOS debug trace printing interface
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef PIOS_TRACE_H
|
||||
#define PIOS_TRACE_H
|
||||
|
||||
extern unsigned int pios_trace_mask;
|
||||
|
||||
/*
|
||||
* Tracing flags.
|
||||
* The flags masked in PIOS_TRACE_ALWAYS are always traced.
|
||||
*/
|
||||
|
||||
#define PIOS_TRACE_OS 0x00000002
|
||||
#define PIOS_TRACE_ALLOCATE 0x00000004
|
||||
#define PIOS_TRACE_TEST 0x00000008
|
||||
#define PIOS_TRACE_BAD_BLOCKS 0x00000010
|
||||
#define PIOS_TRACE_ERASE 0x00000020
|
||||
#define PIOS_TRACE_GC 0x00000040
|
||||
#define PIOS_TRACE_WRITE 0x00000080
|
||||
#define PIOS_TRACE_TRACING 0x00000100
|
||||
#define PIOS_TRACE_DELETION 0x00000200
|
||||
#define PIOS_TRACE_BUFFERS 0x00000400
|
||||
#define PIOS_TRACE_NANDACCESS 0x00000800
|
||||
#define PIOS_TRACE_GC_DETAIL 0x00001000
|
||||
#define PIOS_TRACE_SCAN_DEBUG 0x00002000
|
||||
#define PIOS_TRACE_MTD 0x00004000
|
||||
#define PIOS_TRACE_CHECKPOINT 0x00008000
|
||||
|
||||
#define PIOS_TRACE_VERIFY 0x00010000
|
||||
#define PIOS_TRACE_VERIFY_NAND 0x00020000
|
||||
#define PIOS_TRACE_VERIFY_FULL 0x00040000
|
||||
#define PIOS_TRACE_VERIFY_ALL 0x000f0000
|
||||
|
||||
#define PIOS_TRACE_SYNC 0x00100000
|
||||
#define PIOS_TRACE_BACKGROUND 0x00200000
|
||||
#define PIOS_TRACE_LOCK 0x00400000
|
||||
#define PIOS_TRACE_MOUNT 0x00800000
|
||||
|
||||
#define PIOS_TRACE_ERROR 0x40000000
|
||||
#define PIOS_TRACE_BUG 0x80000000
|
||||
#define PIOS_TRACE_ALWAYS 0xf0000000
|
||||
|
||||
|
||||
#ifdef PIOS_TRACE
|
||||
#define pios_trace(msk, fmt, ...) \
|
||||
do { \
|
||||
if (pios_trace_mask & (msk)) { \
|
||||
printf("pios_trace: " fmt "\n",##__VA_ARGS__); } \
|
||||
} \
|
||||
while (0)
|
||||
#else
|
||||
#define pios_trace(msk, fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Trace control functions */
|
||||
unsigned pios_set_trace(unsigned tm);
|
||||
unsigned pios_get_trace(void);
|
||||
|
||||
|
||||
#endif // ifndef PIOS_TRACE_H
|
27
flight/pios/posix/libraries/yaffs2/yaffs_impl.mk
Normal file
27
flight/pios/posix/libraries/yaffs2/yaffs_impl.mk
Normal file
@ -0,0 +1,27 @@
|
||||
#
|
||||
# Rules to add yaffs2 to the PiOS target
|
||||
#
|
||||
#
|
||||
# Note that the PIOS target-specific makefile will detect that YAFFS2_DIR
|
||||
# has been defined and add in the target-specific pieces separately.
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Directory containing this makefile
|
||||
#
|
||||
YAFFS2_SIMPOSIX_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
|
||||
# Compiler options
|
||||
#
|
||||
CDEFS += -DCONFIG_YAFFS_DIRECT
|
||||
CDEFS += -DCONFIG_YAFFS_DEFINES_TYPES
|
||||
CDEFS += -DCONFIG_YAFFS_PROVIDE_DEFS
|
||||
CDEFS += -DCONFIG_YAFFSFS_PROVIDE_VALUES
|
||||
#ARCHFLAGS += -DARCH_POSIX
|
||||
|
||||
#
|
||||
# Yaffs2 device library source and includes
|
||||
#
|
||||
SRC += $(sort $(wildcard $(YAFFS2_SIMPOSIX_DIR)*.c))
|
||||
#EXTRAINCDIRS += $(YAFFS2_DIR)/inc
|
349
flight/pios/posix/libraries/yaffs2/yaffs_nor_drv.c
Normal file
349
flight/pios/posix/libraries/yaffs2/yaffs_nor_drv.c
Normal file
@ -0,0 +1,349 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is an interface module for handling NOR in yaffs1 mode.
|
||||
*/
|
||||
|
||||
/* This code is intended to be used with "regular" NOR that is bit-modifyable.
|
||||
*
|
||||
* Each "chunk" is a contguous area of 512 + 16 bytes.
|
||||
* This makes 248 such chunks with some space left over where a format markerr
|
||||
* is stored.
|
||||
*/
|
||||
|
||||
#include "yaffs_nor_drv.h"
|
||||
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
#include "yaffs_flashif.h"
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
/* Tunable data */
|
||||
#define DATA_BYTES_PER_CHUNK 512
|
||||
#define SPARE_BYTES_PER_CHUNK 16
|
||||
#define BLOCK_SIZE_IN_BYTES (128 * 1024)
|
||||
#define BYTES_IN_DEVICE (4 * 1024 * 1024)
|
||||
|
||||
#define BYTES_PER_CHUNK (DATA_BYTES_PER_CHUNK + SPARE_BYTES_PER_CHUNK)
|
||||
#define SPARE_AREA_OFFSET DATA_BYTES_PER_CHUNK
|
||||
#define CHUNKS_PER_BLOCK (BLOCK_SIZE_IN_BYTES / BYTES_PER_CHUNK)
|
||||
|
||||
#define BLOCKS_IN_DEVICE (BYTES_IN_DEVICE / BLOCK_SIZE_IN_BYTES)
|
||||
|
||||
#define YNOR_PREMARKER (0xF6)
|
||||
#define YNOR_POSTMARKER (0xF0)
|
||||
|
||||
#define FORMAT_OFFSET (CHUNKS_PER_BLOCK * BYTES_PER_CHUNK)
|
||||
#define FORMAT_VALUE 0x1234
|
||||
|
||||
#if 1
|
||||
|
||||
/* Compile this for a simulation */
|
||||
#include "ynorsim.h"
|
||||
|
||||
static struct nor_sim *nor_sim;
|
||||
|
||||
#define nor_drv_FlashInit() \
|
||||
do { nor_sim = ynorsim_initialise("emfile-nor", BLOCKS_IN_DEVICE, BLOCK_SIZE_IN_BYTES); } \
|
||||
while (0)
|
||||
#define nor_drv_FlashDeinit() ynorsim_shutdown(nor_sim)
|
||||
#define nor_drv_FlashWrite32(addr, buf, nwords) ynorsim_wr32(nor_sim, addr, buf, nwords)
|
||||
#define nor_drv_FlashRead32(addr, buf, nwords) ynorsim_rd32(nor_sim, addr, buf, nwords)
|
||||
#define nor_drv_FlashEraseBlock(addr) ynorsim_erase(nor_sim, addr)
|
||||
#define DEVICE_BASE ynorsim_get_base(nor_sim)
|
||||
|
||||
#else
|
||||
|
||||
/* Compile this to hook up to read hardware */
|
||||
#include "../blob/yflashrw.h"
|
||||
#define nor_drv_FlashInit() \
|
||||
do {} \
|
||||
while (0)
|
||||
#define nor_drv_FlashDeinit() \
|
||||
do {} \
|
||||
while (0)
|
||||
#define nor_drv_FlashWrite32(addr, buf, nwords) Y_FlashWrite(addr, buf, nwords)
|
||||
#define nor_drv_FlashRead32(addr, buf, nwords) Y_FlashRead(addr, buf, nwords)
|
||||
#define nor_drv_FlashEraseBlock(addr) Y_FlashErase(addr, BLOCK_SIZE_IN_BYTES)
|
||||
#define DEVICE_BASE (32 * 1024 * 1024)
|
||||
#endif /* if 1 */
|
||||
|
||||
|
||||
static u32 *Block2Addr(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u8 *addr;
|
||||
|
||||
dev = dev;
|
||||
|
||||
addr = (u8 *)DEVICE_BASE;
|
||||
addr += blockNumber * BLOCK_SIZE_IN_BYTES;
|
||||
|
||||
return (u32 *)addr;
|
||||
}
|
||||
|
||||
static u32 *Block2FormatAddr(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u8 *addr;
|
||||
|
||||
addr = (u8 *)Block2Addr(dev, blockNumber);
|
||||
addr += FORMAT_OFFSET;
|
||||
|
||||
return (u32 *)addr;
|
||||
}
|
||||
|
||||
static u32 *Chunk2DataAddr(struct yaffs_dev *dev, int chunk_id)
|
||||
{
|
||||
unsigned block;
|
||||
unsigned chunkInBlock;
|
||||
u8 *addr;
|
||||
|
||||
block = chunk_id / dev->param.chunks_per_block;
|
||||
chunkInBlock = chunk_id % dev->param.chunks_per_block;
|
||||
|
||||
addr = (u8 *)Block2Addr(dev, block);
|
||||
addr += chunkInBlock * BYTES_PER_CHUNK;
|
||||
|
||||
return (u32 *)addr;
|
||||
}
|
||||
|
||||
static u32 *Chunk2SpareAddr(struct yaffs_dev *dev, int chunk_id)
|
||||
{
|
||||
u8 *addr;
|
||||
|
||||
addr = (u8 *)Chunk2DataAddr(dev, chunk_id);
|
||||
addr += SPARE_AREA_OFFSET;
|
||||
return (u32 *)addr;
|
||||
}
|
||||
|
||||
|
||||
static void nor_drv_AndBytes(u8 *target, const u8 *src, int nbytes)
|
||||
{
|
||||
while (nbytes > 0) {
|
||||
*target &= *src;
|
||||
target++;
|
||||
src++;
|
||||
nbytes--;
|
||||
}
|
||||
}
|
||||
|
||||
static int nor_drv_WriteChunkToNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, int data_len,
|
||||
const u8 *oob, int oob_len)
|
||||
{
|
||||
u32 *dataAddr = Chunk2DataAddr(dev, nand_chunk);
|
||||
u32 *spareAddr = Chunk2SpareAddr(dev, nand_chunk);
|
||||
|
||||
struct yaffs_spare *spare = (struct yaffs_spare *)oob;
|
||||
struct yaffs_spare tmpSpare;
|
||||
|
||||
(void)oob_len;
|
||||
|
||||
/* We should only be getting called for one of 3 reasons:
|
||||
* Writing a chunk: data and spare will not be NULL
|
||||
* Writing a deletion marker: data will be NULL, spare not NULL
|
||||
* Writing a bad block marker: data will be NULL, spare not NULL
|
||||
*/
|
||||
|
||||
if (sizeof(struct yaffs_spare) != SPARE_BYTES_PER_CHUNK) {
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (data && oob) {
|
||||
if (spare->page_status != 0xff) {
|
||||
BUG();
|
||||
}
|
||||
/* Write a pre-marker */
|
||||
memset(&tmpSpare, 0xff, sizeof(tmpSpare));
|
||||
tmpSpare.page_status = YNOR_PREMARKER;
|
||||
nor_drv_FlashWrite32(spareAddr, (u32 *)&tmpSpare, sizeof(struct yaffs_spare) / sizeof(u32));
|
||||
|
||||
/* Write the data */
|
||||
nor_drv_FlashWrite32(dataAddr, (u32 *)data, data_len / sizeof(u32));
|
||||
|
||||
memcpy(&tmpSpare, spare, sizeof(struct yaffs_spare));
|
||||
|
||||
/* Write the real tags, but override the premarker*/
|
||||
tmpSpare.page_status = YNOR_PREMARKER;
|
||||
nor_drv_FlashWrite32(spareAddr, (u32 *)&tmpSpare, sizeof(struct yaffs_spare) / sizeof(u32));
|
||||
|
||||
/* Write a post-marker */
|
||||
tmpSpare.page_status = YNOR_POSTMARKER;
|
||||
nor_drv_FlashWrite32(spareAddr, (u32 *)&tmpSpare, sizeof(tmpSpare) / sizeof(u32));
|
||||
} else if (spare) {
|
||||
/* This has to be a read-modify-write operation to handle NOR-ness */
|
||||
|
||||
nor_drv_FlashRead32(spareAddr, (u32 *)&tmpSpare, sizeof(struct yaffs_spare) / sizeof(u32));
|
||||
|
||||
nor_drv_AndBytes((u8 *)&tmpSpare, (u8 *)spare, sizeof(struct yaffs_spare));
|
||||
|
||||
nor_drv_FlashWrite32(spareAddr, (u32 *)&tmpSpare, sizeof(struct yaffs_spare) / sizeof(u32));
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int nor_drv_ReadChunkFromNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, int data_len,
|
||||
u8 *oob, int oob_len,
|
||||
enum yaffs_ecc_result *ecc_result)
|
||||
{
|
||||
struct yaffs_spare *spare = (struct yaffs_spare *)oob;
|
||||
|
||||
u32 *dataAddr = Chunk2DataAddr(dev, nand_chunk);
|
||||
u32 *spareAddr = Chunk2SpareAddr(dev, nand_chunk);
|
||||
|
||||
if (data) {
|
||||
nor_drv_FlashRead32(dataAddr, (u32 *)data, dev->param.total_bytes_per_chunk / sizeof(u32));
|
||||
}
|
||||
|
||||
if (oob) {
|
||||
nor_drv_FlashRead32(spareAddr, (u32 *)spare, oob_len / sizeof(u32));
|
||||
|
||||
/* If the page status is YNOR_POSTMARKER then it was written properly
|
||||
* so change that to 0xFF so that the rest of yaffs is happy.
|
||||
*/
|
||||
if (spare->page_status == YNOR_POSTMARKER) {
|
||||
spare->page_status = 0xff;
|
||||
} else if (spare->page_status != 0xff &&
|
||||
(spare->page_status | YNOR_PREMARKER) != 0xff) {
|
||||
spare->page_status = YNOR_PREMARKER;
|
||||
}
|
||||
}
|
||||
|
||||
if (ecc_result) {
|
||||
*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
|
||||
static int nor_drv_FormatBlock(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u32 *blockAddr = Block2Addr(dev, blockNumber);
|
||||
u32 *formatAddr = Block2FormatAddr(dev, blockNumber);
|
||||
u32 formatValue = FORMAT_VALUE;
|
||||
|
||||
nor_drv_FlashEraseBlock(blockAddr);
|
||||
nor_drv_FlashWrite32(formatAddr, &formatValue, 1);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int nor_drv_UnformatBlock(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u32 *formatAddr = Block2FormatAddr(dev, blockNumber);
|
||||
u32 formatValue = 0;
|
||||
|
||||
nor_drv_FlashWrite32(formatAddr, &formatValue, 1);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int nor_drv_IsBlockFormatted(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u32 *formatAddr = Block2FormatAddr(dev, blockNumber);
|
||||
u32 formatValue;
|
||||
|
||||
|
||||
nor_drv_FlashRead32(formatAddr, &formatValue, 1);
|
||||
|
||||
return formatValue == FORMAT_VALUE;
|
||||
}
|
||||
|
||||
static int nor_drv_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
if (blockNumber < 0 || blockNumber >= BLOCKS_IN_DEVICE) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Attempt to erase non-existant block %d\n",
|
||||
blockNumber);
|
||||
return YAFFS_FAIL;
|
||||
} else {
|
||||
nor_drv_UnformatBlock(dev, blockNumber);
|
||||
nor_drv_FormatBlock(dev, blockNumber);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static int nor_drv_InitialiseNAND(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
nor_drv_FlashInit();
|
||||
/* Go through the blocks formatting them if they are not formatted */
|
||||
for (i = dev->param.start_block; i <= dev->param.end_block; i++) {
|
||||
if (!nor_drv_IsBlockFormatted(dev, i)) {
|
||||
nor_drv_FormatBlock(dev, i);
|
||||
}
|
||||
}
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int nor_drv_Deinitialise_flash_fn(struct yaffs_dev *dev)
|
||||
{
|
||||
dev = dev;
|
||||
|
||||
nor_drv_FlashDeinit();
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
|
||||
struct yaffs_dev *yaffs_nor_install_drv(const char *name)
|
||||
{
|
||||
struct yaffs_dev *dev = malloc(sizeof(struct yaffs_dev));
|
||||
char *name_copy = strdup(name);
|
||||
struct yaffs_param *param;
|
||||
struct yaffs_driver *drv;
|
||||
|
||||
|
||||
if (!dev || !name_copy) {
|
||||
free(name_copy);
|
||||
free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
param = &dev->param;
|
||||
drv = &dev->drv;
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
|
||||
param->name = name_copy;
|
||||
|
||||
param->total_bytes_per_chunk = DATA_BYTES_PER_CHUNK;
|
||||
param->chunks_per_block = CHUNKS_PER_BLOCK;
|
||||
param->n_reserved_blocks = 2;
|
||||
param->start_block = 0; // Can use block 0
|
||||
param->end_block = BLOCKS_IN_DEVICE - 1; // Last block
|
||||
param->use_nand_ecc = 0; // use YAFFS's ECC
|
||||
param->disable_soft_del = 1;
|
||||
|
||||
drv->drv_write_chunk_fn = nor_drv_WriteChunkToNAND;
|
||||
drv->drv_read_chunk_fn = nor_drv_ReadChunkFromNAND;
|
||||
drv->drv_erase_fn = nor_drv_EraseBlockInNAND;
|
||||
drv->drv_initialise_fn = nor_drv_InitialiseNAND;
|
||||
drv->drv_deinitialise_fn = nor_drv_Deinitialise_flash_fn;
|
||||
|
||||
param->n_caches = 10;
|
||||
param->disable_soft_del = 1;
|
||||
|
||||
dev->driver_context = (void *)nor_sim;
|
||||
|
||||
yaffs_add_device(dev);
|
||||
|
||||
return NULL;
|
||||
}
|
23
flight/pios/posix/libraries/yaffs2/yaffs_nor_drv.h
Normal file
23
flight/pios/posix/libraries/yaffs2/yaffs_nor_drv.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YAFFS_NOR_DRV_H__
|
||||
#define __YAFFS_NOR_DRV_H__
|
||||
|
||||
struct yaffs_dev;
|
||||
struct yaffs_dev *yaffs_nor_install_drv(const char *name);
|
||||
|
||||
#endif
|
221
flight/pios/posix/libraries/yaffs2/yaffs_osglue.c
Normal file
221
flight/pios/posix/libraries/yaffs2/yaffs_osglue.c
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Example OS glue functions for running on a Linux/POSIX system.
|
||||
*/
|
||||
|
||||
#include "yaffscfg.h"
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffsfs.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include <assert.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pios_trace.h"
|
||||
#include "yaffsfs.h"
|
||||
|
||||
/*
|
||||
* yaffsfs_SetError() and yaffsfs_GetError()
|
||||
* Do whatever to set the system error.
|
||||
* yaffsfs_GetError() just fetches the last error.
|
||||
*/
|
||||
|
||||
static int yaffsfs_lastError;
|
||||
|
||||
void yaffsfs_SetError(int err)
|
||||
{
|
||||
// Do whatever to set error
|
||||
yaffsfs_lastError = err;
|
||||
errno = err;
|
||||
pios_trace(PIOS_TRACE_ERROR, "yaffsfs_SetError(%d) %s", err, yaffs_error_to_str(err));
|
||||
}
|
||||
|
||||
int yaffsfs_GetLastError(void)
|
||||
{
|
||||
return yaffsfs_lastError;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_CheckMemRegion()
|
||||
* Check that access to an address is valid.
|
||||
* This can check memory is in bounds and is writable etc.
|
||||
*
|
||||
* Returns 0 if ok, negative if not.
|
||||
*/
|
||||
int yaffsfs_CheckMemRegion(const void *addr, size_t size, int write_request)
|
||||
{
|
||||
if (!addr) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_Lock()
|
||||
* yaffsfs_Unlock()
|
||||
* A single mechanism to lock and unlock yaffs. Hook up to a mutex or whatever.
|
||||
* Here are two examples, one using POSIX pthreads, the other doing nothing.
|
||||
*
|
||||
* If we use pthreads then we also start a background gc thread.
|
||||
*/
|
||||
|
||||
#if 1
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t mutex1;
|
||||
static pthread_t bc_gc_thread;
|
||||
|
||||
void yaffsfs_Lock(void)
|
||||
{
|
||||
pthread_mutex_lock(&mutex1);
|
||||
}
|
||||
|
||||
void yaffsfs_Unlock(void)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex1);
|
||||
}
|
||||
|
||||
static void *bg_gc_func(void *dummy)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
int urgent = 0;
|
||||
int result;
|
||||
int next_urgent;
|
||||
|
||||
/* Sleep for a bit to allow start up */
|
||||
sleep(2);
|
||||
|
||||
|
||||
while (1) {
|
||||
/* Iterate through devices, do bg gc updating ungency */
|
||||
yaffs_dev_rewind();
|
||||
next_urgent = 0;
|
||||
|
||||
while ((dev = yaffs_next_dev()) != NULL) {
|
||||
result = yaffs_do_background_gc_reldev(dev, urgent);
|
||||
if (result > 0) {
|
||||
next_urgent = 1;
|
||||
}
|
||||
}
|
||||
|
||||
urgent = next_urgent;
|
||||
|
||||
if (next_urgent) {
|
||||
sleep(1);
|
||||
} else {
|
||||
sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't ever return. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void yaffsfs_LockInit(void)
|
||||
{
|
||||
/* Initialise lock */
|
||||
pthread_mutex_init(&mutex1, NULL);
|
||||
|
||||
/* Sneak in starting a background gc thread too */
|
||||
// pthread_create(&bc_gc_thread, NULL, bg_gc_func, NULL);
|
||||
}
|
||||
|
||||
#else /* if 1 */
|
||||
|
||||
void yaffsfs_Lock(void)
|
||||
{}
|
||||
|
||||
void yaffsfs_Unlock(void)
|
||||
{}
|
||||
|
||||
void yaffsfs_LockInit(void)
|
||||
{}
|
||||
#endif /* if 1 */
|
||||
|
||||
/*
|
||||
* yaffsfs_CurrentTime() retrns a 32-bit timestamp.
|
||||
*
|
||||
* Can return 0 if your system does not care about time.
|
||||
*/
|
||||
|
||||
u32 yaffsfs_CurrentTime(void)
|
||||
{
|
||||
return time(NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* yaffsfs_malloc()
|
||||
* yaffsfs_free()
|
||||
*
|
||||
* Functions to allocate and free memory.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_YAFFS_TEST_MALLOC
|
||||
|
||||
static int yaffs_kill_alloc = 0;
|
||||
static size_t total_malloced = 0;
|
||||
static size_t malloc_limit = 0 & 6000000;
|
||||
|
||||
void *yaffsfs_malloc(size_t size)
|
||||
{
|
||||
void *this;
|
||||
|
||||
if (yaffs_kill_alloc) {
|
||||
return NULL;
|
||||
}
|
||||
if (malloc_limit && malloc_limit < (total_malloced + size)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this = malloc(size);
|
||||
if (this) {
|
||||
total_malloced += size;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
#else /* ifdef CONFIG_YAFFS_TEST_MALLOC */
|
||||
|
||||
void *yaffsfs_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
#endif /* ifdef CONFIG_YAFFS_TEST_MALLOC */
|
||||
|
||||
void yaffsfs_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void yaffsfs_OSInitialisation(void)
|
||||
{
|
||||
yaffsfs_LockInit();
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_bug_fn()
|
||||
* Function to report a bug.
|
||||
*/
|
||||
|
||||
void yaffs_bug_fn(const char *file_name, int line_no)
|
||||
{
|
||||
printf("yaffs bug detected %s:%d\n",
|
||||
file_name, line_no);
|
||||
assert(0);
|
||||
}
|
192
flight/pios/posix/libraries/yaffs2/yaffscfg2k.c
Normal file
192
flight/pios/posix/libraries/yaffs2/yaffscfg2k.c
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* yaffscfg2k.c The configuration for the "direct" use of yaffs.
|
||||
*
|
||||
* This file is intended to be modified to your requirements.
|
||||
* There is no need to redistribute this file.
|
||||
*/
|
||||
|
||||
#include "yaffscfg.h"
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffsfs.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_osglue.h"
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
#include "pios.h"
|
||||
#include "pios_trace.h"
|
||||
#include "pios_stdio.h"
|
||||
|
||||
#include <openpilot.h>
|
||||
#include "pios_flashfs.h" /* API for flash filesystem */
|
||||
#include "pios_flashfs_logfs_priv.h"
|
||||
|
||||
#include <pios_stdio.h>
|
||||
|
||||
|
||||
unsigned yaffs_trace_mask = 0;
|
||||
// YAFFS_TRACE_ERROR |
|
||||
// YAFFS_TRACE_BUG |
|
||||
// YAFFS_TRACE_ALWAYS |
|
||||
// 0;
|
||||
|
||||
int random_seed;
|
||||
int simulate_power_failure = 0;
|
||||
static unsigned int pios_flash_device_count = 0;
|
||||
|
||||
|
||||
/* Configure the devices that will be used */
|
||||
|
||||
#include "yaffs_nor_drv.h"
|
||||
|
||||
// called first before device device driver setup so need to change
|
||||
int yaffs_start_up(void)
|
||||
{
|
||||
static int start_up_called = 0;
|
||||
|
||||
if (start_up_called) {
|
||||
return 0;
|
||||
}
|
||||
start_up_called = 1;
|
||||
|
||||
/* Call the OS initialisation (eg. set up lock semaphore */
|
||||
yaffsfs_OSInitialisation();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void yaffsSigHandler(int sig)
|
||||
{
|
||||
char devicename[8];
|
||||
int fs_id;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "yaffsSigHandler sig=%d", sig);
|
||||
switch (sig) {
|
||||
case SIGQUIT:
|
||||
case SIGTERM:
|
||||
case SIGKILL:
|
||||
case SIGINT:
|
||||
|
||||
for (fs_id = 0; fs_id < pios_flash_device_count; fs_id++) {
|
||||
snprintf(devicename, 6, "/dev%01u", (unsigned)fs_id);
|
||||
|
||||
pios_umount((const char *)devicename);
|
||||
}
|
||||
pios_flash_device_count = 0;
|
||||
exit(1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffsSigSetup(
|
||||
void (*sighandler)(int sig))
|
||||
{
|
||||
// sigset_t block_sigusr;
|
||||
struct sigaction sa;
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = sighandler;
|
||||
if (sigaction(SIGQUIT, &sa, NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = sighandler;
|
||||
if (sigaction(SIGINT, &sa, NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = sighandler;
|
||||
if (sigaction(SIGTERM, &sa, NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = sighandler;
|
||||
if (sigaction(SIGKILL, &sa, NULL)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the flash object setting FS. Each call creates a yaffs device
|
||||
* @return 0 if success, -1 if failure
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_Logfs_Init(
|
||||
__attribute__((unused)) uintptr_t *fs_id, // return identifier for fs device
|
||||
__attribute__((unused)) const struct flashfs_logfs_cfg *cfg, // optional - if flash
|
||||
__attribute__((unused)) const struct pios_flash_driver *driver, // optional - if flash
|
||||
__attribute__((unused)) uintptr_t flash_id) // optional - if flash
|
||||
{
|
||||
int retval;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_Logfs_Init");
|
||||
char devicename[8];
|
||||
char logfs_path[12];
|
||||
|
||||
// Initialise the yaffs OS subsystem
|
||||
if (yaffs_start_up()) {
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
|
||||
// Allocate the next device id
|
||||
*fs_id = pios_flash_device_count;
|
||||
pios_flash_device_count++;
|
||||
|
||||
snprintf(devicename, 6, "/dev%01u", (unsigned)*fs_id);
|
||||
|
||||
// Simposix implementation uses a ram nor simulation which can be installed
|
||||
// as multiple instances
|
||||
yaffs_nor_install_drv(devicename);
|
||||
|
||||
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
yaffsSigSetup(yaffsSigHandler);
|
||||
|
||||
// Attempt to mount the device
|
||||
retval = pios_mount(devicename);
|
||||
if (retval < 0) {
|
||||
pios_trace(PIOS_TRACE_ERROR, "Couldn't mount %s", devicename);
|
||||
} else {
|
||||
// Create a "logfs" directory on each yaffs device for use by the
|
||||
// pios_logfs API.
|
||||
snprintf(logfs_path, 12, "%s/%s", devicename, PIOS_LOGFS_DIR);
|
||||
|
||||
// Create the logfs directory if it does not already exist
|
||||
retval = pios_mkdir(logfs_path, O_CREAT);
|
||||
if (retval < 0) {
|
||||
pios_trace(PIOS_TRACE_ERROR, "Couldn't mkdir %s", logfs_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return retval;
|
||||
}
|
213
flight/pios/posix/libraries/yaffs2/ynorsim.c
Normal file
213
flight/pios/posix/libraries/yaffs2/ynorsim.c
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "ynorsim.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "pios_trace.h"
|
||||
|
||||
#define YNORSIM_FNAME "emfile-nor"
|
||||
|
||||
/* Set YNORSIM_BIT_CHANGES to a a value from 1..30 to
|
||||
* simulate bit flipping as the programming happens.
|
||||
* A low value results in faster simulation with less chance of encountering a partially programmed
|
||||
* word.
|
||||
*/
|
||||
|
||||
// #define YNORSIM_BIT_CHANGES 15
|
||||
#define YNORSIM_BIT_CHANGES 2
|
||||
|
||||
#if 0
|
||||
/* Simulate 32MB of flash in 256k byte blocks.
|
||||
* This stuff is x32.
|
||||
*/
|
||||
|
||||
#define YNORSIM_BLOCK_SIZE_U32 (256 * 1024 / 4)
|
||||
#define YNORSIM_DEV_SIZE_U32 (32 * 1024 * 1024 / 4)
|
||||
#else
|
||||
/* Simulate 8MB of flash in 256k byte blocks.
|
||||
* This stuff is x32.
|
||||
*/
|
||||
|
||||
#define YNORSIM_BLOCK_SIZE_U32 (256 * 1024 / 4)
|
||||
#define YNORSIM_DEV_SIZE_U32 (8 * 1024 * 1024 / 4)
|
||||
#endif
|
||||
|
||||
struct nor_sim {
|
||||
int n_blocks;
|
||||
int block_size_bytes;
|
||||
int file_size;
|
||||
u32 *word;
|
||||
int initialised;
|
||||
char *fname;
|
||||
int remaining_ops;
|
||||
int nops_so_far;
|
||||
};
|
||||
|
||||
int ops_multiplier = 500;
|
||||
extern int random_seed;
|
||||
extern int simulate_power_failure;
|
||||
|
||||
static void NorError(struct nor_sim *sim)
|
||||
{
|
||||
printf("Nor error on device %s\n", sim->fname);
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
static void ynorsim_save_image(struct nor_sim *sim)
|
||||
{
|
||||
int h;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "ynorsim_save_image");
|
||||
h = open(sim->fname, O_RDWR | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
|
||||
write(h, sim->word, sim->file_size);
|
||||
close(h);
|
||||
}
|
||||
|
||||
static void ynorsim_restore_image(struct nor_sim *sim)
|
||||
{
|
||||
int h;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "ynorsim_restore_image");
|
||||
h = open(sim->fname, O_RDONLY, S_IREAD | S_IWRITE);
|
||||
memset(sim->word, 0xFF, sim->file_size);
|
||||
read(h, sim->word, sim->file_size);
|
||||
close(h);
|
||||
}
|
||||
|
||||
static void ynorsim_power_fail(struct nor_sim *sim)
|
||||
{
|
||||
ynorsim_save_image(sim);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void ynorsim_maybe_power_fail(struct nor_sim *sim)
|
||||
{
|
||||
sim->nops_so_far++;
|
||||
sim->remaining_ops--;
|
||||
if (simulate_power_failure && sim->remaining_ops < 1) {
|
||||
printf("Simulated power failure after %d operations\n",
|
||||
sim->nops_so_far);
|
||||
ynorsim_power_fail(sim);
|
||||
}
|
||||
}
|
||||
|
||||
static void ynorsim_ready(struct nor_sim *sim)
|
||||
{
|
||||
if (sim->initialised) {
|
||||
return;
|
||||
}
|
||||
srand(random_seed);
|
||||
sim->remaining_ops = 1000000000;
|
||||
sim->remaining_ops =
|
||||
(rand() % 10000) * ops_multiplier * YNORSIM_BIT_CHANGES;
|
||||
ynorsim_restore_image(sim);
|
||||
sim->initialised = 1;
|
||||
}
|
||||
|
||||
/* Public functions. */
|
||||
|
||||
void ynorsim_rd32(struct nor_sim *sim, u32 *addr, u32 *buf, int nwords)
|
||||
{
|
||||
sim = sim;
|
||||
while (nwords > 0) {
|
||||
*buf = *addr;
|
||||
buf++;
|
||||
addr++;
|
||||
nwords--;
|
||||
}
|
||||
}
|
||||
|
||||
void ynorsim_wr_one_word32(struct nor_sim *sim, u32 *addr, u32 val)
|
||||
{
|
||||
u32 tmp;
|
||||
u32 m;
|
||||
int i;
|
||||
|
||||
tmp = *addr;
|
||||
if (val & ~tmp) {
|
||||
/* Fail due to trying to change a zero into a 1 */
|
||||
printf("attempt to set a zero to one (%x)->(%x)\n", tmp, val);
|
||||
NorError(sim);
|
||||
}
|
||||
|
||||
for (i = 0; i < YNORSIM_BIT_CHANGES; i++) {
|
||||
m = 1 << (rand() & 31);
|
||||
if (!(m & val)) {
|
||||
tmp &= ~m;
|
||||
*addr = tmp;
|
||||
ynorsim_maybe_power_fail(sim);
|
||||
}
|
||||
}
|
||||
|
||||
*addr = tmp & val;
|
||||
ynorsim_maybe_power_fail(sim);
|
||||
}
|
||||
|
||||
void ynorsim_wr32(struct nor_sim *sim, u32 *addr, u32 *buf, int nwords)
|
||||
{
|
||||
while (nwords > 0) {
|
||||
ynorsim_wr_one_word32(sim, addr, *buf);
|
||||
addr++;
|
||||
buf++;
|
||||
nwords--;
|
||||
}
|
||||
}
|
||||
|
||||
void ynorsim_erase(struct nor_sim *sim, u32 *addr)
|
||||
{
|
||||
/* Todo... bit flipping */
|
||||
pios_trace(PIOS_TRACE_TEST, "ynorsim_erase");
|
||||
memset(addr, 0xFF, sim->block_size_bytes);
|
||||
}
|
||||
|
||||
struct nor_sim *ynorsim_initialise(char *name, int n_blocks,
|
||||
int block_size_bytes)
|
||||
{
|
||||
struct nor_sim *sim;
|
||||
|
||||
sim = malloc(sizeof(*sim));
|
||||
if (!sim) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(sim, 0, sizeof(*sim));
|
||||
sim->n_blocks = n_blocks;
|
||||
sim->block_size_bytes = block_size_bytes;
|
||||
sim->file_size = n_blocks * block_size_bytes;
|
||||
sim->word = malloc(sim->file_size);
|
||||
sim->fname = strdup(name);
|
||||
|
||||
if (!sim->word) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ynorsim_ready(sim);
|
||||
return sim;
|
||||
}
|
||||
|
||||
void ynorsim_shutdown(struct nor_sim *sim)
|
||||
{
|
||||
ynorsim_save_image(sim);
|
||||
sim->initialised = 0;
|
||||
}
|
||||
|
||||
u32 *ynorsim_get_base(struct nor_sim *sim)
|
||||
{
|
||||
return sim->word;
|
||||
}
|
30
flight/pios/posix/libraries/yaffs2/ynorsim.h
Normal file
30
flight/pios/posix/libraries/yaffs2/ynorsim.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __Y_NORSIM_H__
|
||||
#define __Y_NORSIM_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
struct nor_sim;
|
||||
|
||||
void ynorsim_rd32(struct nor_sim *sim, u32 *addr, u32 *data, int nwords);
|
||||
void ynorsim_wr32(struct nor_sim *sim, u32 *addr, u32 *data, int nwords);
|
||||
void ynorsim_erase(struct nor_sim *sim, u32 *addr);
|
||||
void ynorsim_shutdown(struct nor_sim *sim);
|
||||
struct nor_sim *ynorsim_initialise(char *name, int n_blocks, int block_size_bytes);
|
||||
u32 *ynorsim_get_base(struct nor_sim *sim);
|
||||
|
||||
#endif
|
@ -180,7 +180,7 @@ static int PIOS_DSM_UnrollChannels(struct pios_dsm_dev *dsm_dev)
|
||||
{
|
||||
struct pios_dsm_state *state = &(dsm_dev->state);
|
||||
/* Fix resolution for detection. */
|
||||
static uint8_t resolution = 11;
|
||||
static uint8_t resolution = 11;
|
||||
uint32_t channel_log = 0;
|
||||
|
||||
#ifdef DSM_LOST_FRAME_COUNTER
|
||||
@ -213,13 +213,25 @@ static int PIOS_DSM_UnrollChannels(struct pios_dsm_dev *dsm_dev)
|
||||
/* extract and save the channel value */
|
||||
uint8_t channel_num = (word >> resolution) & 0x0f;
|
||||
if (channel_num < PIOS_DSM_NUM_INPUTS) {
|
||||
if (channel_log & (1 << channel_num))
|
||||
{
|
||||
/* Found duplicate! */
|
||||
/* Update resolution and restart processing the current frame. */
|
||||
if (channel_log & (1 << channel_num)) {
|
||||
/* Found duplicate. This should happen when in 11 bit */
|
||||
/* mode and the data is 10 bits */
|
||||
if (resolution == 10) {
|
||||
return -1;
|
||||
}
|
||||
resolution = 10;
|
||||
return PIOS_DSM_UnrollChannels(dsm_dev);
|
||||
}
|
||||
|
||||
if ((channel_log & 0xFF) == 0x55) {
|
||||
/* This pattern indicates 10 bit pattern */
|
||||
if (resolution == 11) {
|
||||
return -1;
|
||||
}
|
||||
resolution = 11;
|
||||
return PIOS_DSM_UnrollChannels(dsm_dev);
|
||||
}
|
||||
|
||||
state->channel_data[channel_num] = (word & mask);
|
||||
/* keep track of this channel */
|
||||
channel_log |= (1 << channel_num);
|
||||
@ -282,7 +294,7 @@ int32_t PIOS_DSM_Init(uint32_t *dsm_id,
|
||||
}
|
||||
|
||||
/* Bind the configuration to the device instance */
|
||||
dsm_dev->cfg = cfg;
|
||||
dsm_dev->cfg = cfg;
|
||||
|
||||
/* Bind the receiver if requested */
|
||||
if (bind) {
|
||||
|
@ -132,8 +132,10 @@ static void PIOS_DSM_Bind(struct pios_dsm_dev *dsm_dev, uint8_t bind)
|
||||
/* RX line, set high */
|
||||
GPIO_SetBits(cfg->bind.gpio, cfg->bind.init.GPIO_Pin);
|
||||
|
||||
/* on CC works up to 140ms, guess bind window is around 20-140ms after power up */
|
||||
PIOS_DELAY_WaitmS(60);
|
||||
/* Wait until the bind window opens. */
|
||||
while (PIOS_DELAY_GetuS() < DSM_BIND_MIN_DELAY_US) {
|
||||
;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bind; i++) {
|
||||
/* RX line, drive low for 120us */
|
||||
@ -181,7 +183,7 @@ static int PIOS_DSM_UnrollChannels(struct pios_dsm_dev *dsm_dev)
|
||||
{
|
||||
struct pios_dsm_state *state = &(dsm_dev->state);
|
||||
/* Fix resolution for detection. */
|
||||
static uint8_t resolution = 11;
|
||||
static uint8_t resolution = 11;
|
||||
uint32_t channel_log = 0;
|
||||
|
||||
#ifdef DSM_LOST_FRAME_COUNTER
|
||||
@ -213,13 +215,25 @@ static int PIOS_DSM_UnrollChannels(struct pios_dsm_dev *dsm_dev)
|
||||
/* extract and save the channel value */
|
||||
uint8_t channel_num = (word >> resolution) & 0x0f;
|
||||
if (channel_num < PIOS_DSM_NUM_INPUTS) {
|
||||
if (channel_log & (1 << channel_num))
|
||||
{
|
||||
/* Found duplicate! */
|
||||
/* Update resolution and restart processing the current frame. */
|
||||
if (channel_log & (1 << channel_num)) {
|
||||
/* Found duplicate. This should happen when in 11 bit */
|
||||
/* mode and the data is 10 bits */
|
||||
if (resolution == 10) {
|
||||
return -1;
|
||||
}
|
||||
resolution = 10;
|
||||
return PIOS_DSM_UnrollChannels(dsm_dev);
|
||||
}
|
||||
|
||||
if ((channel_log & 0xFF) == 0x55) {
|
||||
/* This pattern indicates 10 bit pattern */
|
||||
if (resolution == 11) {
|
||||
return -1;
|
||||
}
|
||||
resolution = 11;
|
||||
return PIOS_DSM_UnrollChannels(dsm_dev);
|
||||
}
|
||||
|
||||
state->channel_data[channel_num] = (word & mask);
|
||||
/* keep track of this channel */
|
||||
channel_log |= (1 << channel_num);
|
||||
@ -282,7 +296,7 @@ int32_t PIOS_DSM_Init(uint32_t *dsm_id,
|
||||
}
|
||||
|
||||
/* Bind the configuration to the device instance */
|
||||
dsm_dev->cfg = cfg;
|
||||
dsm_dev->cfg = cfg;
|
||||
|
||||
/* Bind the receiver if requested */
|
||||
if (bind) {
|
||||
|
@ -74,7 +74,7 @@ static void genericTIM_OCxPreloadConfig(TIM_TypeDef *TIMx, uint16_t TIM_OCPreloa
|
||||
},
|
||||
},
|
||||
....
|
||||
[HWSETTINGS_WS2811LED_OUT_FLEXIPIN4] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_FLEXIIOPIN4] = {
|
||||
.gpio = GPIOB,
|
||||
.gpioInit = {
|
||||
.GPIO_Pin = GPIO_Pin_13,
|
||||
|
@ -19,6 +19,7 @@
|
||||
# These are the UAVObjects supposed to be build as part of the OpenPilot target
|
||||
# (all architectures)
|
||||
UAVOBJSRCFILENAMES =
|
||||
UAVOBJSRCFILENAMES += vtolselftuningstats
|
||||
UAVOBJSRCFILENAMES += accelgyrosettings
|
||||
UAVOBJSRCFILENAMES += accessorydesired
|
||||
UAVOBJSRCFILENAMES += actuatorcommand
|
||||
@ -76,6 +77,7 @@ UAVOBJSRCFILENAMES += pathaction
|
||||
UAVOBJSRCFILENAMES += pathdesired
|
||||
UAVOBJSRCFILENAMES += pathplan
|
||||
UAVOBJSRCFILENAMES += pathstatus
|
||||
UAVOBJSRCFILENAMES += pathsummary
|
||||
UAVOBJSRCFILENAMES += positionstate
|
||||
UAVOBJSRCFILENAMES += ratedesired
|
||||
UAVOBJSRCFILENAMES += ekfconfiguration
|
||||
|
@ -2020,72 +2020,72 @@ void DMA2_Stream1_IRQHandler(void) __attribute__((alias("PIOS_WS2811_irq_handler
|
||||
// this will not clash with PWM in or servo output as
|
||||
// pins will be reconfigured as _OUT so the alternate function is disabled.
|
||||
const struct pios_ws2811_pin_cfg pios_ws2811_pin_cfg[] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT1] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT1] = {
|
||||
.gpio = GPIOB,
|
||||
.gpioInit = {
|
||||
.gpioInit = {
|
||||
.GPIO_Pin = GPIO_Pin_0,
|
||||
.GPIO_Speed = GPIO_Speed_25MHz,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
.GPIO_OType = GPIO_OType_PP,
|
||||
},
|
||||
},
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT2] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT2] = {
|
||||
.gpio = GPIOB,
|
||||
.gpioInit = {
|
||||
.gpioInit = {
|
||||
.GPIO_Pin = GPIO_Pin_1,
|
||||
.GPIO_Speed = GPIO_Speed_25MHz,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
.GPIO_OType = GPIO_OType_PP,
|
||||
},
|
||||
},
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT3] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT3] = {
|
||||
.gpio = GPIOA,
|
||||
.gpioInit = {
|
||||
.gpioInit = {
|
||||
.GPIO_Pin = GPIO_Pin_3,
|
||||
.GPIO_Speed = GPIO_Speed_25MHz,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
.GPIO_OType = GPIO_OType_PP,
|
||||
},
|
||||
},
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT4] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT4] = {
|
||||
.gpio = GPIOA,
|
||||
.gpioInit = {
|
||||
.gpioInit = {
|
||||
.GPIO_Pin = GPIO_Pin_2,
|
||||
.GPIO_Speed = GPIO_Speed_25MHz,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
.GPIO_OType = GPIO_OType_PP,
|
||||
},
|
||||
},
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT5] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT5] = {
|
||||
.gpio = GPIOA,
|
||||
.gpioInit = {
|
||||
.gpioInit = {
|
||||
.GPIO_Pin = GPIO_Pin_1,
|
||||
.GPIO_Speed = GPIO_Speed_25MHz,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
.GPIO_OType = GPIO_OType_PP,
|
||||
},
|
||||
},
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT6] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_SERVOOUT6] = {
|
||||
.gpio = GPIOA,
|
||||
.gpioInit = {
|
||||
.gpioInit = {
|
||||
.GPIO_Pin = GPIO_Pin_0,
|
||||
.GPIO_Speed = GPIO_Speed_25MHz,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
.GPIO_OType = GPIO_OType_PP,
|
||||
},
|
||||
},
|
||||
[HWSETTINGS_WS2811LED_OUT_FLEXIPIN3] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_FLEXIIOPIN3] = {
|
||||
.gpio = GPIOB,
|
||||
.gpioInit = {
|
||||
.gpioInit = {
|
||||
.GPIO_Pin = GPIO_Pin_12,
|
||||
.GPIO_Speed = GPIO_Speed_25MHz,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
.GPIO_OType = GPIO_OType_PP,
|
||||
},
|
||||
},
|
||||
[HWSETTINGS_WS2811LED_OUT_FLEXIPIN4] = {
|
||||
[HWSETTINGS_WS2811LED_OUT_FLEXIIOPIN4] = {
|
||||
.gpio = GPIOB,
|
||||
.gpioInit = {
|
||||
.gpioInit = {
|
||||
.GPIO_Pin = GPIO_Pin_13,
|
||||
.GPIO_Speed = GPIO_Speed_25MHz,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
|
@ -19,6 +19,7 @@
|
||||
# These are the UAVObjects supposed to be build as part of the OpenPilot target
|
||||
# (all architectures)
|
||||
UAVOBJSRCFILENAMES =
|
||||
UAVOBJSRCFILENAMES += vtolselftuningstats
|
||||
UAVOBJSRCFILENAMES += accelgyrosettings
|
||||
UAVOBJSRCFILENAMES += accessorydesired
|
||||
UAVOBJSRCFILENAMES += actuatorcommand
|
||||
@ -76,6 +77,7 @@ UAVOBJSRCFILENAMES += pathaction
|
||||
UAVOBJSRCFILENAMES += pathdesired
|
||||
UAVOBJSRCFILENAMES += pathplan
|
||||
UAVOBJSRCFILENAMES += pathstatus
|
||||
UAVOBJSRCFILENAMES += pathsummary
|
||||
UAVOBJSRCFILENAMES += positionstate
|
||||
UAVOBJSRCFILENAMES += ratedesired
|
||||
UAVOBJSRCFILENAMES += ekfconfiguration
|
||||
|
@ -359,9 +359,6 @@ static void PIOS_Board_PPM_callback(const int16_t *channels)
|
||||
|
||||
void PIOS_Board_Init(void)
|
||||
{
|
||||
/* Delay system */
|
||||
PIOS_DELAY_Init();
|
||||
|
||||
const struct pios_board_info *bdinfo = &pios_board_info_blob;
|
||||
|
||||
#if defined(PIOS_INCLUDE_LED)
|
||||
@ -397,11 +394,6 @@ void PIOS_Board_Init(void)
|
||||
if (PIOS_FLASHFS_Logfs_Init(&pios_uavo_settings_fs_id, &flashfs_external_system_cfg, &pios_jedec_flash_driver, flash_id)) {
|
||||
PIOS_DEBUG_Assert(0);
|
||||
}
|
||||
|
||||
if (PIOS_FLASHFS_Logfs_Init(&pios_user_fs_id, &flashfs_external_user_cfg, &pios_jedec_flash_driver, flash_id)) {
|
||||
PIOS_DEBUG_Assert(0);
|
||||
}
|
||||
|
||||
#endif /* if defined(PIOS_INCLUDE_FLASH) */
|
||||
|
||||
#if defined(PIOS_INCLUDE_RTC)
|
||||
@ -463,8 +455,57 @@ void PIOS_Board_Init(void)
|
||||
AlarmsSet(SYSTEMALARMS_ALARM_BOOTFAULT, SYSTEMALARMS_ALARM_CRITICAL);
|
||||
}
|
||||
|
||||
/* Configure IO ports */
|
||||
uint8_t hwsettings_DSMxBind;
|
||||
HwSettingsDSMxBindGet(&hwsettings_DSMxBind);
|
||||
|
||||
// PIOS_IAP_Init();
|
||||
/* Configure FlexiPort */
|
||||
uint8_t hwsettings_flexiport;
|
||||
HwSettingsRM_FlexiPortGet(&hwsettings_flexiport);
|
||||
switch (hwsettings_flexiport) {
|
||||
case HWSETTINGS_RM_FLEXIPORT_DISABLED:
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_TELEMETRY:
|
||||
PIOS_Board_configure_com(&pios_usart_flexi_cfg, PIOS_COM_TELEM_RF_RX_BUF_LEN, PIOS_COM_TELEM_RF_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_telem_rf_id);
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_I2C:
|
||||
#if defined(PIOS_INCLUDE_I2C)
|
||||
{
|
||||
if (PIOS_I2C_Init(&pios_i2c_flexiport_adapter_id, &pios_i2c_flexiport_adapter_cfg)) {
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
}
|
||||
#endif /* PIOS_INCLUDE_I2C */
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_GPS:
|
||||
PIOS_Board_configure_com(&pios_usart_flexi_cfg, PIOS_COM_GPS_RX_BUF_LEN, PIOS_COM_GPS_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_gps_id);
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_DSM:
|
||||
// TODO: Define the various Channelgroup for Revo dsm inputs and handle here
|
||||
PIOS_Board_configure_dsm(&pios_usart_dsm_flexi_cfg, &pios_dsm_flexi_cfg,
|
||||
&pios_usart_com_driver, MANUALCONTROLSETTINGS_CHANNELGROUPS_DSMFLEXIPORT, &hwsettings_DSMxBind);
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_DEBUGCONSOLE:
|
||||
#if defined(PIOS_INCLUDE_DEBUG_CONSOLE)
|
||||
{
|
||||
PIOS_Board_configure_com(&pios_usart_main_cfg, 0, PIOS_COM_DEBUGCONSOLE_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_debug_id);
|
||||
}
|
||||
#endif /* PIOS_INCLUDE_DEBUG_CONSOLE */
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_COMBRIDGE:
|
||||
PIOS_Board_configure_com(&pios_usart_flexi_cfg, PIOS_COM_BRIDGE_RX_BUF_LEN, PIOS_COM_BRIDGE_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_bridge_id);
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_OSDHK:
|
||||
PIOS_Board_configure_com(&pios_usart_hkosd_flexi_cfg, PIOS_COM_HKOSD_RX_BUF_LEN, PIOS_COM_HKOSD_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_hkosd_id);
|
||||
break;
|
||||
} /* hwsettings_rm_flexiport */
|
||||
|
||||
/* Moved this here to allow binding on flexiport */
|
||||
#if defined(PIOS_INCLUDE_FLASH)
|
||||
if (PIOS_FLASHFS_Logfs_Init(&pios_user_fs_id, &flashfs_external_user_cfg, &pios_jedec_flash_driver, flash_id)) {
|
||||
PIOS_DEBUG_Assert(0);
|
||||
}
|
||||
#endif /* if defined(PIOS_INCLUDE_FLASH) */
|
||||
|
||||
#if defined(PIOS_INCLUDE_USB)
|
||||
/* Initialize board specific USB data */
|
||||
@ -599,9 +640,6 @@ void PIOS_Board_Init(void)
|
||||
}
|
||||
#endif /* PIOS_INCLUDE_USB */
|
||||
|
||||
/* Configure IO ports */
|
||||
uint8_t hwsettings_DSMxBind;
|
||||
HwSettingsDSMxBindGet(&hwsettings_DSMxBind);
|
||||
|
||||
/* Configure main USART port */
|
||||
uint8_t hwsettings_mainport;
|
||||
@ -643,7 +681,7 @@ void PIOS_Board_Init(void)
|
||||
// TODO: Define the various Channelgroup for Revo dsm inputs and handle here
|
||||
PIOS_Board_configure_dsm(&pios_usart_dsm_main_cfg, &pios_dsm_main_cfg,
|
||||
&pios_usart_com_driver, MANUALCONTROLSETTINGS_CHANNELGROUPS_DSMMAINPORT, &hwsettings_DSMxBind);
|
||||
break;
|
||||
break;
|
||||
case HWSETTINGS_RM_MAINPORT_DEBUGCONSOLE:
|
||||
#if defined(PIOS_INCLUDE_DEBUG_CONSOLE)
|
||||
{
|
||||
@ -664,47 +702,6 @@ void PIOS_Board_Init(void)
|
||||
GPIO_WriteBit(pios_sbus_cfg.inv.gpio, pios_sbus_cfg.inv.init.GPIO_Pin, pios_sbus_cfg.gpio_inv_disable);
|
||||
}
|
||||
|
||||
/* Configure FlexiPort */
|
||||
uint8_t hwsettings_flexiport;
|
||||
HwSettingsRM_FlexiPortGet(&hwsettings_flexiport);
|
||||
switch (hwsettings_flexiport) {
|
||||
case HWSETTINGS_RM_FLEXIPORT_DISABLED:
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_TELEMETRY:
|
||||
PIOS_Board_configure_com(&pios_usart_flexi_cfg, PIOS_COM_TELEM_RF_RX_BUF_LEN, PIOS_COM_TELEM_RF_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_telem_rf_id);
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_I2C:
|
||||
#if defined(PIOS_INCLUDE_I2C)
|
||||
{
|
||||
if (PIOS_I2C_Init(&pios_i2c_flexiport_adapter_id, &pios_i2c_flexiport_adapter_cfg)) {
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
}
|
||||
#endif /* PIOS_INCLUDE_I2C */
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_GPS:
|
||||
PIOS_Board_configure_com(&pios_usart_flexi_cfg, PIOS_COM_GPS_RX_BUF_LEN, PIOS_COM_GPS_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_gps_id);
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_DSM:
|
||||
// TODO: Define the various Channelgroup for Revo dsm inputs and handle here
|
||||
PIOS_Board_configure_dsm(&pios_usart_dsm_flexi_cfg, &pios_dsm_flexi_cfg,
|
||||
&pios_usart_com_driver, MANUALCONTROLSETTINGS_CHANNELGROUPS_DSMFLEXIPORT, &hwsettings_DSMxBind);
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_DEBUGCONSOLE:
|
||||
#if defined(PIOS_INCLUDE_DEBUG_CONSOLE)
|
||||
{
|
||||
PIOS_Board_configure_com(&pios_usart_main_cfg, 0, PIOS_COM_DEBUGCONSOLE_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_debug_id);
|
||||
}
|
||||
#endif /* PIOS_INCLUDE_DEBUG_CONSOLE */
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_COMBRIDGE:
|
||||
PIOS_Board_configure_com(&pios_usart_flexi_cfg, PIOS_COM_BRIDGE_RX_BUF_LEN, PIOS_COM_BRIDGE_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_bridge_id);
|
||||
break;
|
||||
case HWSETTINGS_RM_FLEXIPORT_OSDHK:
|
||||
PIOS_Board_configure_com(&pios_usart_hkosd_flexi_cfg, PIOS_COM_HKOSD_RX_BUF_LEN, PIOS_COM_HKOSD_TX_BUF_LEN, &pios_usart_com_driver, &pios_com_hkosd_id);
|
||||
break;
|
||||
} /* hwsettings_rm_flexiport */
|
||||
|
||||
|
||||
/* Initalize the RFM22B radio COM device. */
|
||||
#if defined(PIOS_INCLUDE_RFM22B)
|
||||
|
@ -19,6 +19,7 @@
|
||||
# These are the UAVObjects supposed to be build as part of the OpenPilot target
|
||||
# (all architectures)
|
||||
UAVOBJSRCFILENAMES =
|
||||
UAVOBJSRCFILENAMES += vtolselftuningstats
|
||||
UAVOBJSRCFILENAMES += accelgyrosettings
|
||||
UAVOBJSRCFILENAMES += accessorydesired
|
||||
UAVOBJSRCFILENAMES += actuatorcommand
|
||||
@ -76,6 +77,7 @@ UAVOBJSRCFILENAMES += pathaction
|
||||
UAVOBJSRCFILENAMES += pathdesired
|
||||
UAVOBJSRCFILENAMES += pathplan
|
||||
UAVOBJSRCFILENAMES += pathstatus
|
||||
UAVOBJSRCFILENAMES += pathsummary
|
||||
UAVOBJSRCFILENAMES += positionstate
|
||||
UAVOBJSRCFILENAMES += ratedesired
|
||||
UAVOBJSRCFILENAMES += ekfconfiguration
|
||||
|
@ -70,6 +70,12 @@ UAVOBJSYNTHDIR = $(OUTDIR)/../uavobject-synthetics/flight
|
||||
|
||||
# optional component libraries
|
||||
include $(PIOS)/common/libraries/FreeRTOS/library.mk
|
||||
ifeq ($(USE_YAFFS),YES)
|
||||
SRC += $(PIOSCORECOMMON)/pios_logfs.c # Used for yaffs testing
|
||||
include $(PIOS)/common/libraries/yaffs2/library.mk # Only required for yaffs testing
|
||||
include $(PIOS)/posix/libraries/yaffs2/yaffs_impl.mk # Only required for yaffs testing
|
||||
endif
|
||||
|
||||
#include $(FLIGHTLIB)/PyMite/pymite.mk
|
||||
|
||||
# List C source files here. (C dependencies are automatically generated.)
|
||||
@ -103,7 +109,12 @@ SRC += $(MATHLIB)/mathmisc.c
|
||||
SRC += $(MATHLIB)/butterworth.c
|
||||
|
||||
SRC += $(PIOSCORECOMMON)/pios_task_monitor.c
|
||||
ifeq ($(USE_YAFFS),YES)
|
||||
SRC += $(PIOSCORECOMMON)/pios_logfs.c # Used for yaffs testing
|
||||
else
|
||||
SRC += $(PIOSCORECOMMON)/pios_dosfs_logfs.c
|
||||
endif
|
||||
SRC += $(PIOSCORECOMMON)/pios_trace.c
|
||||
SRC += $(PIOSCORECOMMON)/pios_debuglog.c
|
||||
SRC += $(PIOSCORECOMMON)/pios_callbackscheduler.c
|
||||
SRC += $(PIOSCORECOMMON)/pios_deltatime.c
|
||||
@ -155,7 +166,10 @@ CFLAGS += -finstrument-functions -ffixed-r10
|
||||
else
|
||||
CFLAGS += -Os
|
||||
endif
|
||||
|
||||
|
||||
# tracing
|
||||
# CFLAGS += -DPIOS_TRACE
|
||||
|
||||
# common architecture-specific flags from the device-specific library makefile
|
||||
CFLAGS += $(ARCHFLAGS)
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
# (all architectures)
|
||||
|
||||
UAVOBJSRCFILENAMES =
|
||||
UAVOBJSRCFILENAMES += vtolselftuningstats
|
||||
UAVOBJSRCFILENAMES += accessorydesired
|
||||
UAVOBJSRCFILENAMES += actuatorcommand
|
||||
UAVOBJSRCFILENAMES += actuatordesired
|
||||
@ -77,6 +78,7 @@ UAVOBJSRCFILENAMES += pathaction
|
||||
UAVOBJSRCFILENAMES += pathdesired
|
||||
UAVOBJSRCFILENAMES += pathplan
|
||||
UAVOBJSRCFILENAMES += pathstatus
|
||||
UAVOBJSRCFILENAMES += pathsummary
|
||||
UAVOBJSRCFILENAMES += positionstate
|
||||
UAVOBJSRCFILENAMES += ratedesired
|
||||
UAVOBJSRCFILENAMES += revocalibration
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <manualcontrolsettings.h>
|
||||
#include <taskinfo.h>
|
||||
|
||||
|
||||
/*
|
||||
* Pull in the board-specific static HW definitions.
|
||||
* Including .c files is a bit ugly but this allows all of
|
||||
@ -119,13 +120,16 @@ void PIOS_Board_Init(void)
|
||||
/* Delay system */
|
||||
PIOS_DELAY_Init();
|
||||
|
||||
// Initialize dosfs fake flash logfs
|
||||
// Initialize logfs for settings.
|
||||
// If linking in yaffs for testing, this will be /dev0 with settings stored
|
||||
// via the logfs object api in /dev0/logfs/
|
||||
if (PIOS_FLASHFS_Logfs_Init(&pios_uavo_settings_fs_id, NULL, NULL, 0)) {
|
||||
PIOS_DEBUG_Assert(0);
|
||||
}
|
||||
// If linking in yaffs for testing, this will re-use the simposix yaffs /dev0 nor
|
||||
// simulation, which does not support being instanced twice.
|
||||
pios_user_fs_id = pios_uavo_settings_fs_id;
|
||||
|
||||
|
||||
/* Initialize the task monitor */
|
||||
if (PIOS_TASK_MONITOR_Initialize(TASKINFO_RUNNING_NUMELEM)) {
|
||||
PIOS_Assert(0);
|
||||
|
@ -45,7 +45,6 @@
|
||||
#if INCLUDE_TEST_TASKS
|
||||
static uint8_t sdcard_available;
|
||||
#endif
|
||||
FILEINFO File;
|
||||
char Buffer[1024];
|
||||
uint32_t Cache;
|
||||
|
||||
|
@ -29,16 +29,6 @@
|
||||
#ifndef PIOS_BOARD_H
|
||||
#define PIOS_BOARD_H
|
||||
|
||||
/**
|
||||
* glue macros for file IO
|
||||
**/
|
||||
#define FILEINFO FILE *
|
||||
#define PIOS_FOPEN_READ(filename, file) (file = fopen((char *)filename, "r")) == NULL
|
||||
#define PIOS_FOPEN_WRITE(filename, file) (file = fopen((char *)filename, "w")) == NULL
|
||||
#define PIOS_FREAD(file, bufferadr, length, resultadr) (*resultadr = fread((uint8_t *)bufferadr, 1, length, *file)) != length
|
||||
#define PIOS_FWRITE(file, bufferadr, length, resultadr) *resultadr = fwrite((uint8_t *)bufferadr, 1, length, *file)
|
||||
#define PIOS_FCLOSE(file) fclose(file)
|
||||
#define PIOS_FUNLINK(file) unlink((char *)filename)
|
||||
|
||||
// ------------------------
|
||||
// Timers and Channels Used
|
||||
|
@ -202,10 +202,6 @@ uint8_t UAVObjUpdateCRC(UAVObjHandle obj_handle, uint16_t instId, uint8_t crc);
|
||||
int32_t UAVObjSave(UAVObjHandle obj_handle, uint16_t instId);
|
||||
int32_t UAVObjLoad(UAVObjHandle obj_handle, uint16_t instId);
|
||||
int32_t UAVObjDelete(UAVObjHandle obj_handle, uint16_t instId);
|
||||
#if defined(PIOS_INCLUDE_SDCARD)
|
||||
int32_t UAVObjSaveToFile(UAVObjHandle obj_handle, uint16_t instId, FILEINFO *file);
|
||||
int32_t UAVObjLoadFromFile(UAVObjHandle obj_handle, FILEINFO *file);
|
||||
#endif
|
||||
int32_t UAVObjSaveSettings();
|
||||
int32_t UAVObjLoadSettings();
|
||||
int32_t UAVObjDeleteSettings();
|
||||
|
@ -1,14 +0,0 @@
|
||||
include(../openpilotgcs.pri)
|
||||
|
||||
TEMPLATE = app
|
||||
TARGET = $$GCS_APP_WRAPPER
|
||||
OBJECTS_DIR =
|
||||
|
||||
PRE_TARGETDEPS = $$PWD/openpilotgcs
|
||||
|
||||
QMAKE_LINK = cp $$PWD/openpilotgcs $@ && : IGNORE REST
|
||||
|
||||
QMAKE_CLEAN = $$GCS_APP_WRAPPER
|
||||
|
||||
target.path = /bin
|
||||
INSTALLS += target
|
@ -1,36 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
makeAbsolute() {
|
||||
case "$1" in
|
||||
/*)
|
||||
# already absolute, return it
|
||||
echo "$1"
|
||||
;;
|
||||
*)
|
||||
# relative, prepend $2 made absolute
|
||||
echo `makeAbsolute "$2" "$PWD"`/"$1" | sed 's,/\.$,,'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
if test -L "$0"; then
|
||||
# Try readlink(1)
|
||||
readlink=`type readlink 2>/dev/null` || readlink=
|
||||
if test -n "$readlink"; then
|
||||
# We have readlink(1), so we can use it
|
||||
me=`readlink -nf "$0"`
|
||||
else
|
||||
# No readlink(1), so let's try ls -l
|
||||
me=`ls -l "$0" | sed 's/^.*-> //'`
|
||||
base=`dirname "$0"`
|
||||
me=`makeAbsolute "$me" "$base"`
|
||||
fi
|
||||
else
|
||||
me="$0"
|
||||
fi
|
||||
|
||||
bindir=`dirname "$me"`
|
||||
libdir=`cd "${bindir}/../lib" ; pwd`
|
||||
LD_LIBRARY_PATH="${libdir}/openpilotgcs:${LD_LIBRARY_PATH}"
|
||||
export LD_LIBRARY_PATH
|
||||
exec "${bindir}/openpilotgcs.bin" ${1+"$@"}
|
@ -31,9 +31,9 @@ GCS_LIBRARY_PATH
|
||||
libQt5DBus.so.5 \
|
||||
libQt5QuickParticles.so.5 \
|
||||
libqgsttools_p.so.1 \
|
||||
libicui18n.so.52 \
|
||||
libicuuc.so.52 \
|
||||
libicudata.so.52
|
||||
libicui18n.so.53 \
|
||||
libicuuc.so.53 \
|
||||
libicudata.so.53
|
||||
|
||||
data_copy.commands += -@$(MKDIR) $$targetPath(\"$$GCS_QT_LIBRARY_PATH\") $$addNewline()
|
||||
for(lib, QT_LIBS) {
|
||||
@ -142,9 +142,9 @@ GCS_LIBRARY_PATH
|
||||
Qt5MultimediaWidgets$${DS}.dll \
|
||||
Qt5Quick$${DS}.dll \
|
||||
Qt5Qml$${DS}.dll \
|
||||
icuin52.dll \
|
||||
icudt52.dll \
|
||||
icuuc52.dll
|
||||
icuin53.dll \
|
||||
icudt53.dll \
|
||||
icuuc53.dll
|
||||
# it is more robust to take the following DLLs from Qt rather than from MinGW
|
||||
QT_DLLS += libgcc_s_dw2-1.dll \
|
||||
libstdc++-6.dll \
|
||||
|
@ -96,7 +96,15 @@ macx {
|
||||
copydata = 1
|
||||
copyqt = 1
|
||||
} else {
|
||||
GCS_LIBRARY_PATH = $$GCS_BUILD_TREE/$$GCS_LIBRARY_BASENAME/openpilotgcs
|
||||
GCS_PLUGIN_PATH = $$GCS_LIBRARY_PATH/plugins
|
||||
GCS_LIBEXEC_PATH = $$GCS_APP_PATH # FIXME
|
||||
GCS_DATA_PATH = $$GCS_BUILD_TREE/share/openpilotgcs
|
||||
GCS_DATA_BASENAME = share/openpilotgcs
|
||||
GCS_DOC_PATH = $$GCS_BUILD_TREE/share/doc
|
||||
|
||||
!isEqual(GCS_SOURCE_TREE, $$GCS_BUILD_TREE):copydata = 1
|
||||
|
||||
win32 {
|
||||
SDL_DIR = $$(SDL_DIR)
|
||||
isEmpty(SDL_DIR):SDL_DIR = $${TOOLS_DIR}/SDL-1.2.15
|
||||
@ -111,28 +119,19 @@ macx {
|
||||
GCS_APP_TARGET = openpilotgcs
|
||||
copyqt = $$copydata
|
||||
} else {
|
||||
GCS_APP_WRAPPER = openpilotgcs
|
||||
GCS_APP_TARGET = openpilotgcs.bin
|
||||
GCS_QT_LIBRARY_PATH = $$GCS_BUILD_TREE/$$GCS_LIBRARY_BASENAME/qt5
|
||||
GCS_QT_PLUGINS_PATH = $$GCS_BUILD_TREE/$$GCS_LIBRARY_BASENAME/qt5/plugins
|
||||
GCS_QT_QML_PATH = $$GCS_BUILD_TREE/$$GCS_LIBRARY_BASENAME/qt5/qml
|
||||
GCS_APP_TARGET = openpilotgcs
|
||||
GCS_QT_BASEPATH = $$GCS_LIBRARY_PATH/qt5
|
||||
GCS_QT_LIBRARY_PATH = $$GCS_QT_BASEPATH/lib
|
||||
GCS_QT_PLUGINS_PATH = $$GCS_QT_BASEPATH/plugins
|
||||
GCS_QT_QML_PATH = $$GCS_QT_BASEPATH/qml
|
||||
|
||||
QT_INSTALL_DIR = $$clean_path($$[QT_INSTALL_LIBS]/../../../..)
|
||||
equals(QT_INSTALL_DIR, $$TOOLS_DIR) {
|
||||
copyqt = 1
|
||||
} else {
|
||||
copyqt = 0
|
||||
copyqt = 0
|
||||
}
|
||||
|
||||
FORCE_COPY_QT = $$(FORCE_COPY_QT)
|
||||
!isEmpty(FORCE_COPY_QT):copyqt = 1
|
||||
}
|
||||
GCS_LIBRARY_PATH = $$GCS_BUILD_TREE/$$GCS_LIBRARY_BASENAME/openpilotgcs
|
||||
GCS_PLUGIN_PATH = $$GCS_LIBRARY_PATH/plugins
|
||||
GCS_LIBEXEC_PATH = $$GCS_APP_PATH # FIXME
|
||||
GCS_DATA_PATH = $$GCS_BUILD_TREE/share/openpilotgcs
|
||||
GCS_DATA_BASENAME = share/openpilotgcs
|
||||
GCS_DOC_PATH = $$GCS_BUILD_TREE/share/doc
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,14 +5,14 @@
|
||||
|
||||
cache()
|
||||
|
||||
#check Qt version
|
||||
# check Qt version
|
||||
QT_VERSION = $$[QT_VERSION]
|
||||
QT_VERSION = $$split(QT_VERSION, ".")
|
||||
QT_VER_MAJ = $$member(QT_VERSION, 0)
|
||||
QT_VER_MIN = $$member(QT_VERSION, 1)
|
||||
|
||||
lessThan(QT_VER_MAJ, 5) | lessThan(QT_VER_MIN, 1) {
|
||||
error(OpenPilot GCS requires Qt 5.1.0 or newer but Qt $$[QT_VERSION] was detected.)
|
||||
lessThan(QT_VER_MAJ, 5) | lessThan(QT_VER_MIN, 2) {
|
||||
error(OpenPilot GCS requires Qt 5.2.0 or newer but Qt $$[QT_VERSION] was detected.)
|
||||
}
|
||||
|
||||
macx {
|
||||
@ -32,6 +32,5 @@ CONFIG += ordered
|
||||
DEFINES += USE_PATHPLANNER
|
||||
|
||||
SUBDIRS = src share copydata
|
||||
unix:!macx:!isEmpty(copydata):SUBDIRS += bin
|
||||
|
||||
copydata.file = copydata.pro
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user