mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-01 09:24:10 +01:00
Remove (almost) exact copy of dosfs from F1-specific directory
The difference was in dfs_sdcard.c, line 107: -if((status = PIOS_SDCARD_SectorRead(sector, buffer)) < 0) { +if((status = PIOS_SDCARD_SectorRead(sector, buffer)) != 0) { Currenly unused, kept as is.
This commit is contained in:
parent
448a07fe57
commit
db0cdc6a0c
@ -1,368 +0,0 @@
|
||||
README.TXT (C) Copyright 2006
|
||||
DOSFS Level 1 Version 1.02 Lewin A.R.W. Edwards (sysadm@zws.com)
|
||||
=====================================================================
|
||||
|
||||
Abstract
|
||||
========
|
||||
DOSFS is a FAT-compatible filesystem intended for fairly low-end
|
||||
embedded applications. It is not the leanest possible implementation
|
||||
(the leanest FAT implementations operate in << 512 bytes of RAM, with
|
||||
heavy restrictions). This code strikes a good balance between size
|
||||
and functionality, with an emphasis on RAM footprint.
|
||||
|
||||
Intended target systems would be in the ballpark of 1K RAM, 4K ROM
|
||||
or more.
|
||||
|
||||
Features:
|
||||
* Supports FAT12, FAT16 and FAT32 volumes
|
||||
* Supports storage devices up to 2048Gbytes in size (LBA32)
|
||||
* Supports devices with or without MBRs (hard disks vs. floppy disks
|
||||
or ZIP drives formatted as "big floppies")
|
||||
* Supports multiple partitions on disks with MBRs
|
||||
* Supports subdirectories
|
||||
* Can be operated with a single global 512-byte sector buffer
|
||||
* Fully reentrant code (assuming the underlying physical device driver
|
||||
is reentrant and global sector buffers are not used). There are no
|
||||
global variables in the filesystem
|
||||
* Does not perform any memory allocation
|
||||
* Partial support for random-access files
|
||||
|
||||
Applications:
|
||||
* Firmware upgrades
|
||||
* Failsafe IPL
|
||||
* Media playback
|
||||
* Data logging
|
||||
* Configuration storage
|
||||
|
||||
There is no technical support for this free product; however, if you
|
||||
have questions or suggestions, you are encouraged to email Lewin
|
||||
Edwards at sysadm@zws.com. If you need custom additions to the code,
|
||||
or if you have other projects for which you need engineering
|
||||
assistance, please feel free to email or call (646) 549-3715.
|
||||
|
||||
License
|
||||
=======
|
||||
The license for DOSFS is very simple but verbose to state.
|
||||
|
||||
1. DOSFS is (C) Copyright 2006 by Lewin A.R.W. Edwards ("Author").
|
||||
All rights not explicitly granted herein are reserved. The DOSFS
|
||||
code is the permanent property of the Author and no transfer of
|
||||
ownership is implied by this license.
|
||||
|
||||
2. DOSFS is an educational project, provided as-is. No guarantee of
|
||||
performance or suitability for any application is stated or
|
||||
implied. You use this product entirely at your own risk. Use of
|
||||
this product in any manner automatically waives any right to seek
|
||||
compensation or damages of any sort from the Author. Since the
|
||||
products you might make are entirely out of the Author's control,
|
||||
use of this product also constitutes an agreement by you to take
|
||||
full responsibility for and indemnify the Author against any
|
||||
action for any loss or damage (including economic loss of any
|
||||
type, and specifically including patent litigation) that arises
|
||||
from a product made by you that incorporates any portion of
|
||||
the DOSFS code.
|
||||
|
||||
3. If you live under the jurisdiction of any legislation that would
|
||||
prohibit or limit any condition in this license, you cannot be
|
||||
licensed to use this product.
|
||||
|
||||
4. If you do not fall into the excluded category in point 3, you are
|
||||
hereby licensed to use the DOSFS code in any application that you
|
||||
see fit. You are not required to pay any fee or notify the Author
|
||||
that you are using DOSFS. Any modifications made by you to the
|
||||
DOSFS code are your property and you may distribute the modified
|
||||
version in any manner that you wish. You are not required to
|
||||
disclose sourcecode to such modifications, either to the Author or
|
||||
to any third party. Any such disclosure made to the Author will
|
||||
irrevocably become the property of the Author in the absence of a
|
||||
formal agreement to the contrary, established prior to such
|
||||
disclosure being made.
|
||||
|
||||
To summarize the intent of the above: DOSFS is free. You can do what
|
||||
you want with it. Anything that happens as a result is entirely your
|
||||
responsibility. You can't take ownership of my code and stop me from
|
||||
doing whatever I want with it. If you do something nifty with DOSFS
|
||||
and send me the sourcecode, I may include your changes in the next
|
||||
distribution and it will be released to the world as free software.
|
||||
If someone sues you because your DOSFS-containing product causes
|
||||
any sort of legal, financial or other problem, it's your lawsuit,
|
||||
not mine, and you'll exclude me from the proceedings.
|
||||
|
||||
User-Supplied Functions
|
||||
=======================
|
||||
You must provide functions to read sectors into memory and write
|
||||
them back to the target media. The demo suite includes an emulation
|
||||
module that reads/writes a disk image file (#define HOSTVER pulls
|
||||
in hostemu.h which wraps the prototypes for these functions).
|
||||
There are various tools for UNIX, DOS, Windows et al, to create
|
||||
images from storage media; my preferred utility is dd.
|
||||
|
||||
The functions you must supply in your embedded app are:
|
||||
|
||||
DFS_ReadSector(unit,buffer,sector,count)
|
||||
DFS_WriteSector(unit,buffer,sector,count)
|
||||
|
||||
These two functions read and write, respectively, "count" sectors of
|
||||
size SECTOR_SIZE (512 bytes; see below) from/to physical sector
|
||||
#"sector" of device "unit", to/from the scratch buffer "buffer". They
|
||||
should return 0 for success or nonzero for failure. In the current
|
||||
implementation of DOSFS, count will always be 1.
|
||||
|
||||
The "unit" argument is designed to permit implementation of multiple
|
||||
storage devices, for example multiple media slots on a single device,
|
||||
or to differentiate between master and slave devices on an ATAPI bus.
|
||||
|
||||
This code is designed for 512-byte sectors. Although the sector size
|
||||
is a #define, you should not tinker with it because the vast majority
|
||||
of FAT filesystems use 512-byte sectors, and the DOSFS code doesn't
|
||||
support runtime determination of sector size. This will not affect the
|
||||
vast majority of users.
|
||||
|
||||
Example Code
|
||||
============
|
||||
Refer to the tests in main.c to see how to call DOSFS functions.
|
||||
(These tests are all commented out). Note that the only two files
|
||||
you need to add to your project are dosfs.c and dosfs.h.
|
||||
|
||||
|
||||
Mounting Volumes
|
||||
================
|
||||
--If the device has a partition table (practically all removable flash
|
||||
media are formatted this way), call DFS_GetPtnStart to get the
|
||||
starting sector# of the desired partition. You can optionally also
|
||||
retrieve the active state, partition type byte and partition size
|
||||
in this step. The reason this step is broken out separately is so
|
||||
you can support devices that are formatted like a floppy disk, i.e.
|
||||
the volume starts directly at physical sector 0 of the media.
|
||||
|
||||
--Call DFS_GetVolInfo to read filesystem info into a VOLINFO structure.
|
||||
DFS_GetVolInfo needs to know the unit number and partition starting
|
||||
sector (as returned by DFS_GetPtnStart, or 0 if this is a "floppy-
|
||||
format" volume without an MBR).
|
||||
|
||||
From this point on, the VOLINFO structure is all you'll need - you can
|
||||
forget the unit and partition start sector numbers.
|
||||
|
||||
Enumerating Directory Contents
|
||||
==============================
|
||||
--Call DFS_Opendir and supply a path, populated VOLINFO and a
|
||||
DIRINFO structure to receive the results. Note - you must PREPOPULATE
|
||||
the DIRINFO.scratch field with a pointer to a sector scratch buffer.
|
||||
This buffer must remain unmolested while you have the directory open
|
||||
for searching.
|
||||
--Call DFS_GetNext to receive the DIRENT contents for the next directory
|
||||
item. This function returns DFS_OK for no error, and DFS_EOF if there
|
||||
are no more entries in the directory being searched.
|
||||
Before using the DIRENT, check the first character of the name. If it
|
||||
is NULL, then this is an unusable entry - call DFS_GetNext again to
|
||||
keep searching. LFN directory entries are automatically tagged this way
|
||||
so your application will not be pestered by them.
|
||||
|
||||
Note: A designed side-effect of this code is that when you locate the
|
||||
file of interest, the DIRINFO.currentcluster, DIRINFO.currentsector
|
||||
and DIRINFO.currententry-1 fields will identify the directory entry of
|
||||
interest.
|
||||
|
||||
Reading a File
|
||||
==============
|
||||
--Call DFS_OpenFile with mode = DFS_READ and supply a path and the relevant
|
||||
VOLINFO structure. DFS_OpenFile will populate a FILEINFO that can be used
|
||||
to refer to the file.
|
||||
--Optionally call DFS_Seek to set the file pointer. If you attempt to set
|
||||
the file pointer past the end of file, the file will NOT be extended. Check
|
||||
the FILEINFO.pointer value after DFS_Seek to verify that the pointer is
|
||||
where you expect it to be.
|
||||
--Observe that functionality similar to the "whence" parameter of fseek() can
|
||||
be obtained by using simple arithmetic on the FILEINFO.pointer and
|
||||
FILEINFO.filelen members.
|
||||
--Call DFS_ReadFile with the FILEINFO you obtained from OpenFile, and a
|
||||
pointer to a buffer plus the desired number of bytes to read, and a
|
||||
pointer to a sector-sized scratch buffer. The reason a scratch sector is
|
||||
required is because the underlying sector read function doesn't know
|
||||
about partial reads.
|
||||
--Note that a file opened for reading cannot be written. If you need r/w
|
||||
access, open with mode = DFS_WRITE (see below).
|
||||
|
||||
Writing a file
|
||||
==============
|
||||
--Call DFS_OpenFile with mode = DFS_WRITE and supply a path and the relevant
|
||||
VOLINFO structure. DFS_OpenFile will populate a FILEINFO that can be used to
|
||||
refer to the file.
|
||||
--Optionally call DFS_Seek to set the file pointer. Refer to the notes on
|
||||
this topic in the section on reading files, above.
|
||||
--Call DFS_WriteFile with the FILEINFO you obtained from OpenFile, and a
|
||||
pointer to the source buffer, and a pointer to a sector-sized scratch
|
||||
buffer.
|
||||
--Note that a file open for writing can also be read.
|
||||
--Files are created automatically if they do not exist. Subdirectories are
|
||||
NOT automatically created.
|
||||
--If you open an existing file for writing, the file pointer will start at
|
||||
the beginning of the data; if you want to append, seek to the end before
|
||||
writing new data.
|
||||
--If you perform random-access writes to a file, the length will NOT change
|
||||
unless you exceed the file's original length. There is currently no
|
||||
function to truncate a file at the current pointer position.
|
||||
--On-disk consistency is guaranteed when DFS_WriteFile exits, unless your
|
||||
physical layer has a writeback cache in it.
|
||||
|
||||
Deleting a file
|
||||
===============
|
||||
--Call DFS_UnlinkFile
|
||||
--WARNING: This call will delete a subdirectory (correctly) but will NOT
|
||||
first recurse the directory to delete the contents - so you will end up
|
||||
with lost clusters.
|
||||
|
||||
Notes
|
||||
=====
|
||||
Some platforms may require explicit pragmas or attributes to the structures
|
||||
and unions. For example, arm-gcc will require __attribute__ ((__packed__))
|
||||
otherwise it will try to be "smart" and place the uint8_t members on 4-byte
|
||||
boundaries. There is no truly elegant compiler-independent method to get
|
||||
around this sort of problem.
|
||||
|
||||
The code assumes either a von Neumann architecture, or a compiler that
|
||||
is smart enough to understand where your pointers are aimed and emit
|
||||
the right kind of memory read and write instructions. The implications
|
||||
of this statement depend on your target processor and the compiler you
|
||||
are using. Be very careful not to straddle bank boundaries on bank-
|
||||
switched memory systems.
|
||||
|
||||
Physical 32-bit sector numbers are used throughout. Therefore, the
|
||||
CHS geometry (if any) of the storage media is not known to DOSFS. Your
|
||||
sector r/w functions may need to query the CHS geometry and perform
|
||||
mapping.
|
||||
|
||||
File timestamps set by DOSFS are always 1:01:00am on Jan 1, 2006. If
|
||||
your system has a concept of real time, you can enhance this.
|
||||
|
||||
FILEINFO structures contain a pointer to the corresponding VOLINFO
|
||||
used to open the file, mainly in order to avoid mixups but also to
|
||||
obviate the need for an extra parameter to every file read/write. DOSFS
|
||||
assumes that the VOLINFO won't move around. If you need to move or
|
||||
destroy VOLINFOs pertaining to open files, you'll have to fix up the
|
||||
pointer in the FILEINFO structure yourself.
|
||||
|
||||
The subdirectory delimiter is a forward slash ( '/' ) by default. The
|
||||
reason for this is to avoid the common programming error of forgetting
|
||||
that backslash is an escape character in C strings; i.e. "\MYDIR\FILE"
|
||||
is NOT what you want; "\\MYDIR\\FILE" is what you wanted to type. If you
|
||||
are porting DOS code into an embedded environment, feel free to change
|
||||
this #define.
|
||||
|
||||
DOSFS does not have a concept of "current directory". A current directory
|
||||
is owned by a process, and a process is an operating system concept.
|
||||
DOSFS is a filesystem library, not an operating system. Therefore, any
|
||||
path you provide to a DOSFS call is assumed to be relative to the root of
|
||||
the volume.
|
||||
|
||||
There is no call to close a file or directory that is open for reading or
|
||||
writing. You can simply destroy or reuse the data structures allocated for
|
||||
that operation; there is no internal state in DOSFS so no cleanup is
|
||||
necessary. Similarly, there is no call to close a file that is open for
|
||||
writing. (Observe that dosfs.c has no global variables. All state information
|
||||
is stored in data structures provided by the caller).
|
||||
|
||||
MAX_PATH is defined as 64. MS-type DOS filesystems support 128 characters
|
||||
or more in paths. You can increase this define, but it may GREATLY
|
||||
increase memory requirements.
|
||||
|
||||
VFAT long filenames are not supported. There is a certain amount of
|
||||
patent controversy about them, but more importantly they don't really
|
||||
belong in the scope of a "minimalist embedded filesystem".
|
||||
|
||||
Improving Performance
|
||||
=====================
|
||||
Read performance is fairly good, but can be improved by implementing read
|
||||
caching on the FAT (see below) and, depending on your hardware platform,
|
||||
possibly by implementing multi-sector reads.
|
||||
|
||||
Write performance may benefit ENORMOUSLY from platform-specific
|
||||
optimization, especially if you are working with a flash media type that
|
||||
has a large erase block size. While it is not possible to offer detailed
|
||||
platform-independent advice, my general advice is to implement writeback
|
||||
caching on the FAT area. One method for doing this would be to have a
|
||||
cache system that lives in the DFS_ReadSector/WriteSector functions (on
|
||||
top of the physical sector r/w functions) and is initially switched off.
|
||||
Once you have called DFS_GetVolInfo, you then extract the VOLINFO.fat1
|
||||
and VOLINFO.rootdir parameters and pass them to your caching layer.
|
||||
Sectors >= fat1 and < rootdir should be cached. The cache strategy is
|
||||
determined by the physical storage medium underlying the filesystem.
|
||||
|
||||
CACHING HINT:
|
||||
Observe that there will be numerous read-modify-write operations in the
|
||||
region from VOLINFO.fat1 through VOLINFO.fat1+VOLINFO.secperfat-1, but
|
||||
in the region from VOLINFO.fat1+VOLINFO.secperfat through VOLINFO.rootdir
|
||||
there will ONLY be write operations.
|
||||
|
||||
Platform Compatibility
|
||||
======================
|
||||
DOSFS was derived from code originally written for ARM7TDMI but
|
||||
designed to be portable. It has been tested on AVR (using avrgcc),
|
||||
MSP430 (using Rowley's CrossWorks) and PPC603e (using gcc); the host
|
||||
test suite has also been validated on x86 using gcc under both Cygwin
|
||||
and 32-bit Fedora Core 4 Linux.
|
||||
|
||||
TODO list
|
||||
=========
|
||||
* Add function to create subdirectory
|
||||
* Make DFS_UnlinkFile recognize non-empty subdirectories
|
||||
* Support "fast write" files where the FAT is not updated, for
|
||||
logging applications where latency is important.
|
||||
|
||||
Test cases for V1.02
|
||||
====================
|
||||
Version 1.02 has NOT been through full regression testing. However the
|
||||
bugs fixed in this version are important, and people have been asking
|
||||
about them.
|
||||
|
||||
Test cases for V1.01
|
||||
====================
|
||||
See below.
|
||||
|
||||
Test cases for V1.00
|
||||
====================
|
||||
These are the test cases that were used to validate the correct
|
||||
functionality of the DOSFS suite. Each test was performed on FAT12,
|
||||
FAT16 and FAT32 volumes. P=Pass, F=Fail.
|
||||
|
||||
Case F12 F16 F32
|
||||
---------------------------------------------------------------------
|
||||
Get volume information P P P
|
||||
Open root directory P P P
|
||||
List contents of root directory (fully populated) P P P
|
||||
Open subdirectory P P P
|
||||
List contents of subdirectory (<= 1 cluster) P P P
|
||||
List contents of large subdirectory (> 1 cluster) P P P
|
||||
Open 5-level nested subdirectory P P P
|
||||
Open existing file for reading P P P
|
||||
Open nonexistent file for reading P P P
|
||||
Seek past EOF, file open for reading P P P
|
||||
Seek to cluster boundary P P P
|
||||
Seek past cluster boundary P P P
|
||||
Seek backwards to nonzero offset, pointer > cluster size P P P
|
||||
Block-read entire file >1 cluster in size, odd size P P P
|
||||
Seek to odd location in file P P P
|
||||
Perform <1 sector reads from random file locations P P P
|
||||
Open nonexistent file for writing in root dir P P P
|
||||
Open nonexistent file for writing in subdir P P P
|
||||
Repeat prev. 2 tests on volume with 0 free clusters P P P
|
||||
Seek past EOF, file open for writing P P P
|
||||
Open existing file for writing in root dir P P P
|
||||
Write random-length records to file, 20 clusters total P P P
|
||||
MS-DOS 6.0 SCANDISK cross-check P P P
|
||||
|
||||
Revision History
|
||||
================
|
||||
Jan-06-2005 larwe Initial release (1.0)
|
||||
Jan-29-2006 larwe Bugfix release (1.01)
|
||||
- Fixed error in FAT12 FAT read on boundary of sector
|
||||
- Improved compilability under avrgcc
|
||||
Sep-16-2006 larwe Bugfix release (1.02)
|
||||
- DFS_Seek would not correctly rewind to start of file
|
||||
- DFS_Seek would not correctly seek to a position not on a cluster
|
||||
boundary
|
||||
- DFS_OpenFile fencepost error caused memory access at [start of
|
||||
string-1] with a local variable
|
||||
- DFS_OpenFile could not open a file in the root directory
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
DOSFS has been developed by Lewin Edwards, and is provided as freeware.
|
||||
|
||||
See README.txt for details.
|
||||
|
||||
This package has been downloaded from:
|
||||
http://www.larwe.com/zws/products/dosfs/
|
||||
|
||||
Version 1.03 from 9/30/06 is used.
|
||||
|
||||
|
||||
dfs_sdcard has been added as access layer between DFS functions and PIOS_SDCARD functions
|
||||
|
||||
The original usage examples can be found under unused/main.c
|
||||
|
||||
Addendum:
|
||||
|
||||
TK 2008-12-18:
|
||||
DFS_Seek was running endless, applied a patch which has been posted at
|
||||
http://reza.net/wordpress/?p=110
|
||||
|
||||
TK 2008-12-18:
|
||||
patched the patch: endcluster wasn't calculated correctly
|
||||
|
||||
TK 2008-12-18:
|
||||
added 'DFS_CachingEnabledSet(uint8_t enable)' function to enable a simple
|
||||
caching mechanism. This feature has to be explicitely enabled, as it isn't
|
||||
reentrant and requires to use the same buffer pointer whenever reading a file!
|
||||
|
||||
TK 2008-18-12
|
||||
added missing pendant to DFS_CanonicalToDir;
|
||||
char *DFS_DirToCanonical(char *dest, char *src)
|
||||
expects a 13 byte buffer in *dest
|
||||
|
||||
TK 2009-02-12
|
||||
added dummy "DFS_Close" function
|
||||
It has no effect if writing to SD Card, it's only used by the DosFS wrapper
|
||||
in emulation
|
||||
|
||||
TK 2009-07-04
|
||||
fixed bug in DFS_GetNext() in conjunction with the DFS_GetFreeDirEnt() function
|
||||
New files where not added correctly to subdirectories
|
||||
|
@ -1,143 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file pios.c
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
||||
* Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org)
|
||||
* @brief Access layer between DOSFS and PIOS
|
||||
* @see The GNU Public License (GPL) Version 3
|
||||
*
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* 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 files */
|
||||
|
||||
#include <pios.h>
|
||||
|
||||
#include "dosfs.h"
|
||||
|
||||
|
||||
/* Local variables */
|
||||
|
||||
/* For caching - this feature has to be explicitly enabled, as it isn't reentrant */
|
||||
/* and requires to use the same buffer pointer whenever reading a file. */
|
||||
static uint32_t last_sector;
|
||||
static uint8_t caching_enabled = 0;
|
||||
|
||||
void DFS_CachingEnabledSet(uint8_t enable)
|
||||
{
|
||||
caching_enabled = enable;
|
||||
last_sector = 0xffffffff;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts directory name to canonical name
|
||||
* (missing pendant to DFS_anonicalToDir)
|
||||
* dest must point to a 13-byte buffer
|
||||
*/
|
||||
char *DFS_DirToCanonical(char *dest, char *src)
|
||||
{
|
||||
uint8_t pos = 0;
|
||||
|
||||
while( pos < 8 && src[pos] != ' ' ) {
|
||||
*dest++ = src[pos++];
|
||||
}
|
||||
|
||||
if( src[8] != ' ' ) {
|
||||
*dest++ = '.';
|
||||
|
||||
pos = 8;
|
||||
while( pos < 11 && src[pos] != ' ' ) {
|
||||
*dest++ = src[pos++];
|
||||
}
|
||||
}
|
||||
|
||||
/* Terminate string */
|
||||
*dest = 0;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Read sector from SD Card
|
||||
* Returns 0 OK, nonzero for any error
|
||||
*/
|
||||
uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count)
|
||||
{
|
||||
/* Only allow access to single unit */
|
||||
if(unit != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* According to README.txt, count is always 1 - check this! */
|
||||
if(count != 1) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Cache: */
|
||||
if(caching_enabled && sector == last_sector) {
|
||||
/* we assume that sector is already in *buffer */
|
||||
/* since the user has to take care that the same buffer is used for file reads, this */
|
||||
/* feature has to be explicitly enabled with DFS_CachingEnabledSet(uint8_t enable) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
last_sector = sector;
|
||||
|
||||
/* Forward to PIOS */
|
||||
int32_t status;
|
||||
if((status = PIOS_SDCARD_SectorRead(sector, buffer)) < 0) {
|
||||
/* Cannot access SD Card */
|
||||
return 3;
|
||||
}
|
||||
|
||||
/* Success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write sector to SD Card
|
||||
* Returns 0 OK, nonzero for any error
|
||||
*/
|
||||
uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count)
|
||||
{
|
||||
/* Only allow access to single unit */
|
||||
if(unit != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* According to README.txt, count is always 1 - check this! */
|
||||
if(count != 1) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Invalidate cache */
|
||||
last_sector = 0xffffffff;
|
||||
|
||||
/* Forward to PIOS */
|
||||
int32_t status;
|
||||
if((status = PIOS_SDCARD_SectorWrite(sector, buffer)) < 0) {
|
||||
/* Cannot access SD Card */
|
||||
return 3;
|
||||
}
|
||||
|
||||
/* Success */
|
||||
return 0;
|
||||
}
|
@ -1,1336 +0,0 @@
|
||||
/*
|
||||
DOSFS Embedded FAT-Compatible Filesystem
|
||||
(C) 2005 Lewin A.R.W. Edwards (sysadm@zws.com)
|
||||
|
||||
You are permitted to modify and/or use this code in your own projects without
|
||||
payment of royalty, regardless of the license(s) you choose for those projects.
|
||||
|
||||
You cannot re-copyright or restrict use of the code as released by Lewin Edwards.
|
||||
*/
|
||||
|
||||
//#include "common.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
//#include "fat.h"
|
||||
|
||||
#define div(a,b) ldiv(a,b)
|
||||
|
||||
#include "dosfs.h"
|
||||
|
||||
/*
|
||||
Get starting sector# of specified partition on drive #unit
|
||||
NOTE: This code ASSUMES an MBR on the disk.
|
||||
scratchsector should point to a SECTOR_SIZE scratch area
|
||||
Returns 0xffffffff for any error.
|
||||
If pactive is non-NULL, this function also returns the partition active flag.
|
||||
If pptype is non-NULL, this function also returns the partition type.
|
||||
If psize is non-NULL, this function also returns the partition size.
|
||||
*/
|
||||
uint32_t DFS_GetPtnStart(uint8_t unit, uint8_t *scratchsector, uint8_t pnum, uint8_t *pactive, uint8_t *pptype, uint32_t *psize)
|
||||
{
|
||||
uint32_t result=0;
|
||||
PMBR mbr = (PMBR) scratchsector;
|
||||
|
||||
// DOS ptable supports maximum 4 partitions
|
||||
if (pnum > 3)
|
||||
return DFS_ERRMISC;
|
||||
|
||||
// Read MBR from target media
|
||||
if (DFS_ReadSector(unit,scratchsector,0,1)) {
|
||||
return DFS_ERRMISC;
|
||||
}
|
||||
|
||||
// check if jump to boot, VBR
|
||||
if(mbr->bootcode[0]==0xEB || mbr->bootcode[0]==0xE9){
|
||||
// MBR is actually VBR
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = (uint32_t) mbr->ptable[pnum].start_0 |
|
||||
(((uint32_t) mbr->ptable[pnum].start_1) << 8) |
|
||||
(((uint32_t) mbr->ptable[pnum].start_2) << 16) |
|
||||
(((uint32_t) mbr->ptable[pnum].start_3) << 24);
|
||||
|
||||
if (pactive)
|
||||
*pactive = mbr->ptable[pnum].active;
|
||||
|
||||
if (pptype)
|
||||
*pptype = mbr->ptable[pnum].type;
|
||||
|
||||
if (psize)
|
||||
*psize = (uint32_t) mbr->ptable[pnum].size_0 |
|
||||
(((uint32_t) mbr->ptable[pnum].size_1) << 8) |
|
||||
(((uint32_t) mbr->ptable[pnum].size_2) << 16) |
|
||||
(((uint32_t) mbr->ptable[pnum].size_3) << 24);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Retrieve volume info from BPB and store it in a VOLINFO structure
|
||||
You must provide the unit and starting sector of the filesystem, and
|
||||
a pointer to a sector buffer for scratch
|
||||
Attempts to read BPB and glean information about the FS from that.
|
||||
Returns 0 OK, nonzero for any error.
|
||||
*/
|
||||
uint32_t DFS_GetVolInfo(uint8_t unit, uint8_t *scratchsector, uint32_t startsector, PVOLINFO volinfo)
|
||||
{
|
||||
PLBR lbr = (PLBR) scratchsector;
|
||||
volinfo->unit = unit;
|
||||
volinfo->startsector = startsector;
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_GetVolInfo\r\n");
|
||||
#endif
|
||||
|
||||
if(DFS_ReadSector(unit,scratchsector,startsector,1))
|
||||
return DFS_ERRMISC;
|
||||
|
||||
// tag: OEMID, refer dosfs.h
|
||||
// strncpy(volinfo->oemid, lbr->oemid, 8);
|
||||
// volinfo->oemid[8] = 0;
|
||||
|
||||
volinfo->secperclus = lbr->bpb.secperclus;
|
||||
volinfo->reservedsecs = (uint16_t) lbr->bpb.reserved_l |
|
||||
(((uint16_t) lbr->bpb.reserved_h) << 8);
|
||||
|
||||
volinfo->numsecs = (uint16_t) lbr->bpb.sectors_s_l |
|
||||
(((uint16_t) lbr->bpb.sectors_s_h) << 8);
|
||||
|
||||
if (!volinfo->numsecs)
|
||||
volinfo->numsecs = (uint32_t) lbr->bpb.sectors_l_0 |
|
||||
(((uint32_t) lbr->bpb.sectors_l_1) << 8) |
|
||||
(((uint32_t) lbr->bpb.sectors_l_2) << 16) |
|
||||
(((uint32_t) lbr->bpb.sectors_l_3) << 24);
|
||||
|
||||
// If secperfat is 0, we must be in a FAT32 volume; get secperfat
|
||||
// from the FAT32 EBPB. The volume label and system ID string are also
|
||||
// in different locations for FAT12/16 vs FAT32.
|
||||
volinfo->secperfat = (uint16_t) lbr->bpb.secperfat_l |
|
||||
(((uint16_t) lbr->bpb.secperfat_h) << 8);
|
||||
if (!volinfo->secperfat) {
|
||||
volinfo->secperfat = (uint32_t) lbr->ebpb.ebpb32.fatsize_0 |
|
||||
(((uint32_t) lbr->ebpb.ebpb32.fatsize_1) << 8) |
|
||||
(((uint32_t) lbr->ebpb.ebpb32.fatsize_2) << 16) |
|
||||
(((uint32_t) lbr->ebpb.ebpb32.fatsize_3) << 24);
|
||||
|
||||
memcpy(volinfo->label, lbr->ebpb.ebpb32.label, 11);
|
||||
volinfo->label[11] = 0;
|
||||
|
||||
// tag: OEMID, refer dosfs.h
|
||||
// memcpy(volinfo->system, lbr->ebpb.ebpb32.system, 8);
|
||||
// volinfo->system[8] = 0;
|
||||
}
|
||||
else {
|
||||
memcpy(volinfo->label, lbr->ebpb.ebpb.label, 11);
|
||||
volinfo->label[11] = 0;
|
||||
|
||||
// tag: OEMID, refer dosfs.h
|
||||
// memcpy(volinfo->system, lbr->ebpb.ebpb.system, 8);
|
||||
// volinfo->system[8] = 0;
|
||||
}
|
||||
|
||||
// note: if rootentries is 0, we must be in a FAT32 volume.
|
||||
volinfo->rootentries = (uint16_t) lbr->bpb.rootentries_l |
|
||||
(((uint16_t) lbr->bpb.rootentries_h) << 8);
|
||||
|
||||
// after extracting raw info we perform some useful precalculations
|
||||
volinfo->fat1 = startsector + volinfo->reservedsecs;
|
||||
|
||||
// The calculation below is designed to round up the root directory size for FAT12/16
|
||||
// and to simply ignore the root directory for FAT32, since it's a normal, expandable
|
||||
// file in that situation.
|
||||
if (volinfo->rootentries) {
|
||||
volinfo->rootdir = volinfo->fat1 + (volinfo->secperfat * 2);
|
||||
volinfo->dataarea = volinfo->rootdir + (((volinfo->rootentries * 32) + (SECTOR_SIZE - 1)) / SECTOR_SIZE);
|
||||
}
|
||||
else {
|
||||
volinfo->dataarea = volinfo->fat1 + (volinfo->secperfat * 2);
|
||||
volinfo->rootdir = (uint32_t) lbr->ebpb.ebpb32.root_0 |
|
||||
(((uint32_t) lbr->ebpb.ebpb32.root_1) << 8) |
|
||||
(((uint32_t) lbr->ebpb.ebpb32.root_2) << 16) |
|
||||
(((uint32_t) lbr->ebpb.ebpb32.root_3) << 24);
|
||||
}
|
||||
|
||||
// Calculate number of clusters in data area and infer FAT type from this information.
|
||||
volinfo->numclusters = (volinfo->numsecs - volinfo->dataarea) / volinfo->secperclus;
|
||||
if (volinfo->numclusters < 4085)
|
||||
volinfo->filesystem = FAT12;
|
||||
else if (volinfo->numclusters < 65525)
|
||||
volinfo->filesystem = FAT16;
|
||||
else
|
||||
volinfo->filesystem = FAT32;
|
||||
|
||||
return DFS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Fetch FAT entry for specified cluster number
|
||||
You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
|
||||
Returns a FAT32 BAD_CLUSTER value for any error, otherwise the contents of the desired
|
||||
FAT entry.
|
||||
scratchcache should point to a UINT32. This variable caches the physical sector number
|
||||
last read into the scratch buffer for performance enhancement reasons.
|
||||
*/
|
||||
uint32_t DFS_GetFAT(PVOLINFO volinfo, uint8_t *scratch, uint32_t *scratchcache, uint32_t cluster)
|
||||
{
|
||||
uint32_t offset=0, sector=0, result=0;
|
||||
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_GetFAT\r\n");
|
||||
#endif
|
||||
|
||||
if (volinfo->filesystem == FAT12) {
|
||||
offset = cluster + (cluster / 2);
|
||||
}
|
||||
else if (volinfo->filesystem == FAT16) {
|
||||
offset = cluster * 2;
|
||||
}
|
||||
else if (volinfo->filesystem == FAT32) {
|
||||
offset = cluster * 4;
|
||||
}
|
||||
else
|
||||
return 0x0ffffff7; // FAT32 bad cluster
|
||||
|
||||
// at this point, offset is the BYTE offset of the desired sector from the start
|
||||
// of the FAT. Calculate the physical sector containing this FAT entry.
|
||||
sector = ldiv(offset, SECTOR_SIZE).quot + volinfo->fat1;
|
||||
|
||||
// If this is not the same sector we last read, then read it into RAM
|
||||
if (sector != *scratchcache) {
|
||||
if(DFS_ReadSector(volinfo->unit, scratch, sector, 1)) {
|
||||
// avoid anyone assuming that this cache value is still valid, which
|
||||
// might cause disk corruption
|
||||
*scratchcache = 0;
|
||||
return 0x0ffffff7; // FAT32 bad cluster
|
||||
}
|
||||
*scratchcache = sector;
|
||||
}
|
||||
|
||||
// At this point, we "merely" need to extract the relevant entry.
|
||||
// This is easy for FAT16 and FAT32, but a royal PITA for FAT12 as a single entry
|
||||
// may span a sector boundary. The normal way around this is always to read two
|
||||
// FAT sectors, but that luxury is (by design intent) unavailable to DOSFS.
|
||||
offset = ldiv(offset, SECTOR_SIZE).rem;
|
||||
|
||||
if (volinfo->filesystem == FAT12) {
|
||||
// Special case for sector boundary - Store last byte of current sector.
|
||||
// Then read in the next sector and put the first byte of that sector into
|
||||
// the high byte of result.
|
||||
if (offset == SECTOR_SIZE - 1) {
|
||||
result = (uint32_t) scratch[offset];
|
||||
sector++;
|
||||
if(DFS_ReadSector(volinfo->unit, scratch, sector, 1)) {
|
||||
// avoid anyone assuming that this cache value is still valid, which
|
||||
// might cause disk corruption
|
||||
*scratchcache = 0;
|
||||
return 0x0ffffff7; // FAT32 bad cluster
|
||||
}
|
||||
*scratchcache = sector;
|
||||
// Thanks to Claudio Leonel for pointing out this missing line.
|
||||
result |= ((uint32_t) scratch[0]) << 8;
|
||||
}
|
||||
else {
|
||||
result = (uint32_t) scratch[offset] |
|
||||
((uint32_t) scratch[offset+1]) << 8;
|
||||
}
|
||||
if (cluster & 1)
|
||||
result = result >> 4;
|
||||
else
|
||||
result = result & 0xfff;
|
||||
}
|
||||
else if (volinfo->filesystem == FAT16) {
|
||||
result = (uint32_t) scratch[offset] |
|
||||
((uint32_t) scratch[offset+1]) << 8;
|
||||
}
|
||||
else if (volinfo->filesystem == FAT32) {
|
||||
result = ((uint32_t) scratch[offset] |
|
||||
((uint32_t) scratch[offset+1]) << 8 |
|
||||
((uint32_t) scratch[offset+2]) << 16 |
|
||||
((uint32_t) scratch[offset+3]) << 24) & 0x0fffffff;
|
||||
}
|
||||
else
|
||||
result = 0x0ffffff7; // FAT32 bad cluster
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Set FAT entry for specified cluster number
|
||||
You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
|
||||
Returns DFS_ERRMISC for any error, otherwise DFS_OK
|
||||
scratchcache should point to a UINT32. This variable caches the physical sector number
|
||||
last read into the scratch buffer for performance enhancement reasons.
|
||||
|
||||
NOTE: This code is HIGHLY WRITE-INEFFICIENT, particularly for flash media. Considerable
|
||||
performance gains can be realized by caching the sector. However this is difficult to
|
||||
achieve on FAT12 without requiring 2 sector buffers of scratch space, and it is a design
|
||||
requirement of this code to operate on a single 512-byte scratch.
|
||||
|
||||
If you are operating DOSFS over flash, you are strongly advised to implement a writeback
|
||||
cache in your physical I/O driver. This will speed up your code significantly and will
|
||||
also conserve power and flash write life.
|
||||
*/
|
||||
uint32_t DFS_SetFAT(PVOLINFO volinfo, uint8_t *scratch, uint32_t *scratchcache, uint32_t cluster, uint32_t new_contents)
|
||||
{
|
||||
uint32_t offset=0, sector=0, result=0;
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_SetFAT (%lu->%lu)\r\n",cluster,new_contents);
|
||||
#endif
|
||||
|
||||
if (volinfo->filesystem == FAT12) {
|
||||
offset = cluster + (cluster / 2);
|
||||
new_contents &=0xfff;
|
||||
}
|
||||
else if (volinfo->filesystem == FAT16) {
|
||||
offset = cluster * 2;
|
||||
new_contents &=0xffff;
|
||||
}
|
||||
else if (volinfo->filesystem == FAT32) {
|
||||
offset = cluster * 4;
|
||||
new_contents &=0x0fffffff; // FAT32 is really "FAT28"
|
||||
}
|
||||
else
|
||||
return DFS_ERRMISC;
|
||||
|
||||
// at this point, offset is the BYTE offset of the desired sector from the start
|
||||
// of the FAT. Calculate the physical sector containing this FAT entry.
|
||||
sector = ldiv(offset, SECTOR_SIZE).quot + volinfo->fat1;
|
||||
|
||||
// If this is not the same sector we last read, then read it into RAM
|
||||
if (sector != *scratchcache) {
|
||||
if(DFS_ReadSector(volinfo->unit, scratch, sector, 1)) {
|
||||
// avoid anyone assuming that this cache value is still valid, which
|
||||
// might cause disk corruption
|
||||
*scratchcache = 0;
|
||||
return DFS_ERRMISC;
|
||||
}
|
||||
*scratchcache = sector;
|
||||
}
|
||||
|
||||
// At this point, we "merely" need to extract the relevant entry.
|
||||
// This is easy for FAT16 and FAT32, but a royal PITA for FAT12 as a single entry
|
||||
// may span a sector boundary. The normal way around this is always to read two
|
||||
// FAT sectors, but that luxury is (by design intent) unavailable to DOSFS.
|
||||
offset = ldiv(offset, SECTOR_SIZE).rem;
|
||||
|
||||
if (volinfo->filesystem == FAT12) {
|
||||
|
||||
// If this is an odd cluster, pre-shift the desired new contents 4 bits to
|
||||
// make the calculations below simpler
|
||||
if (cluster & 1)
|
||||
new_contents = new_contents << 4;
|
||||
|
||||
// Special case for sector boundary
|
||||
if (offset == SECTOR_SIZE - 1) {
|
||||
|
||||
// Odd cluster: High 12 bits being set
|
||||
if (cluster & 1) {
|
||||
scratch[offset] = (scratch[offset] & 0x0f) | (new_contents & 0xf0);
|
||||
}
|
||||
// Even cluster: Low 12 bits being set
|
||||
else {
|
||||
scratch[offset] = new_contents & 0xff;
|
||||
}
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
|
||||
// mirror the FAT into copy 2
|
||||
if (DFS_OK == result)
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
|
||||
|
||||
// If we wrote that sector OK, then read in the subsequent sector
|
||||
// and poke the first byte with the remainder of this FAT entry.
|
||||
if (DFS_OK == result) {
|
||||
// *scratchcache++;
|
||||
++*scratchcache; // TK: to avoid warning "value computed is not used"
|
||||
result = DFS_ReadSector(volinfo->unit, scratch, *scratchcache, 1);
|
||||
if (DFS_OK == result) {
|
||||
// Odd cluster: High 12 bits being set
|
||||
if (cluster & 1) {
|
||||
scratch[0] = new_contents & 0xff00;
|
||||
}
|
||||
// Even cluster: Low 12 bits being set
|
||||
else {
|
||||
scratch[0] = (scratch[0] & 0xf0) | (new_contents & 0x0f);
|
||||
}
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
|
||||
// mirror the FAT into copy 2
|
||||
if (DFS_OK == result)
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
|
||||
}
|
||||
else {
|
||||
// avoid anyone assuming that this cache value is still valid, which
|
||||
// might cause disk corruption
|
||||
*scratchcache = 0;
|
||||
}
|
||||
}
|
||||
} // if (offset == SECTOR_SIZE - 1)
|
||||
|
||||
// Not a sector boundary. But we still have to worry about if it's an odd
|
||||
// or even cluster number.
|
||||
else {
|
||||
// Odd cluster: High 12 bits being set
|
||||
if (cluster & 1) {
|
||||
scratch[offset] = (scratch[offset] & 0x0f) | (new_contents & 0xf0);
|
||||
scratch[offset+1] = new_contents & 0xff00;
|
||||
}
|
||||
// Even cluster: Low 12 bits being set
|
||||
else {
|
||||
scratch[offset] = new_contents & 0xff;
|
||||
scratch[offset+1] = (scratch[offset+1] & 0xf0) | (new_contents & 0x0f);
|
||||
}
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
|
||||
// mirror the FAT into copy 2
|
||||
if (DFS_OK == result)
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
|
||||
}
|
||||
}
|
||||
else if (volinfo->filesystem == FAT16) {
|
||||
scratch[offset] = (new_contents & 0xff);
|
||||
scratch[offset+1] = (new_contents & 0xff00) >> 8;
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
|
||||
// mirror the FAT into copy 2 - XXX
|
||||
if (DFS_OK == result)
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
|
||||
}
|
||||
else if (volinfo->filesystem == FAT32) {
|
||||
scratch[offset] = (new_contents & 0xff);
|
||||
scratch[offset+1] = (new_contents & 0xff00) >> 8;
|
||||
scratch[offset+2] = (new_contents & 0xff0000) >> 16;
|
||||
scratch[offset+3] = (scratch[offset+3] & 0xf0) | ((new_contents & 0x0f000000) >> 24);
|
||||
// Note well from the above: Per Microsoft's guidelines we preserve the upper
|
||||
// 4 bits of the FAT32 cluster value. It's unclear what these bits will be used
|
||||
// for; in every example I've encountered they are always zero.
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
|
||||
// mirror the FAT into copy 2
|
||||
if (DFS_OK == result)
|
||||
result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
|
||||
}
|
||||
else
|
||||
result = DFS_ERRMISC;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
Convert a filename element from canonical (8.3) to directory entry (11) form
|
||||
src must point to the first non-separator character.
|
||||
dest must point to a 12-byte buffer.
|
||||
*/
|
||||
uint8_t *DFS_CanonicalToDir(uint8_t *dest, uint8_t *src)
|
||||
{
|
||||
uint8_t *destptr = dest;
|
||||
|
||||
memset(dest, ' ', 11);
|
||||
dest[11] = 0;
|
||||
|
||||
while (*src && (*src != DIR_SEPARATOR) && (destptr - dest < 11)) {
|
||||
if (*src >= 'a' && *src <='z') {
|
||||
*destptr++ = (*src - 'a') + 'A';
|
||||
src++;
|
||||
}
|
||||
else if (*src == '.') {
|
||||
src++;
|
||||
destptr = dest + 8;
|
||||
}
|
||||
else {
|
||||
*destptr++ = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/*
|
||||
Find the first unused FAT entry
|
||||
You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
|
||||
Returns a FAT32 BAD_CLUSTER value for any error, otherwise the contents of the desired
|
||||
FAT entry.
|
||||
Returns FAT32 bad_sector (0x0ffffff7) if there is no free cluster available
|
||||
*/
|
||||
uint32_t DFS_GetFreeFAT(PVOLINFO volinfo, uint8_t *scratch)
|
||||
{
|
||||
uint32_t i, result = 0xffffffff, scratchcache = 0;
|
||||
|
||||
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_GetFreeFAT\r\n");
|
||||
#endif
|
||||
|
||||
// Search starts at cluster 2, which is the first usable cluster
|
||||
// NOTE: This search can't terminate at a bad cluster, because there might
|
||||
// legitimately be bad clusters on the disk.
|
||||
for (i=2; i < volinfo->numclusters; i++) {
|
||||
result = DFS_GetFAT(volinfo, scratch, &scratchcache, i);
|
||||
if (!result) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0x0ffffff7; // Can't find a free cluster
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Open a directory for enumeration by DFS_GetNextDirEnt
|
||||
You must supply a populated VOLINFO (see DFS_GetVolInfo)
|
||||
** you must also make sure dirinfo->scratch is valid in the dirinfo you pass it** //reza
|
||||
The empty string or a string containing only the directory separator are
|
||||
considered to be the root directory.
|
||||
Returns 0 OK, nonzero for any error.
|
||||
*/
|
||||
uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo)
|
||||
{
|
||||
// Default behavior is a regular search for existing entries
|
||||
dirinfo->flags = 0;
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_OpenDir\r\n");
|
||||
#endif
|
||||
|
||||
if (!strlen((char *) dirname) || (strlen((char *) dirname) == 1 && dirname[0] == DIR_SEPARATOR)) {
|
||||
if (volinfo->filesystem == FAT32) {
|
||||
dirinfo->currentcluster = volinfo->rootdir;
|
||||
dirinfo->currentsector = 0;
|
||||
dirinfo->currententry = 0;
|
||||
|
||||
// read first sector of directory
|
||||
return DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((volinfo->rootdir - 2) * volinfo->secperclus), 1);
|
||||
}
|
||||
else {
|
||||
dirinfo->currentcluster = 0;
|
||||
dirinfo->currentsector = 0;
|
||||
dirinfo->currententry = 0;
|
||||
|
||||
// read first sector of directory
|
||||
return DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// This is not the root directory. We need to find the start of this subdirectory.
|
||||
// We do this by devious means, using our own companion function DFS_GetNext.
|
||||
else {
|
||||
uint8_t tmpfn[12];
|
||||
uint8_t *ptr = dirname;
|
||||
uint32_t result;
|
||||
DIRENT de;
|
||||
|
||||
if (volinfo->filesystem == FAT32) {
|
||||
dirinfo->currentcluster = volinfo->rootdir;
|
||||
dirinfo->currentsector = 0;
|
||||
dirinfo->currententry = 0;
|
||||
|
||||
// read first sector of directory
|
||||
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((volinfo->rootdir - 2) * volinfo->secperclus), 1))
|
||||
return DFS_ERRMISC;
|
||||
}
|
||||
else {
|
||||
dirinfo->currentcluster = 0;
|
||||
dirinfo->currentsector = 0;
|
||||
dirinfo->currententry = 0;
|
||||
|
||||
// read first sector of directory
|
||||
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir, 1))
|
||||
return DFS_ERRMISC;
|
||||
}
|
||||
|
||||
// skip leading path separators
|
||||
while (*ptr == DIR_SEPARATOR && *ptr)
|
||||
ptr++;
|
||||
|
||||
// Scan the path from left to right, finding the start cluster of each entry
|
||||
// Observe that this code is inelegant, but obviates the need for recursion.
|
||||
while (*ptr) {
|
||||
DFS_CanonicalToDir(tmpfn, ptr);
|
||||
|
||||
de.name[0] = 0;
|
||||
|
||||
do {
|
||||
result = DFS_GetNext(volinfo, dirinfo, &de);
|
||||
} while (!result && memcmp(de.name, tmpfn, 11));
|
||||
|
||||
if (!memcmp(de.name, tmpfn, 11) && ((de.attr & ATTR_DIRECTORY) == ATTR_DIRECTORY)) {
|
||||
if (volinfo->filesystem == FAT32) {
|
||||
dirinfo->currentcluster = (uint32_t) de.startclus_l_l |
|
||||
((uint32_t) de.startclus_l_h) << 8 |
|
||||
((uint32_t) de.startclus_h_l) << 16 |
|
||||
((uint32_t) de.startclus_h_h) << 24;
|
||||
}
|
||||
else {
|
||||
dirinfo->currentcluster = (uint32_t) de.startclus_l_l |
|
||||
((uint32_t) de.startclus_l_h) << 8;
|
||||
}
|
||||
dirinfo->currentsector = 0;
|
||||
dirinfo->currententry = 0;
|
||||
|
||||
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((dirinfo->currentcluster - 2) * volinfo->secperclus), 1))
|
||||
return DFS_ERRMISC;
|
||||
}
|
||||
else if (!memcmp(de.name, tmpfn, 11) && !(de.attr & ATTR_DIRECTORY))
|
||||
return DFS_NOTFOUND;
|
||||
|
||||
// seek to next item in list
|
||||
while (*ptr != DIR_SEPARATOR && *ptr)
|
||||
ptr++;
|
||||
if (*ptr == DIR_SEPARATOR)
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (!dirinfo->currentcluster)
|
||||
return DFS_NOTFOUND;
|
||||
}
|
||||
return DFS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Get next entry in opened directory structure. Copies fields into the dirent
|
||||
structure, updates dirinfo. Note that it is the _caller's_ responsibility to
|
||||
handle the '.' and '..' entries.
|
||||
A deleted file will be returned as a NULL entry (first char of filename=0)
|
||||
by this code. Filenames beginning with 0x05 will be translated to 0xE5
|
||||
automatically. Long file name entries will be returned as NULL.
|
||||
returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid,
|
||||
or DFS_ERRMISC for a media error
|
||||
*/
|
||||
uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent)
|
||||
{
|
||||
uint32_t tempint; // required by DFS_GetFAT
|
||||
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_GetNext\r\n");
|
||||
#endif
|
||||
|
||||
// Do we need to read the next sector of the directory?
|
||||
if (dirinfo->currententry >= SECTOR_SIZE / sizeof(DIRENT)) {
|
||||
dirinfo->currententry = 0;
|
||||
dirinfo->currentsector++;
|
||||
|
||||
// Root directory; special case handling
|
||||
// Note that currentcluster will only ever be zero if both:
|
||||
// (a) this is the root directory, and
|
||||
// (b) we are on a FAT12/16 volume, where the root dir can't be expanded
|
||||
if (dirinfo->currentcluster == 0) {
|
||||
// Trying to read past end of root directory?
|
||||
if (dirinfo->currentsector * (SECTOR_SIZE / sizeof(DIRENT)) >= volinfo->rootentries)
|
||||
return DFS_EOF;
|
||||
|
||||
// Otherwise try to read the next sector
|
||||
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir + dirinfo->currentsector, 1))
|
||||
return DFS_ERRMISC;
|
||||
}
|
||||
|
||||
// Normal handling
|
||||
else {
|
||||
if (dirinfo->currentsector >= volinfo->secperclus) {
|
||||
dirinfo->currentsector = 0;
|
||||
if ((dirinfo->currentcluster >= 0xff7 && volinfo->filesystem == FAT12) ||
|
||||
(dirinfo->currentcluster >= 0xfff7 && volinfo->filesystem == FAT16) ||
|
||||
(dirinfo->currentcluster >= 0x0ffffff7 && volinfo->filesystem == FAT32)) {
|
||||
|
||||
// We are at the end of the directory chain. If this is a normal
|
||||
// find operation, we should indicate that there is nothing more
|
||||
// to see.
|
||||
if (!(dirinfo->flags & DFS_DI_BLANKENT))
|
||||
return DFS_EOF;
|
||||
|
||||
// On the other hand, if this is a "find free entry" search,
|
||||
// we need to tell the caller to allocate a new cluster
|
||||
else
|
||||
return DFS_ALLOCNEW;
|
||||
}
|
||||
dirinfo->currentcluster = DFS_GetFAT(volinfo, dirinfo->scratch, &tempint, dirinfo->currentcluster);
|
||||
}
|
||||
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((dirinfo->currentcluster - 2) * volinfo->secperclus) + dirinfo->currentsector, 1))
|
||||
return DFS_ERRMISC;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(dirent, &(((PDIRENT) dirinfo->scratch)[dirinfo->currententry]), sizeof(DIRENT));
|
||||
|
||||
if (dirent->name[0] == 0) { // no more files in this directory
|
||||
// If this is a "find blank" then we can reuse this name.
|
||||
#if 0
|
||||
if (dirinfo->flags & DFS_DI_BLANKENT)
|
||||
return DFS_OK;
|
||||
#else
|
||||
|
||||
// TK: DFS_GetFreeDirEnt() expects that currententry has been incremented by 1
|
||||
if (dirinfo->flags & DFS_DI_BLANKENT) {
|
||||
dirinfo->currententry++;
|
||||
return DFS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
else
|
||||
return DFS_EOF;
|
||||
}
|
||||
|
||||
if (dirent->name[0] == 0xe5) // handle deleted file entries
|
||||
dirent->name[0] = 0;
|
||||
#if 1
|
||||
// TK: ensure that DFS_GetFreeDirEnt() doesn't return entries with long name
|
||||
else if( dirinfo->flags & DFS_DI_BLANKENT )
|
||||
{} // do nothing..
|
||||
#endif
|
||||
|
||||
else if ((dirent->attr & ATTR_LONG_NAME) == ATTR_LONG_NAME)
|
||||
dirent->name[0] = 0;
|
||||
else if (dirent->name[0] == 0x05) // handle kanji filenames beginning with 0xE5
|
||||
dirent->name[0] = 0xe5;
|
||||
|
||||
dirinfo->currententry++;
|
||||
|
||||
return DFS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
INTERNAL
|
||||
Find a free directory entry in the directory specified by path
|
||||
This function MAY cause a disk write if it is necessary to extend the directory
|
||||
size.
|
||||
Note - di.scratch must be preinitialized to point to a sector scratch buffer
|
||||
de is a scratch structure
|
||||
Returns DFS_ERRMISC if a new entry could not be located or created
|
||||
de is updated with the same return information you would expect from DFS_GetNext
|
||||
*/
|
||||
uint32_t DFS_GetFreeDirEnt(PVOLINFO volinfo, uint8_t *path, PDIRINFO di, PDIRENT de)
|
||||
{
|
||||
uint32_t tempclus=0,i=0;
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_GetFreeDirEnt\r\n");
|
||||
#endif
|
||||
|
||||
if (DFS_OpenDir(volinfo, path, di))
|
||||
return DFS_NOTFOUND;
|
||||
|
||||
// Set "search for empty" flag so DFS_GetNext knows what we're doing
|
||||
di->flags |= DFS_DI_BLANKENT;
|
||||
|
||||
// We seek through the directory looking for an empty entry
|
||||
// Note we are reusing tempclus as a temporary result holder.
|
||||
tempclus = 0;
|
||||
do {
|
||||
tempclus = DFS_GetNext(volinfo, di, de);
|
||||
|
||||
// Empty entry found
|
||||
if (tempclus == DFS_OK && (!de->name[0])) {
|
||||
return DFS_OK;
|
||||
}
|
||||
|
||||
// End of root directory reached
|
||||
else if (tempclus == DFS_EOF)
|
||||
return DFS_ERRMISC;
|
||||
|
||||
else if (tempclus == DFS_ALLOCNEW) {
|
||||
tempclus = DFS_GetFreeFAT(volinfo, di->scratch);
|
||||
if (tempclus == 0x0ffffff7)
|
||||
return DFS_ERRMISC;
|
||||
|
||||
// write out zeroed sectors to the new cluster
|
||||
memset(di->scratch, 0, SECTOR_SIZE);
|
||||
for (i=0;i<volinfo->secperclus;i++) {
|
||||
if (DFS_WriteSector(volinfo->unit, di->scratch, volinfo->dataarea + ((tempclus - 2) * volinfo->secperclus) + i, 1))
|
||||
return DFS_ERRMISC;
|
||||
}
|
||||
// Point old end cluster to newly allocated cluster
|
||||
i = 0;
|
||||
DFS_SetFAT(volinfo, di->scratch, &i, di->currentcluster, tempclus);
|
||||
|
||||
// Update DIRINFO so caller knows where to place the new file
|
||||
di->currentcluster = tempclus;
|
||||
di->currentsector = 0;
|
||||
di->currententry = 1; // since the code coming after this expects to subtract 1
|
||||
|
||||
// Mark newly allocated cluster as end of chain
|
||||
switch(volinfo->filesystem) {
|
||||
case FAT12: tempclus = 0xff8; break;
|
||||
case FAT16: tempclus = 0xfff8; break;
|
||||
case FAT32: tempclus = 0x0ffffff8; break;
|
||||
default: return DFS_ERRMISC;
|
||||
}
|
||||
DFS_SetFAT(volinfo, di->scratch, &i, di->currentcluster, tempclus);
|
||||
}
|
||||
} while (!tempclus);
|
||||
|
||||
// We shouldn't get here
|
||||
return DFS_ERRMISC;
|
||||
}
|
||||
|
||||
/*
|
||||
Open a file for reading or writing. You supply populated VOLINFO, a path to the file,
|
||||
mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to
|
||||
provide a pointer to a sector-sized scratch buffer.
|
||||
Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used
|
||||
to access the file from this point on.
|
||||
*/
|
||||
uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo)
|
||||
{
|
||||
uint8_t tmppath[MAX_PATH];
|
||||
uint8_t filename[12];
|
||||
uint8_t *p;
|
||||
DIRINFO di;
|
||||
DIRENT de;
|
||||
uint32_t temp;
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_OpenFile\r\n");
|
||||
#endif
|
||||
|
||||
// larwe 2006-09-16 +1 zero out file structure
|
||||
memset(fileinfo, 0, sizeof(FILEINFO));
|
||||
|
||||
// save access mode
|
||||
fileinfo->mode = mode;
|
||||
|
||||
// Get a local copy of the path. If it's longer than MAX_PATH, abort.
|
||||
strncpy((char *) tmppath, (char *) path, MAX_PATH);
|
||||
tmppath[MAX_PATH - 1] = 0;
|
||||
if (strcmp((char *) path,(char *) tmppath)) {
|
||||
return DFS_PATHLEN;
|
||||
}
|
||||
|
||||
// strip leading path separators
|
||||
while (tmppath[0] == DIR_SEPARATOR)
|
||||
strcpy((char *) tmppath, (char *) tmppath + 1);
|
||||
|
||||
// Parse filename off the end of the supplied path
|
||||
p = tmppath;
|
||||
while (*(p++));
|
||||
|
||||
p--;
|
||||
while (p > tmppath && *p != DIR_SEPARATOR) // larwe 9/16/06 ">=" to ">" bugfix
|
||||
p--;
|
||||
if (*p == DIR_SEPARATOR)
|
||||
p++;
|
||||
|
||||
DFS_CanonicalToDir(filename, p);
|
||||
|
||||
if (p > tmppath)
|
||||
p--;
|
||||
if (*p == DIR_SEPARATOR || p == tmppath) // larwe 9/16/06 +"|| p == tmppath" bugfix
|
||||
*p = 0;
|
||||
|
||||
// At this point, if our path was MYDIR/MYDIR2/FILE.EXT, filename = "FILE EXT" and
|
||||
// tmppath = "MYDIR/MYDIR2".
|
||||
di.scratch = scratch;
|
||||
if (DFS_OpenDir(volinfo, tmppath, &di))
|
||||
return DFS_NOTFOUND;
|
||||
|
||||
while (!DFS_GetNext(volinfo, &di, &de)) {
|
||||
if (!memcmp(de.name, filename, 11)) {
|
||||
// You can't use this function call to open a directory.
|
||||
if (de.attr & ATTR_DIRECTORY)
|
||||
return DFS_NOTFOUND;
|
||||
|
||||
fileinfo->volinfo = volinfo;
|
||||
fileinfo->pointer = 0;
|
||||
// The reason we store this extra info about the file is so that we can
|
||||
// speedily update the file size, modification date, etc. on a file that is
|
||||
// opened for writing.
|
||||
if (di.currentcluster == 0)
|
||||
fileinfo->dirsector = volinfo->rootdir + di.currentsector;
|
||||
else
|
||||
fileinfo->dirsector = volinfo->dataarea + ((di.currentcluster - 2) * volinfo->secperclus) + di.currentsector;
|
||||
fileinfo->diroffset = di.currententry - 1;
|
||||
if (volinfo->filesystem == FAT32) {
|
||||
fileinfo->cluster = (uint32_t) de.startclus_l_l |
|
||||
((uint32_t) de.startclus_l_h) << 8 |
|
||||
((uint32_t) de.startclus_h_l) << 16 |
|
||||
((uint32_t) de.startclus_h_h) << 24;
|
||||
}
|
||||
else {
|
||||
fileinfo->cluster = (uint32_t) de.startclus_l_l |
|
||||
((uint32_t) de.startclus_l_h) << 8;
|
||||
}
|
||||
fileinfo->firstcluster = fileinfo->cluster;
|
||||
fileinfo->filelen = (uint32_t) de.filesize_0 |
|
||||
((uint32_t) de.filesize_1) << 8 |
|
||||
((uint32_t) de.filesize_2) << 16 |
|
||||
((uint32_t) de.filesize_3) << 24;
|
||||
|
||||
return DFS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we KNOW the file does not exist. If the file was opened
|
||||
// with write access, we can create it.
|
||||
if (mode & DFS_WRITE) {
|
||||
uint32_t cluster;
|
||||
|
||||
// Locate or create a directory entry for this file
|
||||
if (DFS_OK != DFS_GetFreeDirEnt(volinfo, tmppath, &di, &de))
|
||||
return DFS_ERRMISC;
|
||||
|
||||
// put sane values in the directory entry
|
||||
memset(&de, 0, sizeof(de));
|
||||
memcpy(de.name, filename, 11);
|
||||
de.crttime_l = 0x20; // 01:01:00am, Jan 1, 2006.
|
||||
de.crttime_h = 0x08;
|
||||
de.crtdate_l = 0x11;
|
||||
de.crtdate_h = 0x34;
|
||||
de.lstaccdate_l = 0x11;
|
||||
de.lstaccdate_h = 0x34;
|
||||
de.wrttime_l = 0x20;
|
||||
de.wrttime_h = 0x08;
|
||||
de.wrtdate_l = 0x11;
|
||||
de.wrtdate_h = 0x34;
|
||||
|
||||
// allocate a starting cluster for the directory entry
|
||||
cluster = DFS_GetFreeFAT(volinfo, scratch);
|
||||
|
||||
de.startclus_l_l = cluster & 0xff;
|
||||
de.startclus_l_h = (cluster & 0xff00) >> 8;
|
||||
de.startclus_h_l = (cluster & 0xff0000) >> 16;
|
||||
de.startclus_h_h = (cluster & 0xff000000) >> 24;
|
||||
|
||||
// update FILEINFO for our caller's sake
|
||||
fileinfo->volinfo = volinfo;
|
||||
fileinfo->pointer = 0;
|
||||
// The reason we store this extra info about the file is so that we can
|
||||
// speedily update the file size, modification date, etc. on a file that is
|
||||
// opened for writing.
|
||||
if (di.currentcluster == 0)
|
||||
fileinfo->dirsector = volinfo->rootdir + di.currentsector;
|
||||
else
|
||||
fileinfo->dirsector = volinfo->dataarea + ((di.currentcluster - 2) * volinfo->secperclus) + di.currentsector;
|
||||
fileinfo->diroffset = di.currententry - 1;
|
||||
fileinfo->cluster = cluster;
|
||||
fileinfo->firstcluster = cluster;
|
||||
fileinfo->filelen = 0;
|
||||
|
||||
// write the directory entry
|
||||
// note that we no longer have the sector containing the directory entry,
|
||||
// tragically, so we have to re-read it
|
||||
if (DFS_ReadSector(volinfo->unit, scratch, fileinfo->dirsector, 1))
|
||||
return DFS_ERRMISC;
|
||||
memcpy(&(((PDIRENT) scratch)[di.currententry-1]), &de, sizeof(DIRENT));
|
||||
if (DFS_WriteSector(volinfo->unit, scratch, fileinfo->dirsector, 1))
|
||||
return DFS_ERRMISC;
|
||||
|
||||
// Mark newly allocated cluster as end of chain
|
||||
switch(volinfo->filesystem) {
|
||||
case FAT12: cluster = 0xff8; break;
|
||||
case FAT16: cluster = 0xfff8; break;
|
||||
case FAT32: cluster = 0x0ffffff8; break;
|
||||
default: return DFS_ERRMISC;
|
||||
}
|
||||
temp = 0;
|
||||
DFS_SetFAT(volinfo, scratch, &temp, fileinfo->cluster, cluster);
|
||||
|
||||
return DFS_OK;
|
||||
}
|
||||
|
||||
return DFS_NOTFOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
Read an open file
|
||||
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
|
||||
pointer to a SECTOR_SIZE scratch buffer.
|
||||
Note that returning DFS_EOF is not an error condition. This function updates the
|
||||
successcount field with the number of bytes actually read.
|
||||
*/
|
||||
uint32_t DFS_ReadFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len)
|
||||
{
|
||||
uint32_t remain=0;
|
||||
uint32_t result = DFS_OK;
|
||||
uint32_t sector=0;
|
||||
uint32_t bytesread=0;
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_ReadFile\r\n");
|
||||
#endif
|
||||
|
||||
// Don't try to read past EOF
|
||||
if (len > fileinfo->filelen - fileinfo->pointer)
|
||||
len = fileinfo->filelen - fileinfo->pointer;
|
||||
|
||||
remain = len;
|
||||
*successcount = 0;
|
||||
|
||||
while (remain && result == DFS_OK) {
|
||||
// This is a bit complicated. The sector we want to read is addressed at a cluster
|
||||
// granularity by the fileinfo->cluster member. The file pointer tells us how many
|
||||
// extra sectors to add to that number.
|
||||
sector = fileinfo->volinfo->dataarea +
|
||||
((fileinfo->cluster - 2) * fileinfo->volinfo->secperclus) +
|
||||
div(div(fileinfo->pointer,fileinfo->volinfo->secperclus * SECTOR_SIZE).rem, SECTOR_SIZE).quot;
|
||||
|
||||
// Case 1 - File pointer is not on a sector boundary
|
||||
if (div(fileinfo->pointer, SECTOR_SIZE).rem) {
|
||||
uint16_t tempreadsize;
|
||||
|
||||
// We always have to go through scratch in this case
|
||||
result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
|
||||
|
||||
// This is the number of bytes that we actually care about in the sector
|
||||
// just read.
|
||||
tempreadsize = SECTOR_SIZE - (div(fileinfo->pointer, SECTOR_SIZE).rem);
|
||||
|
||||
// Case 1A - We want the entire remainder of the sector. After this
|
||||
// point, all passes through the read loop will be aligned on a sector
|
||||
// boundary, which allows us to go through the optimal path 2A below.
|
||||
if (remain >= tempreadsize) {
|
||||
memcpy(buffer, scratch + (SECTOR_SIZE - tempreadsize), tempreadsize);
|
||||
bytesread = tempreadsize;
|
||||
buffer += tempreadsize;
|
||||
fileinfo->pointer += tempreadsize;
|
||||
remain -= tempreadsize;
|
||||
}
|
||||
// Case 1B - This read concludes the file read operation
|
||||
else {
|
||||
memcpy(buffer, scratch + (SECTOR_SIZE - tempreadsize), remain);
|
||||
|
||||
buffer += remain;
|
||||
fileinfo->pointer += remain;
|
||||
bytesread = remain;
|
||||
remain = 0;
|
||||
}
|
||||
}
|
||||
// Case 2 - File pointer is on sector boundary
|
||||
else {
|
||||
// Case 2A - We have at least one more full sector to read and don't have
|
||||
// to go through the scratch buffer. You could insert optimizations here to
|
||||
// read multiple sectors at a time, if you were thus inclined (note that
|
||||
// the maximum multi-read you could perform is a single cluster, so it would
|
||||
// be advantageous to have code similar to case 1A above that would round the
|
||||
// pointer to a cluster boundary the first pass through, so all subsequent
|
||||
// [large] read requests would be able to go a cluster at a time).
|
||||
if (remain >= SECTOR_SIZE) {
|
||||
result = DFS_ReadSector(fileinfo->volinfo->unit, buffer, sector, 1);
|
||||
remain -= SECTOR_SIZE;
|
||||
buffer += SECTOR_SIZE;
|
||||
fileinfo->pointer += SECTOR_SIZE;
|
||||
bytesread = SECTOR_SIZE;
|
||||
}
|
||||
// Case 2B - We are only reading a partial sector
|
||||
else {
|
||||
result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
|
||||
memcpy(buffer, scratch, remain);
|
||||
buffer += remain;
|
||||
fileinfo->pointer += remain;
|
||||
bytesread = remain;
|
||||
remain = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*successcount += bytesread;
|
||||
|
||||
// check to see if we stepped over a cluster boundary
|
||||
if (div(fileinfo->pointer - bytesread, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot !=
|
||||
div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
|
||||
// An act of minor evil - we use bytesread as a scratch integer, knowing that
|
||||
// its value is not used after updating *successcount above
|
||||
bytesread = 0;
|
||||
if (((fileinfo->volinfo->filesystem == FAT12) && (fileinfo->cluster >= 0xff8)) ||
|
||||
((fileinfo->volinfo->filesystem == FAT16) && (fileinfo->cluster >= 0xfff8)) ||
|
||||
((fileinfo->volinfo->filesystem == FAT32) && (fileinfo->cluster >= 0x0ffffff8)))
|
||||
result = DFS_EOF;
|
||||
else
|
||||
fileinfo->cluster = DFS_GetFAT(fileinfo->volinfo, scratch, &bytesread, fileinfo->cluster);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
Seek file pointer to a given position
|
||||
This function does not return status - refer to the fileinfo->pointer value
|
||||
to see where the pointer wound up.
|
||||
Requires a SECTOR_SIZE scratch buffer
|
||||
*/
|
||||
void DFS_Seek(PFILEINFO fileinfo, uint32_t offset, uint8_t *scratch)
|
||||
{
|
||||
uint32_t tempint;
|
||||
uint16_t endcluster=0; //canny/reza 5/7 fixed
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_Seek\r\n");
|
||||
#endif
|
||||
|
||||
// larwe 9/16/06 bugfix split case 0a/0b and changed fallthrough handling
|
||||
// Case 0a - Return immediately for degenerate case
|
||||
if (offset == fileinfo->pointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Case 0b - Don't allow the user to seek past the end of the file
|
||||
if (offset > fileinfo->filelen) {
|
||||
offset = fileinfo->filelen;
|
||||
// NOTE NO RETURN HERE!
|
||||
}
|
||||
|
||||
// Case 1 - Simple rewind to start
|
||||
// Note _intentional_ fallthrough from Case 0b above
|
||||
if (offset == 0) {
|
||||
fileinfo->cluster = fileinfo->firstcluster;
|
||||
fileinfo->pointer = 0;
|
||||
return; // larwe 9/16/06 +1 bugfix
|
||||
}
|
||||
// Case 2 - Seeking backwards. Need to reset and seek forwards
|
||||
else if (offset < fileinfo->pointer) {
|
||||
fileinfo->cluster = fileinfo->firstcluster;
|
||||
fileinfo->pointer = 0;
|
||||
// NOTE NO RETURN HERE!
|
||||
}
|
||||
|
||||
// Case 3 - Seeking forwards
|
||||
// Note _intentional_ fallthrough from Case 2 above
|
||||
// Case 3a - Seek size does not cross cluster boundary -
|
||||
// very simple case
|
||||
// larwe 9/16/06 changed .rem to .quot in both div calls, bugfix
|
||||
if (div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot ==
|
||||
div(fileinfo->pointer + offset, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
|
||||
fileinfo->pointer = offset;
|
||||
}
|
||||
// Case 3b - Seeking across cluster boundary(ies)
|
||||
else {
|
||||
// round file pointer down to cluster boundary
|
||||
fileinfo->pointer = div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot *
|
||||
fileinfo->volinfo->secperclus * SECTOR_SIZE;
|
||||
|
||||
// seek by clusters
|
||||
// larwe 9/30/06 bugfix changed .rem to .quot in both div calls
|
||||
// canny/reza 5/7 added endcluster related code
|
||||
// TK 2008-12-18: fixed endcluster calculation
|
||||
// old: endcluster = div(fileinfo->pointer + offset, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot;
|
||||
endcluster = div(offset, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot;
|
||||
while (div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot !=endcluster) {
|
||||
fileinfo->cluster = DFS_GetFAT(fileinfo->volinfo, scratch, &tempint, fileinfo->cluster);
|
||||
// Abort if there was an error
|
||||
if (fileinfo->cluster == 0x0ffffff7) {
|
||||
fileinfo->pointer = 0;
|
||||
fileinfo->cluster = fileinfo->firstcluster;
|
||||
return;
|
||||
}
|
||||
fileinfo->pointer += SECTOR_SIZE * fileinfo->volinfo->secperclus;
|
||||
}
|
||||
|
||||
// since we know the cluster is right, we have no more work to do
|
||||
fileinfo->pointer = offset;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Delete a file
|
||||
scratch must point to a sector-sized buffer
|
||||
*/
|
||||
uint32_t DFS_UnlinkFile(PVOLINFO volinfo, uint8_t *path, uint8_t *scratch)
|
||||
{
|
||||
// PDIRENT de = (PDIRENT) scratch;
|
||||
FILEINFO fi;
|
||||
uint32_t cache = 0;
|
||||
uint32_t tempclus = 0;
|
||||
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_UnlinkFile\r\n");
|
||||
#endif
|
||||
|
||||
// DFS_OpenFile gives us all the information we need to delete it
|
||||
if (DFS_OK != DFS_OpenFile(volinfo, path, DFS_READ, scratch, &fi))
|
||||
return DFS_NOTFOUND;
|
||||
|
||||
// First, read the directory sector and delete that entry
|
||||
if (DFS_ReadSector(volinfo->unit, scratch, fi.dirsector, 1))
|
||||
return DFS_ERRMISC;
|
||||
((PDIRENT) scratch)[fi.diroffset].name[0] = 0xe5;
|
||||
if (DFS_WriteSector(volinfo->unit, scratch, fi.dirsector, 1))
|
||||
return DFS_ERRMISC;
|
||||
|
||||
// Now follow the cluster chain to free the file space
|
||||
while (!((volinfo->filesystem == FAT12 && fi.firstcluster >= 0x0ff7) ||
|
||||
(volinfo->filesystem == FAT16 && fi.firstcluster >= 0xfff7) ||
|
||||
(volinfo->filesystem == FAT32 && fi.firstcluster >= 0x0ffffff7))) {
|
||||
tempclus = fi.firstcluster;
|
||||
|
||||
fi.firstcluster = DFS_GetFAT(volinfo, scratch, &cache, fi.firstcluster);
|
||||
DFS_SetFAT(volinfo, scratch, &cache, tempclus, 0);
|
||||
|
||||
}
|
||||
return DFS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Write an open file
|
||||
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
|
||||
pointer to a SECTOR_SIZE scratch buffer.
|
||||
This function updates the successcount field with the number of bytes actually written.
|
||||
*/
|
||||
uint32_t DFS_WriteFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len)
|
||||
{
|
||||
uint32_t remain=0;
|
||||
uint32_t result = DFS_OK;
|
||||
uint32_t sector=0;
|
||||
uint32_t byteswritten=0;
|
||||
#ifdef DEBUG_DOSFS
|
||||
printf("DFS_WriteFile\r\n");
|
||||
#endif
|
||||
|
||||
|
||||
// Don't allow writes to a file that's open as readonly
|
||||
if (!(fileinfo->mode & DFS_WRITE))
|
||||
return DFS_ERRMISC;
|
||||
|
||||
remain = len;
|
||||
*successcount = 0;
|
||||
|
||||
while (remain && result == DFS_OK) {
|
||||
// This is a bit complicated. The sector we want to read is addressed at a cluster
|
||||
// granularity by the fileinfo->cluster member. The file pointer tells us how many
|
||||
// extra sectors to add to that number.
|
||||
sector = fileinfo->volinfo->dataarea +
|
||||
((fileinfo->cluster - 2) * fileinfo->volinfo->secperclus) +
|
||||
div(div(fileinfo->pointer,fileinfo->volinfo->secperclus * SECTOR_SIZE).rem, SECTOR_SIZE).quot;
|
||||
|
||||
// Case 1 - File pointer is not on a sector boundary
|
||||
if (div(fileinfo->pointer, SECTOR_SIZE).rem) {
|
||||
uint16_t tempsize;
|
||||
|
||||
// We always have to go through scratch in this case
|
||||
result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
|
||||
|
||||
// This is the number of bytes that we don't want to molest in the
|
||||
// scratch sector just read.
|
||||
tempsize = div(fileinfo->pointer, SECTOR_SIZE).rem;
|
||||
|
||||
// Case 1A - We are writing the entire remainder of the sector. After
|
||||
// this point, all passes through the read loop will be aligned on a
|
||||
// sector boundary, which allows us to go through the optimal path
|
||||
// 2A below.
|
||||
if (remain >= SECTOR_SIZE - tempsize) {
|
||||
memcpy(scratch + tempsize, buffer, SECTOR_SIZE - tempsize);
|
||||
if (!result)
|
||||
result = DFS_WriteSector(fileinfo->volinfo->unit, scratch, sector, 1);
|
||||
|
||||
byteswritten = SECTOR_SIZE - tempsize;
|
||||
buffer += SECTOR_SIZE - tempsize;
|
||||
fileinfo->pointer += SECTOR_SIZE - tempsize;
|
||||
if (fileinfo->filelen < fileinfo->pointer) {
|
||||
fileinfo->filelen = fileinfo->pointer;
|
||||
}
|
||||
remain -= SECTOR_SIZE - tempsize;
|
||||
}
|
||||
// Case 1B - This concludes the file write operation
|
||||
else {
|
||||
memcpy(scratch + tempsize, buffer, remain);
|
||||
if (!result)
|
||||
result = DFS_WriteSector(fileinfo->volinfo->unit, scratch, sector, 1);
|
||||
|
||||
buffer += remain;
|
||||
fileinfo->pointer += remain;
|
||||
if (fileinfo->filelen < fileinfo->pointer) {
|
||||
fileinfo->filelen = fileinfo->pointer;
|
||||
}
|
||||
byteswritten = remain;
|
||||
remain = 0;
|
||||
}
|
||||
} // case 1
|
||||
// Case 2 - File pointer is on sector boundary
|
||||
else {
|
||||
// Case 2A - We have at least one more full sector to write and don't have
|
||||
// to go through the scratch buffer. You could insert optimizations here to
|
||||
// write multiple sectors at a time, if you were thus inclined. Refer to
|
||||
// similar notes in DFS_ReadFile.
|
||||
if (remain >= SECTOR_SIZE) {
|
||||
result = DFS_WriteSector(fileinfo->volinfo->unit, buffer, sector, 1);
|
||||
remain -= SECTOR_SIZE;
|
||||
buffer += SECTOR_SIZE;
|
||||
fileinfo->pointer += SECTOR_SIZE;
|
||||
if (fileinfo->filelen < fileinfo->pointer) {
|
||||
fileinfo->filelen = fileinfo->pointer;
|
||||
}
|
||||
byteswritten = SECTOR_SIZE;
|
||||
}
|
||||
// Case 2B - We are only writing a partial sector and potentially need to
|
||||
// go through the scratch buffer.
|
||||
else {
|
||||
// If the current file pointer is not yet at or beyond the file
|
||||
// length, we are writing somewhere in the middle of the file and
|
||||
// need to load the original sector to do a read-modify-write.
|
||||
if (fileinfo->pointer < fileinfo->filelen) {
|
||||
result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
|
||||
if (!result) {
|
||||
memcpy(scratch, buffer, remain);
|
||||
result = DFS_WriteSector(fileinfo->volinfo->unit, scratch, sector, 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = DFS_WriteSector(fileinfo->volinfo->unit, buffer, sector, 1);
|
||||
}
|
||||
|
||||
buffer += remain;
|
||||
fileinfo->pointer += remain;
|
||||
if (fileinfo->filelen < fileinfo->pointer) {
|
||||
fileinfo->filelen = fileinfo->pointer;
|
||||
}
|
||||
byteswritten = remain;
|
||||
remain = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*successcount += byteswritten;
|
||||
|
||||
// check to see if we stepped over a cluster boundary
|
||||
if (div(fileinfo->pointer - byteswritten, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot !=
|
||||
div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
|
||||
uint32_t lastcluster;
|
||||
|
||||
// We've transgressed into another cluster. If we were already at EOF,
|
||||
// we need to allocate a new cluster.
|
||||
// An act of minor evil - we use byteswritten as a scratch integer, knowing
|
||||
// that its value is not used after updating *successcount above
|
||||
byteswritten = 0;
|
||||
|
||||
lastcluster = fileinfo->cluster;
|
||||
fileinfo->cluster = DFS_GetFAT(fileinfo->volinfo, scratch, &byteswritten, fileinfo->cluster);
|
||||
|
||||
// Allocate a new cluster?
|
||||
if (((fileinfo->volinfo->filesystem == FAT12) && (fileinfo->cluster >= 0xff8)) ||
|
||||
((fileinfo->volinfo->filesystem == FAT16) && (fileinfo->cluster >= 0xfff8)) ||
|
||||
((fileinfo->volinfo->filesystem == FAT32) && (fileinfo->cluster >= 0x0ffffff8))) {
|
||||
uint32_t tempclus;
|
||||
|
||||
tempclus = DFS_GetFreeFAT(fileinfo->volinfo, scratch);
|
||||
byteswritten = 0; // invalidate cache
|
||||
if (tempclus == 0x0ffffff7)
|
||||
return DFS_ERRMISC;
|
||||
|
||||
// Link new cluster onto file
|
||||
DFS_SetFAT(fileinfo->volinfo, scratch, &byteswritten, lastcluster, tempclus);
|
||||
fileinfo->cluster = tempclus;
|
||||
|
||||
// Mark newly allocated cluster as end of chain
|
||||
switch(fileinfo->volinfo->filesystem) {
|
||||
case FAT12: tempclus = 0xff8; break;
|
||||
case FAT16: tempclus = 0xfff8; break;
|
||||
case FAT32: tempclus = 0x0ffffff8; break;
|
||||
default: return DFS_ERRMISC;
|
||||
}
|
||||
DFS_SetFAT(fileinfo->volinfo, scratch, &byteswritten, fileinfo->cluster, tempclus);
|
||||
|
||||
result = DFS_OK;
|
||||
}
|
||||
// No else clause is required.
|
||||
}
|
||||
}
|
||||
|
||||
// Update directory entry
|
||||
if (DFS_ReadSector(fileinfo->volinfo->unit, scratch, fileinfo->dirsector, 1))
|
||||
return DFS_ERRMISC;
|
||||
((PDIRENT) scratch)[fileinfo->diroffset].filesize_0 = fileinfo->filelen & 0xff;
|
||||
((PDIRENT) scratch)[fileinfo->diroffset].filesize_1 = (fileinfo->filelen & 0xff00) >> 8;
|
||||
((PDIRENT) scratch)[fileinfo->diroffset].filesize_2 = (fileinfo->filelen & 0xff0000) >> 16;
|
||||
((PDIRENT) scratch)[fileinfo->diroffset].filesize_3 = (fileinfo->filelen & 0xff000000) >> 24;
|
||||
if (DFS_WriteSector(fileinfo->volinfo->unit, scratch, fileinfo->dirsector, 1))
|
||||
return DFS_ERRMISC;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// TK: added 2009-02-12
|
||||
Close a file
|
||||
No original function of DosFS driver
|
||||
It has no effect if writing to SD Card, it's only used by the DosFS wrapper in emulation
|
||||
*/
|
||||
uint32_t DFS_Close(PFILEINFO fileinfo)
|
||||
{
|
||||
return DFS_OK;
|
||||
}
|
||||
|
@ -1,405 +0,0 @@
|
||||
/*
|
||||
DOSFS Embedded FAT-Compatible Filesystem
|
||||
(C) 2005 Lewin A.R.W. Edwards (sysadm@zws.com)
|
||||
*/
|
||||
|
||||
#ifndef _DOSFS_H
|
||||
#define _DOSFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//===================================================================
|
||||
// User-supplied functions
|
||||
uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
|
||||
uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
|
||||
|
||||
|
||||
//===================================================================
|
||||
// Configurable items
|
||||
#define MAX_PATH 64 // Maximum path length (increasing this will
|
||||
// GREATLY increase stack requirements!)
|
||||
#define DIR_SEPARATOR '/' // character separating directory components
|
||||
|
||||
// End of configurable items
|
||||
//===================================================================
|
||||
|
||||
//===================================================================
|
||||
// 32-bit error codes
|
||||
#define DFS_OK 0 // no error
|
||||
#define DFS_EOF 1 // end of file (not an error)
|
||||
#define DFS_WRITEPROT 2 // volume is write protected
|
||||
#define DFS_NOTFOUND 3 // path or file not found
|
||||
#define DFS_PATHLEN 4 // path too long
|
||||
#define DFS_ALLOCNEW 5 // must allocate new directory cluster
|
||||
#define DFS_ERRMISC 0xffffffff // generic error
|
||||
|
||||
//===================================================================
|
||||
// File access modes
|
||||
#define DFS_READ 1 // read-only
|
||||
#define DFS_WRITE 2 // write-only
|
||||
|
||||
//===================================================================
|
||||
// Miscellaneous constants
|
||||
#define SECTOR_SIZE 512 // sector size in bytes
|
||||
|
||||
//===================================================================
|
||||
// Internal subformat identifiers
|
||||
#define FAT12 0
|
||||
#define FAT16 1
|
||||
#define FAT32 2
|
||||
|
||||
//===================================================================
|
||||
// DOS attribute bits
|
||||
#define ATTR_READ_ONLY 0x01
|
||||
#define ATTR_HIDDEN 0x02
|
||||
#define ATTR_SYSTEM 0x04
|
||||
#define ATTR_VOLUME_ID 0x08
|
||||
#define ATTR_DIRECTORY 0x10
|
||||
#define ATTR_ARCHIVE 0x20
|
||||
#define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)
|
||||
|
||||
|
||||
/*
|
||||
Directory entry structure
|
||||
note: if name[0] == 0xe5, this is a free dir entry
|
||||
if name[0] == 0x00, this is a free entry and all subsequent entries are free
|
||||
if name[0] == 0x05, the first character of the name is 0xe5 [a kanji nicety]
|
||||
|
||||
Date format: bit 0-4 = day of month (1-31)
|
||||
bit 5-8 = month, 1=Jan..12=Dec
|
||||
bit 9-15 = count of years since 1980 (0-127)
|
||||
Time format: bit 0-4 = 2-second count, (0-29)
|
||||
bit 5-10 = minutes (0-59)
|
||||
bit 11-15= hours (0-23)
|
||||
*/
|
||||
typedef struct _tagDIRENT {
|
||||
uint8_t name[11]; // filename
|
||||
uint8_t attr; // attributes (see ATTR_* constant definitions)
|
||||
uint8_t reserved; // reserved, must be 0
|
||||
uint8_t crttimetenth; // create time, 10ths of a second (0-199 are valid)
|
||||
uint8_t crttime_l; // creation time low byte
|
||||
uint8_t crttime_h; // creation time high byte
|
||||
uint8_t crtdate_l; // creation date low byte
|
||||
uint8_t crtdate_h; // creation date high byte
|
||||
uint8_t lstaccdate_l; // last access date low byte
|
||||
uint8_t lstaccdate_h; // last access date high byte
|
||||
uint8_t startclus_h_l; // high word of first cluster, low byte (FAT32)
|
||||
uint8_t startclus_h_h; // high word of first cluster, high byte (FAT32)
|
||||
uint8_t wrttime_l; // last write time low byte
|
||||
uint8_t wrttime_h; // last write time high byte
|
||||
uint8_t wrtdate_l; // last write date low byte
|
||||
uint8_t wrtdate_h; // last write date high byte
|
||||
uint8_t startclus_l_l; // low word of first cluster, low byte
|
||||
uint8_t startclus_l_h; // low word of first cluster, high byte
|
||||
uint8_t filesize_0; // file size, low byte
|
||||
uint8_t filesize_1; //
|
||||
uint8_t filesize_2; //
|
||||
uint8_t filesize_3; // file size, high byte
|
||||
} DIRENT, *PDIRENT;
|
||||
|
||||
/*
|
||||
Partition table entry structure
|
||||
*/
|
||||
typedef struct _tagPTINFO {
|
||||
uint8_t active; // 0x80 if partition active
|
||||
uint8_t start_h; // starting head
|
||||
uint8_t start_cs_l; // starting cylinder and sector (low byte)
|
||||
uint8_t start_cs_h; // starting cylinder and sector (high byte)
|
||||
uint8_t type; // type ID byte
|
||||
uint8_t end_h; // ending head
|
||||
uint8_t end_cs_l; // ending cylinder and sector (low byte)
|
||||
uint8_t end_cs_h; // ending cylinder and sector (high byte)
|
||||
uint8_t start_0; // starting sector# (low byte)
|
||||
uint8_t start_1; //
|
||||
uint8_t start_2; //
|
||||
uint8_t start_3; // starting sector# (high byte)
|
||||
uint8_t size_0; // size of partition (low byte)
|
||||
uint8_t size_1; //
|
||||
uint8_t size_2; //
|
||||
uint8_t size_3; // size of partition (high byte)
|
||||
} PTINFO, *PPTINFO;
|
||||
|
||||
/*
|
||||
Master Boot Record structure
|
||||
*/
|
||||
typedef struct _tagMBR {
|
||||
uint8_t bootcode[0x1be]; // boot sector
|
||||
PTINFO ptable[4]; // four partition table structures
|
||||
uint8_t sig_55; // 0x55 signature byte
|
||||
uint8_t sig_aa; // 0xaa signature byte
|
||||
} MBR, *PMBR;
|
||||
|
||||
/*
|
||||
BIOS Parameter Block structure (FAT12/16)
|
||||
*/
|
||||
typedef struct _tagBPB {
|
||||
uint8_t bytepersec_l; // bytes per sector low byte (0x00)
|
||||
uint8_t bytepersec_h; // bytes per sector high byte (0x02)
|
||||
uint8_t secperclus; // sectors per cluster (1,2,4,8,16,32,64,128 are valid)
|
||||
uint8_t reserved_l; // reserved sectors low byte
|
||||
uint8_t reserved_h; // reserved sectors high byte
|
||||
uint8_t numfats; // number of FAT copies (2)
|
||||
uint8_t rootentries_l; // number of root dir entries low byte (0x00 normally)
|
||||
uint8_t rootentries_h; // number of root dir entries high byte (0x02 normally)
|
||||
uint8_t sectors_s_l; // small num sectors low byte
|
||||
uint8_t sectors_s_h; // small num sectors high byte
|
||||
uint8_t mediatype; // media descriptor byte
|
||||
uint8_t secperfat_l; // sectors per FAT low byte
|
||||
uint8_t secperfat_h; // sectors per FAT high byte
|
||||
uint8_t secpertrk_l; // sectors per track low byte
|
||||
uint8_t secpertrk_h; // sectors per track high byte
|
||||
uint8_t heads_l; // heads low byte
|
||||
uint8_t heads_h; // heads high byte
|
||||
uint8_t hidden_0; // hidden sectors low byte
|
||||
uint8_t hidden_1; // (note - this is the number of MEDIA sectors before
|
||||
uint8_t hidden_2; // first sector of VOLUME - we rely on the MBR instead)
|
||||
uint8_t hidden_3; // hidden sectors high byte
|
||||
uint8_t sectors_l_0; // large num sectors low byte
|
||||
uint8_t sectors_l_1; //
|
||||
uint8_t sectors_l_2; //
|
||||
uint8_t sectors_l_3; // large num sectors high byte
|
||||
} BPB, *PBPB;
|
||||
|
||||
/*
|
||||
Extended BIOS Parameter Block structure (FAT12/16)
|
||||
*/
|
||||
typedef struct _tagEBPB {
|
||||
uint8_t unit; // int 13h drive#
|
||||
uint8_t head; // archaic, used by Windows NT-class OSes for flags
|
||||
uint8_t signature; // 0x28 or 0x29
|
||||
uint8_t serial_0; // serial#
|
||||
uint8_t serial_1; // serial#
|
||||
uint8_t serial_2; // serial#
|
||||
uint8_t serial_3; // serial#
|
||||
uint8_t label[11]; // volume label
|
||||
uint8_t system[8]; // filesystem ID
|
||||
} EBPB, *PEBPB;
|
||||
|
||||
/*
|
||||
Extended BIOS Parameter Block structure (FAT32)
|
||||
*/
|
||||
typedef struct _tagEBPB32 {
|
||||
uint8_t fatsize_0; // big FAT size in sectors low byte
|
||||
uint8_t fatsize_1; //
|
||||
uint8_t fatsize_2; //
|
||||
uint8_t fatsize_3; // big FAT size in sectors high byte
|
||||
uint8_t extflags_l; // extended flags low byte
|
||||
uint8_t extflags_h; // extended flags high byte
|
||||
uint8_t fsver_l; // filesystem version (0x00) low byte
|
||||
uint8_t fsver_h; // filesystem version (0x00) high byte
|
||||
uint8_t root_0; // cluster of root dir, low byte
|
||||
uint8_t root_1; //
|
||||
uint8_t root_2; //
|
||||
uint8_t root_3; // cluster of root dir, high byte
|
||||
uint8_t fsinfo_l; // sector pointer to FSINFO within reserved area, low byte (2)
|
||||
uint8_t fsinfo_h; // sector pointer to FSINFO within reserved area, high byte (0)
|
||||
uint8_t bkboot_l; // sector pointer to backup boot sector within reserved area, low byte (6)
|
||||
uint8_t bkboot_h; // sector pointer to backup boot sector within reserved area, high byte (0)
|
||||
uint8_t reserved[12]; // reserved, should be 0
|
||||
|
||||
uint8_t unit; // int 13h drive#
|
||||
uint8_t head; // archaic, used by Windows NT-class OSes for flags
|
||||
uint8_t signature; // 0x28 or 0x29
|
||||
uint8_t serial_0; // serial#
|
||||
uint8_t serial_1; // serial#
|
||||
uint8_t serial_2; // serial#
|
||||
uint8_t serial_3; // serial#
|
||||
uint8_t label[11]; // volume label
|
||||
uint8_t system[8]; // filesystem ID
|
||||
} EBPB32, *PEBPB32;
|
||||
|
||||
/*
|
||||
Logical Boot Record structure (volume boot sector)
|
||||
*/
|
||||
typedef struct _tagLBR {
|
||||
uint8_t jump[3]; // JMP instruction
|
||||
uint8_t oemid[8]; // OEM ID, space-padded
|
||||
BPB bpb; // BIOS Parameter Block
|
||||
union {
|
||||
EBPB ebpb; // FAT12/16 Extended BIOS Parameter Block
|
||||
EBPB32 ebpb32; // FAT32 Extended BIOS Parameter Block
|
||||
} ebpb;
|
||||
uint8_t code[420]; // boot sector code
|
||||
uint8_t sig_55; // 0x55 signature byte
|
||||
uint8_t sig_aa; // 0xaa signature byte
|
||||
} LBR, *PLBR;
|
||||
|
||||
/*
|
||||
Volume information structure (Internal to DOSFS)
|
||||
*/
|
||||
typedef struct _tagVOLINFO {
|
||||
uint8_t unit; // unit on which this volume resides
|
||||
uint8_t filesystem; // formatted filesystem
|
||||
|
||||
// These two fields aren't very useful, so support for them has been commented out to
|
||||
// save memory. (Note that the "system" tag is not actually used by DOS to determine
|
||||
// filesystem type - that decision is made entirely on the basis of how many clusters
|
||||
// the drive contains. DOSFS works the same way).
|
||||
// See tag: OEMID in dosfs.c
|
||||
// uint8_t oemid[9]; // OEM ID ASCIIZ
|
||||
// uint8_t system[9]; // system ID ASCIIZ
|
||||
uint8_t label[12]; // volume label ASCIIZ
|
||||
uint32_t startsector; // starting sector of filesystem
|
||||
uint8_t secperclus; // sectors per cluster
|
||||
uint16_t reservedsecs; // reserved sectors
|
||||
uint32_t numsecs; // number of sectors in volume
|
||||
uint32_t secperfat; // sectors per FAT
|
||||
uint16_t rootentries; // number of root dir entries
|
||||
|
||||
uint32_t numclusters; // number of clusters on drive
|
||||
|
||||
// The fields below are PHYSICAL SECTOR NUMBERS.
|
||||
uint32_t fat1; // starting sector# of FAT copy 1
|
||||
uint32_t rootdir; // starting sector# of root directory (FAT12/FAT16) or cluster (FAT32)
|
||||
uint32_t dataarea; // starting sector# of data area (cluster #2)
|
||||
} VOLINFO, *PVOLINFO;
|
||||
|
||||
/*
|
||||
Flags in DIRINFO.flags
|
||||
*/
|
||||
#define DFS_DI_BLANKENT 0x01 // Searching for blank entry
|
||||
|
||||
/*
|
||||
Directory search structure (Internal to DOSFS)
|
||||
*/
|
||||
typedef struct _tagDIRINFO {
|
||||
uint32_t currentcluster; // current cluster in dir
|
||||
uint8_t currentsector; // current sector in cluster
|
||||
uint8_t currententry; // current dir entry in sector
|
||||
uint8_t *scratch; // ptr to user-supplied scratch buffer (one sector)
|
||||
uint8_t flags; // internal DOSFS flags
|
||||
} DIRINFO, *PDIRINFO;
|
||||
|
||||
/*
|
||||
File handle structure (Internal to DOSFS)
|
||||
*/
|
||||
typedef struct _tagFILEINFO {
|
||||
PVOLINFO volinfo; // VOLINFO used to open this file
|
||||
uint32_t dirsector; // physical sector containing dir entry of this file
|
||||
uint8_t diroffset; // # of this entry within the dir sector
|
||||
uint8_t mode; // mode in which this file was opened
|
||||
uint32_t firstcluster; // first cluster of file
|
||||
uint32_t filelen; // byte length of file
|
||||
|
||||
uint32_t cluster; // current cluster
|
||||
uint32_t pointer; // current (BYTE) pointer
|
||||
} FILEINFO, *PFILEINFO;
|
||||
|
||||
/*
|
||||
Get starting sector# of specified partition on drive #unit
|
||||
NOTE: This code ASSUMES an MBR on the disk.
|
||||
scratchsector should point to a SECTOR_SIZE scratch area
|
||||
Returns 0xffffffff for any error.
|
||||
If pactive is non-NULL, this function also returns the partition active flag.
|
||||
If pptype is non-NULL, this function also returns the partition type.
|
||||
If psize is non-NULL, this function also returns the partition size.
|
||||
*/
|
||||
uint32_t DFS_GetPtnStart(uint8_t unit, uint8_t *scratchsector, uint8_t pnum, uint8_t *pactive, uint8_t *pptype, uint32_t *psize);
|
||||
|
||||
/*
|
||||
Retrieve volume info from BPB and store it in a VOLINFO structure
|
||||
You must provide the unit and starting sector of the filesystem, and
|
||||
a pointer to a sector buffer for scratch
|
||||
Attempts to read BPB and glean information about the FS from that.
|
||||
Returns 0 OK, nonzero for any error.
|
||||
*/
|
||||
uint32_t DFS_GetVolInfo(uint8_t unit, uint8_t *scratchsector, uint32_t startsector, PVOLINFO volinfo);
|
||||
|
||||
/*
|
||||
Open a directory for enumeration by DFS_GetNextDirEnt
|
||||
You must supply a populated VOLINFO (see DFS_GetVolInfo)
|
||||
The empty string or a string containing only the directory separator are
|
||||
considered to be the root directory.
|
||||
Returns 0 OK, nonzero for any error.
|
||||
*/
|
||||
uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo);
|
||||
|
||||
/*
|
||||
Get next entry in opened directory structure. Copies fields into the dirent
|
||||
structure, updates dirinfo. Note that it is the _caller's_ responsibility to
|
||||
handle the '.' and '..' entries.
|
||||
A deleted file will be returned as a NULL entry (first char of filename=0)
|
||||
by this code. Filenames beginning with 0x05 will be translated to 0xE5
|
||||
automatically. Long file name entries will be returned as NULL.
|
||||
returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid,
|
||||
or DFS_ERRMISC for a media error
|
||||
*/
|
||||
uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent);
|
||||
|
||||
/*
|
||||
Open a file for reading or writing. You supply populated VOLINFO, a path to the file,
|
||||
mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to
|
||||
provide a pointer to a sector-sized scratch buffer.
|
||||
Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used
|
||||
to access the file from this point on.
|
||||
*/
|
||||
uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo);
|
||||
|
||||
/*
|
||||
Read an open file
|
||||
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
|
||||
pointer to a SECTOR_SIZE scratch buffer.
|
||||
Note that returning DFS_EOF is not an error condition. This function updates the
|
||||
successcount field with the number of bytes actually read.
|
||||
*/
|
||||
uint32_t DFS_ReadFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
|
||||
|
||||
/*
|
||||
Write an open file
|
||||
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
|
||||
pointer to a SECTOR_SIZE scratch buffer.
|
||||
This function updates the successcount field with the number of bytes actually written.
|
||||
*/
|
||||
uint32_t DFS_WriteFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
|
||||
|
||||
/*
|
||||
Seek file pointer to a given position
|
||||
This function does not return status - refer to the fileinfo->pointer value
|
||||
to see where the pointer wound up.
|
||||
Requires a SECTOR_SIZE scratch buffer
|
||||
*/
|
||||
void DFS_Seek(PFILEINFO fileinfo, uint32_t offset, uint8_t *scratch);
|
||||
|
||||
/*
|
||||
Delete a file
|
||||
scratch must point to a sector-sized buffer
|
||||
*/
|
||||
uint32_t DFS_UnlinkFile(PVOLINFO volinfo, uint8_t *path, uint8_t *scratch);
|
||||
|
||||
/*
|
||||
Fetch FAT entry for specified cluster number
|
||||
You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
|
||||
Returns a FAT32 BAD_CLUSTER value for any error, otherwise the contents of the desired
|
||||
FAT entry.
|
||||
scratchcache should point to a UINT32. This variable caches the physical sector number
|
||||
last read into the scratch buffer for performance enhancement reasons.
|
||||
*/
|
||||
uint32_t DFS_GetFAT(PVOLINFO volinfo, uint8_t *scratch, uint32_t *scratchcache, uint32_t cluster);
|
||||
|
||||
/*
|
||||
// TK: added 2009-02-12
|
||||
Close a file
|
||||
No original function of DosFS driver
|
||||
It has no effect if writing to SD Card, it's only used by the DosFS wrapper in emulation
|
||||
*/
|
||||
uint32_t DFS_Close(PFILEINFO fileinfo);
|
||||
|
||||
|
||||
|
||||
// TK: added 2008-18-12
|
||||
// for caching - this feature has to be explicitely enabled, as it isn't reentrant
|
||||
// and requires to use the same buffer pointer whenever reading a file
|
||||
void DFS_CachingEnabledSet(uint8_t enable);
|
||||
|
||||
// TK: added 2008-18-12
|
||||
// missing pendant to DFS_CanonicalToDir
|
||||
char *DFS_DirToCanonical(char *dest, char *src);
|
||||
|
||||
|
||||
// If we are building a host-emulation version, include host support
|
||||
#ifdef HOSTVER
|
||||
#include "hostemu.h"
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _DOSFS_H
|
@ -75,8 +75,8 @@ ifeq ($(MCU),cortex-m3)
|
||||
STMUSBSRCDIR = $(STMUSBDIR)/src
|
||||
STMUSBINCDIR = $(STMUSBDIR)/inc
|
||||
CMSISDIR = $(STMLIBDIR)/CMSIS/Core/CM3
|
||||
DOSFSDIR = $(APPLIBDIR)/dosfs
|
||||
MSDDIR = $(APPLIBDIR)/msd
|
||||
DOSFSDIR = $(PIOSCOMMON)/Libraries/dosfs
|
||||
RTOSDIR = $(PIOSCOMMON)/Libraries/FreeRTOS
|
||||
RTOSSRCDIR = $(RTOSDIR)/Source
|
||||
RTOSINCDIR = $(RTOSSRCDIR)/include
|
||||
|
Loading…
Reference in New Issue
Block a user