Skip to content

Instantly share code, notes, and snippets.

@corstian
Created November 7, 2017 10:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save corstian/3333e8bd5affc15f050811258a20e901 to your computer and use it in GitHub Desktop.
Save corstian/3333e8bd5affc15f050811258a20e901 to your computer and use it in GitHub Desktop.
"This is a low-level driver for MMC and SD cards" + "Driver/Library for a FAT filesystem with a PIC"
/////////////////////////////////////////////////////////////////////////
//// FAT_PIC.C ////
//// ////
//// Driver/Library for a FAT filesystem with a PIC ////
//// ////
//// This Library was designed to resemble standard ANSI C I/O as ////
//// much as possible. There are, however, some caveats to this. ////
//// Please read the comments to make sure the inputs and outputs ////
//// to each function are understood before using anything in ////
//// this library. ////
//// ////
//// This library supports FAT16 and FAT32, but not both at the same ////
//// time (this is a compile option, see options below). It is ////
//// recommended to use FAT32, FAT32 also has been tested more. ////
//// ////
//// Any function with an argument taking in a file name must be in ////
//// the form of... ////
//// "/filename.fil" for a file in the root directory ////
//// "/Directory/filename.fil" for a file in a subdirectory of root ////
//// "/Directory/Subdirectory/filename.fil" and so on... ////
//// ////
//// Any function with an argument taking in a directory name must ////
//// be in the form of... ////
//// "/Dirname/" for a directory in the root directory ////
//// "/Dirname/Subdirname/" for a directory in a subdirectory of ////
//// root and so on... ////
//// ////
//// A compatable media library must be provided. This is ////
//// documented after the User Functions. ////
//// ////
//// -- User Functions -- ////
//// ////
//// fat_init() ////
//// Initializes the FAT library, also initializes the media. ////
//// ////
//// fatopen(char *name, char *mode, FILE *fstream) ////
//// Opens up a FILE stream to a specified file with the specified ////
//// permission mode: ////
//// Permissions: "r" = read ////
//// "w" = write ////
//// "a" = append ////
//// "rb" = read binarily ////
//// "w" will erase all of the data in the file upon ////
//// the opening of the file. ////
//// "a" will tack on all of the data to the end of the ////
//// file. ////
//// "r" will keep on reading until the stream ////
//// hits an '\0' ////
//// "rb" will keep on reading until the amount of ////
//// bytes read equals the size of the file. ////
//// ////
//// Unlike standard C fopen(), this does not malloc a FILE - ////
//// instead the caller will have to have allready allocated a ////
//// a FILE and pass a pointer to it. ////
//// ////
//// fatreopen(char *name, char *mode, FILE *fstream) ////
//// Closes a FILE stream, then reopens the stream with a new file ////
//// and new permissions. ////
//// ////
//// fatclose(FILE *fstream) ////
//// Closes a FILE stream. It is very important to call this ////
//// function when you're done reading or writing to a file. //// ////
//// ////
//// fatgetc(FILE *fstream) ////
//// Gets a character from a stream. An EOF will be returned at ////
//// different times depending on whether or not the stream is ////
//// reading binarily. If not reading binarily: EOF when the ////
//// stream reads a '\0'. If reading binarily: EOF when the amount ////
//// of bytes read equals the size of the file (end of file). ////
//// ////
//// fatputc(char c, FILE *fstream) ////
//// Puts a character into a stream (write to the file). ////
//// Writes are buffered, so the media may not be written to until ////
//// a fatclose(). ////
//// ////
//// char* fatgets(char* str, int num, FILE *fstream) ////
//// Gets characters from a stream until either a '\r', EOF, or ////
//// num - 1 is hit. ////
//// ////
//// fatputs(char* str, FILE *fstream) ////
//// Puts a string into a stream (write a string to the file). ////
//// ////
//// fatprintf(FILE *stream): Printfs the entire stream. ////
//// printf()'s the entire stream (printf()'s the contents of the
//// file).
//// ////
//// fatgetpos(FILE *fstream, fatpos_t *pos) ////
//// Gets the current position of the stream/file, saves to pos. ////
//// ////
//// fatsetpos(FILE *fstream, fatpos_t *pos) ////
//// Sets the current position of the stream/file. ////
//// ////
//// fatseek(FILE *fstream, int32 offset, int origin) ////
//// Sets the current position of the stream according to the ////
//// origin parameter: ////
//// SEEK_CUR: Set position relative to the ////
//// current stream position. ////
//// SEEK_END: Set position relative to the ////
//// end of the stream. ////
//// SEEK_SET: Set position relative to the ////
//// beginning of the stream. ////
//// ////
//// fateof(FILE *fstream) ////
//// Returns non-zero if the stream/file position is at EOF, ////
//// non-zero if there are still data left in the stream. ////
//// ////
//// faterror(FILE *fstream): ////
//// Returns non-zero if there have been errors with the stream, ////
//// zero if the stream has been operating correctly since it has ////
//// been opened. ////
//// ////
//// fatread(void* buffer, int size, int32 num, FILE* fstream) ////
//// Reads size*num chars from the stream, saves to buffer. ////
//// ////
//// fatwrite(void* buffer, int size, int32 num, FILE* fstream) ////
//// Writes size*num chars from buffer to the stream. ////
//// ////
//// fatflush(FILE *fstream) ////
//// Flushes the buffer in a stream. ////
//// ////
//// clearerr(FILE *fstream) ////
//// Clears any error flags in the stream. ////
//// ////
//// rewind(FILE *fstream) ////
//// Send the stream back to the beginning of the file. ////
//// ////
//// fatpos_t fattell(FILE *fstream) ////
//// Returns the current position of the stream. ////
//// ////
//// rm_file(char *fname) ////
//// Removes a file. ////
//// ////
//// rm_dir(char *dirname) ////
//// Removes a directory. ////
//// ////
//// mk_file(char *fname) ////
//// Makes a file, file will be blank. ////
//// ////
//// mk_dir(char *dirname) ////
//// Makes a directory. ////
//// ////
//// format(int32 mediaSize) ////
//// Formats the media into a FAT32 or FAT16 file system. ////
//// If you specify a mediaSize larger than the actual media bad ////
//// things will happen. If you specify a mediaSize smaller than ////
//// the actual media size will simply limit the filesystem from ////
//// using 0 to mediaSize-1. Anything after mediaSize can be used ////
//// by the application (perhaps as a general purpose EEPROM?) ////
//// NOTE: Windows thinks the filesystem is RAW. ////
//// NOTE: This may be a little buggy. ////
//// ////
/////////////////////////////////////////////////////////////////////////
//// ////
//// This library was written to use CCS's MMC/SD library as the ////
//// media source. If you want to use a different media source, ////
//// you must provide the following 4 functions: ////
//// ////
//// int8 mmcsd_init(void); ////
//// Initializes the media. This will be called by fat_init(). ////
//// ////
//// int8 mmcsd_read_bytes(int32 a, int16 s, char *p); ////
//// Read s bytes from p to the media starting at address a. ////
//// ////
//// int8 mmcsd_write_data(int32 a, int16 s, char *p); ////
//// Write s bytes from p to the media starting at address a. ////
//// To maximize throughput on some medias, it's a good idea to ////
//// buffer writes in this function. ////
//// ////
//// int8 mmcsd_flush_buffer(void); ////
//// If your write function is buffering writes, this will flush ////
//// the buffer and write it to the media. ////
//// ////
//// All four functions should return 0 if OK, non-zero if error. ////
//// ////
/////////////////////////////////////////////////////////////////////////
//// (C) Copyright 2007 Custom Computer Services ////
//// This source code may only be used by licensed users of the CCS ////
//// C compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use, ////
//// reproduction or distribution is permitted without written ////
//// permission. Derivative programs created using this software ////
//// in object code form are not restricted in any way. ////
/////////////////////////////////////////////////////////////////////////
// NOTE This library has no concept of what time and date it currently is.
// All files and folders created or modified using this library
// will have invalid/inaccurate timestamps and datestamps.
// NOTE To save on ROM and RAM space, the user of this library will have to
// define what type of FAT they will be working with. The defines are
// in the Useful Defines section below.
// NOTE For faster writing or appending for an application such as a logger,
// uncomment #FAST_FAT below. This will make the FAT library assume
// there is one file on the card to write or append to, thereby
// making writing and appending much faster. Reading is impossible in
// this mode.
// THIS IS NOT TESTED VERY WELL YET!
// NOTE The current maximum file name length (full path) is 32 characters
// long. If longer file names are desired, change the
// MAX_FILE_NAME_LENGTH define below. Creating a file whose full path
// is longer than MAX_FILE_NAME_LENGTH may lead to weird operation. Keep
// in mind that making this define larger will make your RAM usage go
// up.
#ifndef FAT_PIC_C
#define FAT_PIC_C
#include <ctype.h>
#include <string.h>
#case
//////////////////////
/// ///
/// Useful Defines ///
/// ///
//////////////////////
/// Define your FAT type here ///
//#define FAT16
#define FAT32
/// For faster single-file writing, uncomment this line below ///
//#define FAST_FAT
/// Everything else ///
#define MAX_FILE_NAME_LENGTH 0x20 // the maximum length of a file name for our FAT, including /0 terminator
#define STREAM_BUF_SIZE 0x20 // how big the FILE buffer is. 0x20 is optimal
//////////////////////////////////////////////////////////////////
#define EOF -1
#define GOODEC 0
#define fatpos_t int32
#define SEEK_CUR 0
#define SEEK_END 1
#define SEEK_SET 2
////////////////////////
/// ///
/// Global Variables ///
/// ///
////////////////////////
int16
Bytes_Per_Cluster, // number of addressable bytes per cluster
FAT_Start; // when the first FAT begins
int32
Data_Start, // when data starts
FAT_Length, // the length of one FAT
Next_Free_Clust, // where the next free cluster is
Root_Dir; // when the root directory starts
enum filetype
{
Data_File, // the stream is pointing to a binary, data file
Directory, // the stream is pointing to a directory
None // the stream isn't currently pointing to anything
};
enum ioflags
{
Closed = 0x00,
Read = 0x01,
Write = 0x02,
Append = 0x04,
Binary = 0x08,
EOF_Reached = 0x10,
Read_Error = 0x20,
Write_Error = 0x40,
File_Not_Found = 0x80
};
struct iobuf
{
fatpos_t
Bytes_Until_EOF, // how many bytes until the stream's end of file
Cur_Char, // the current byte that the stream is pointing at
Entry_Addr, // the entry address of the file that is associated with the stream
Parent_Start_Addr, // the parent's start adddress of the file that is associated with the stream
Size, // the size of the file that is associated with the stream
Start_Addr; // the beginning of the data in the file that is associated with the stream
enum filetype File_Type; // the type of file that is associated with the stream
enum ioflags Flags; // any associated input/output flag
int Buf[STREAM_BUF_SIZE]; // this is a buffer so that during fatputc() or fatgetc()
// the media won't have to be read at every character
};
typedef struct iobuf FILE;
///////////////////////////
/// ///
/// Function Prototypes ///
/// ///
///////////////////////////
/// Standard C Functions ///
signed int fatopen(char fname[], char mode[], FILE* stream);
signed int fatreopen(char fname[], char mode[], FILE* stream);
signed int fatclose(FILE* stream);
signed int fatgetc(FILE* stream);
signed int fatputc(int ch, FILE* stream);
char* fatgets(char* str, int num, FILE* stream);
signed int fatputs(char* str, FILE* stream);
signed int fatprintf(FILE* stream);
signed int fatgetpos(FILE* stream, fatpos_t* position);
signed int fatsetpos(FILE* stream, fatpos_t* position);
signed int fatseek(FILE* stream, int32 offset, int origin);
signed int fateof(FILE* stream);
signed int faterror(FILE* stream);
signed int fatread(void* buffer, int size, int32 num, FILE* stream);
signed int fatwrite(void* buffer, int size, int32 count, FILE* stream );
signed int fatflush(FILE* stream);
signed int remove(char* fname);
void clearerr(FILE* stream);
void rewind(FILE* stream);
fatpos_t fattell(FILE* stream);
/// Non-Standard C Functions ///
signed int rm_file(char fname[]);
signed int rm_dir(char dname[]);
signed int mk_file(char fname[]);
signed int mk_dir(char dname[]);
/// Functions' Utility Functions ///
signed int set_file(char fname[], int attrib, FILE* stream);
signed int get_file_name(int32 file_entry_addr, char name[]);
signed int set_file_name(int32 parent_dir_addr, int32* entry_addr, char name[]);
signed int get_short_file_name(int32 file_entry_addr, char sname[], int type);
signed int make_short_file_name(int32 parent_dir_addr, char fname[], char sname[]);
int long_name_chksum (int* pFcbName);
signed int check_invalid_char(char fname[]);
#ifdef FAT32
signed int get_next_free_cluster(int32* my_cluster);
signed int dealloc_clusters(int32 start_cluster);
signed int alloc_clusters(int32 start_cluster, int32* new_cluster_addr);
signed int clear_cluster(int32 cluster);
signed int write_fat(int32 cluster, int32 data);
#else // FAT16
signed int get_next_free_cluster(int16* my_cluster);
signed int dealloc_clusters(int16 start_cluster);
signed int alloc_clusters(int16 start_cluster, int32* new_cluster_addr);
signed int clear_cluster(int16 cluster);
signed int write_fat(int16 cluster, int16 data);
#endif // #ifdef FAT32
signed int get_next_file(FILE* stream);
signed int get_prev_file(FILE* stream);
signed int get_next_free_addr(int32* my_addr);
signed int get_next_free_entry(int32* start_addr);
signed int get_next_entry(int32* start_addr);
signed int get_prev_entry(int32* start_addr);
signed int read_buffer(FILE* stream, int* val);
signed int write_buffer(FILE* stream, int val);
void fill_entry(char the_entry[], char val, int8 start_ind);
void disp_timestamp(int16 timestamp);
void disp_datestamp(int16 datestamp);
/// Data Utility Functions ///
signed int fat_init();
#ifdef FAT32
signed int get_next_cluster(int32* my_cluster);
signed int get_prev_cluster(int32* my_cluster);
int32 cluster_to_addr(int32 cluster);
int32 addr_to_cluster(int32 addr);
#else // FAT16
signed int get_next_cluster(int16* my_cluster);
signed int get_prev_cluster(int16* my_cluster);
int32 cluster_to_addr(int16 cluster);
int16 addr_to_cluster(int32 addr);
#endif // #ifdef FAT32
signed int get_next_addr(int32* my_addr);
signed int get_prev_addr(int32* my_addr);
signed int format(int32 DskSize);
/// Debugging Utility Functions ///
signed int disp_folder_contents(char foldername[]);
signed int dump_addr(int32 from, int32 to);
signed int dump_clusters(int32 from, int32 to);
void disp_fat_stats();
signed int fatprintfinfo(FILE* stream);
////////////////////////////////
/// ///
/// Function Implementations ///
/// ///
////////////////////////////////
/// Standard C Functions ///
/*
signed int fatopen(char fname[], char mode[], FILE* stream)
Summary: This will open up a file stream for reading, writing, or appending.
Param fname: The full path of the file to open.
Param mode: The mode to open up the stream into.
"r" = Read
"w" = Write
"a" = Append
"rb", "wb", "ab" = Read, Write, or Append in Binary mode
Param stream: The stream to open up.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
Note: Standard C will make a file in case a file isn't found,
however due to recursion this is not possible in CCSC.
*/
signed int fatopen(char fname[], char mode[], FILE* stream)
{
int fname_parse_pos = 1; // the current index of the fname character
char target_file[MAX_FILE_NAME_LENGTH]; // temporary buffer to hold names of files
FILE cur_stream; // this will be the stream that will be returned if all goes well
#ifndef FAST_FAT
int
depth = 0, // how many subdirectories deep the file is
target_file_parse_pos; // the current index of the target_file character
#endif // #ifndef FAST_FAT
// set flags
#ifdef FAST_FAT
switch(mode[0])
{
case 'w':
cur_stream.Flags = Write;
break;
case 'a':
cur_stream.Flags = Append;
break;
default:
return EOF;
}
// start looking for the file, start at root
cur_stream.Start_Addr = cur_stream.Parent_Start_Addr = Root_Dir;
while(fname[fname_parse_pos] != '\0')
{
target_file[fname_parse_pos - 1] = fname[fname_parse_pos];
fname_parse_pos += 1;
}
target_file[fname_parse_pos] = '\0';
// find the file inside of its subdirectory
if(set_file(target_file, 0x20, &cur_stream) != GOODEC)
{
cur_stream.Flags |= File_Not_Found;
*stream = cur_stream;
return EOF;
}
// at this point, we've found the file
*stream = cur_stream;
return GOODEC;
#else // NO FAST_FAT
switch(mode[0])
{
case 'r':
cur_stream.Flags = Read;
break;
case 'w':
cur_stream.Flags = Write;
break;
case 'a':
cur_stream.Flags = Append;
break;
default:
return EOF;
}
if(mode[1] == 'b')
cur_stream.Flags |= Binary;
// start looking for the file, start at root
cur_stream.Start_Addr = cur_stream.Parent_Start_Addr = Root_Dir;
// figure out how deep we have to go, count how many '/' we have in the string
while(fname[fname_parse_pos] != '\0')
{
if(fname[fname_parse_pos] == '/')
depth++;
fname_parse_pos += 1;
}
// start the fname index at 1 to skip over the '/'
fname_parse_pos = 1;
// open up to the subdirectory, if possible
while(depth > 0)
{
// find the name of our next target directory
target_file_parse_pos = 0;
while(fname[fname_parse_pos] != '/')
{
// check to make sure that we're not at the end of a poorly formatted string
if(fname[fname_parse_pos] == '\0')
return EOF;
// fill up the buffer and increment the indexes
target_file[target_file_parse_pos] = fname[fname_parse_pos];
fname_parse_pos += 1;
target_file_parse_pos += 1;
}
// increment the fname index one more because it's currently pointing at the '/'
fname_parse_pos += 1;
// tack on a \0 to the end of the target file to terminate the string
target_file[target_file_parse_pos] = '\0';
// check to see if the directory exists and open it if possible, otherwise exit because the directory doesn't exist
if(set_file(target_file, 0x10, &cur_stream) != GOODEC)
{
cur_stream.Flags |= File_Not_Found;
*stream = cur_stream;
return EOF;
}
depth -= 1;
}
// check to see if we're trying to open just a directory
if(fname[fname_parse_pos] == '\0')
{
*stream = cur_stream;
return GOODEC;
}
// now that we have the location of the subdirectory that the file is in, attempt to open the file
target_file_parse_pos = 0;
while(fname[fname_parse_pos] != '\0')
{
// fill up the buffer and increment the indexes
target_file[target_file_parse_pos] = fname[fname_parse_pos];
fname_parse_pos += 1;
target_file_parse_pos += 1;
}
// tack on a \0 to the end of the target file to terminate the string
target_file[target_file_parse_pos] = '\0';
// find the file inside of its subdirectory
if(set_file(target_file, 0x20, &cur_stream) != GOODEC)
{
cur_stream.Flags |= File_Not_Found;
*stream = cur_stream;
return EOF;
}
// at this point, we've found the file
*stream = cur_stream;
return GOODEC;
#endif // #ifdef FAST_FAT
}
/*
signed int fatreopen(char fname[], char mode[], FILE* old_stream, FILE* new_stream)
Summary: This will close a stream and then reopen it using new parameters.
Param fname: The full path of the file to open.
Param mode: The mode to open up the stream into.
"r" = Read
"w" = Write
"a" = Append
"rb", "wb", "ab" = Read, Write, or Append in Binary mode
Param stream: The stream to close and reopen.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
Note: Standard C will make a file in case a file isn't found,
however due to recursion this is not possible in CCSC.
*/
signed int fatreopen(char fname[], char mode[], FILE* stream)
{
// close the old stream
if(fatclose(stream) == EOF)
return EOF;
// open the new stream
if(fatopen(fname, mode, stream) == EOF)
return EOF;
return GOODEC;
}
/*
signed int fatclose(FILE* stream)
Summary: Closes a stream and commits any changes done to the file.
Param: The stream to close.
Returns: EOF if there was a problem, 0 if everything went okay.
*/
signed int fatclose(FILE* stream)
{
int ec = 0;
int32 first_cluster;
// commit data back to the stream's entry, if needed
if((stream->Flags & Write) || (stream->Flags & Append))
{
// write the new size of the file
if(mmcsd_write_data(stream->Entry_Addr + 0x1C, 4, &(stream->Size)) != GOODEC)
{
stream->Flags |= Write_Error;
return EOF;
}
// check to see if the first cluster is already linked in the file
ec += mmcsd_read_data(stream->Entry_Addr + 0x14, 2, (int16*)&first_cluster + 1);
ec += mmcsd_read_data(stream->Entry_Addr + 0x1A, 2, &first_cluster);
if(ec != GOODEC)
{
stream->Flags |= Read_Error;
return EOF;
}
// write the first cluster to the entry if needed
if(first_cluster == 0)
{
// convert the start address to a cluster number
first_cluster = addr_to_cluster(stream->Start_Addr);
ec += mmcsd_write_data(stream->Entry_Addr + 0x14, 2, (int16*)&first_cluster + 1);
ec += mmcsd_write_data(stream->Entry_Addr + 0x1A, 2, &first_cluster);
if(ec != GOODEC)
{
stream->Flags |= Write_Error;
return EOF;
}
}
// dump the remaining buffer to the card
if(fatflush(stream) == EOF)
return EOF;
}
// nullify the data
stream->Cur_Char = 0;
stream->Entry_Addr = 0;
stream->Size = 0;
stream->Start_Addr = 0;
stream->Flags = 0;
return 0;
}
/*
signed int fatgetc(FILE* stream)
Summary: Gets a character from a stream.
Param: The stream to get a character from.
Returns: The character that was gotten from the stream,
EOF if the stream has reached the end of the file or doesn't have permissions to read,
*/
signed int fatgetc(FILE* stream)
{
char ch; // character read in
// check to see if the stream has proper permissions to read
if(stream->Flags & Read)
{
// when the number of bytes until eof hit zero, we know we are at the end of any file
if(stream->Bytes_Until_EOF == 0)
{
stream->Flags |= EOF_Reached;
return EOF;
}
// read in the next byte in the buffer
if(read_buffer(stream, &ch) == EOF)
return EOF;
// a 0x00 will signify the end of a non-binary file
if((ch == '\0') && !(stream->Flags & Binary))
{
stream->Flags |= EOF_Reached;
return EOF;
}
// get the next contiguous address of the stream
if(get_next_addr(&(stream->Cur_Char)) != GOODEC)
return EOF;
// we just got 1 byte closer to the end of the file
stream->Bytes_Until_EOF -= 1;
return ch;
}
// if the stream doesn't have proper permissions to read, return an EOF
else
return EOF;
}
/*
signed int fatputc(int ch, FILE* stream)
Summary: Puts a character into a stream.
Param ch: The character to put into the stream.
Param stream: The stream to put a character into.
Returns: The character that was put into the stream,
EOF if the stream doesn't have permissions to write, or if a problem happened.
*/
signed int fatputc(int ch, FILE* stream)
{
// check to see if the stream has proper permissions to write
if(((stream->Flags & Write) || (stream->Flags & Append)) && (stream->File_Type == Data_File))
{
// if there isn't any space allocated yet, allocate some
if(stream->Cur_Char < Data_Start)
{
if(get_next_free_cluster(&Next_Free_Clust) == EOF)
return EOF;
#ifdef FAT32
if(write_fat(Next_Free_Clust, 0x0FFFFFFF) == EOF)
return EOF;
#else // FAT16
if(write_fat(Next_Free_Clust, 0xFFFF) == EOF)
return EOF;
#endif // #ifdef FAT32
if(clear_cluster(Next_Free_Clust) == EOF)
return EOF;
stream->Cur_Char = stream->Start_Addr = cluster_to_addr(Next_Free_Clust);
}
// write the next character to the buffer
if(write_buffer(stream, ch) == EOF)
return EOF;
// get the next address, increment Cur_Char
if(get_next_addr(&(stream->Cur_Char)) == EOF)
{
// write the current buffer to the end of the current cluster
if(mmcsd_write_data(stream->Cur_Char - STREAM_BUF_SIZE + 1, STREAM_BUF_SIZE, stream->Buf) != GOODEC)
{
stream->Flags |= Write_Error;
return EOF;
}
// start looking for a new cluster to allocate
if(alloc_clusters(addr_to_cluster(stream->Cur_Char), &(stream->Cur_Char)) == EOF)
return EOF;
}
// our file just got bigger by 1 byte
stream->Size += 1;
return ch;
}
// if the stream doesn't have proper permissions to write, return an EOF
else
return EOF;
}
/*
char* fatgets(char* str, int num, FILE* stream)
Summary: Reads characters from a stream into a string.
Param str: A pointer to the beginning of the string to put characters into.
Param num: The number of characters to put into the string - 1.
Param stream: The stream to read from.
Returns: A pointer to the most recently added character, or NULL if there was an error.
Note: If a newline is read from the stream, then str will be terminated with a newline.
If num - 1 or EOF is reached, then str will be null terminated.
*/
char* fatgets(char* str, int num, FILE* stream)
{
int i; // counter for loops
// loop until num - 1
for(i = 0; i < num - 1; i += 1)
{
str[i] = fatgetc(stream);
if(str[i] == '\n')
return str;
if(str[i] == EOF)
break;
}
// close off str with a null terminator
str[i] = '\0';
return str;
}
/*
signed int fatputs(char* str, FILE* stream)
Summary: Writes characters from a string into a stream.
Param str: A pointer to the beginning of the string to write into the stream.
Param stream: The stream to write into.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int fatputs(char* str, FILE* stream)
{
int i = 0; // counter for loops
// fatputc every character in the stream
while(str[i] != '\0')
{
if(fatputc(str[i], stream) == EOF)
return EOF;
i += 1;
}
return GOODEC;
}
/*
signed int fatprintf(FILE* stream)
Summary: This will print off the entire contents of the stream to the console.
Param: The stream to print off.
Returns: The last character printed off to the console.
*/
signed int fatprintf(FILE* stream)
{
signed int ch; // character read in
// keep on printf any characters read in as long as we don't run into an end of file or a media error
do
{
ch = fatgetc(stream);
printf("%c", ch);
} while(ch != EOF);
return ch;
}
/*
signed int fatgetpos(FILE* stream, fatpos_t* position)
Summary: Returns the current position of where the stream is pointing to relative to the beginning of the stream.
Param stream: The stream to get the position of.
Param position: A pointer to a variable put the current position of the pointer into.
Returns: 0 on success.
*/
signed int fatgetpos(FILE* stream, fatpos_t* position)
{
*position = stream->Size - stream->Bytes_Until_EOF;
return 0;
}
/*
signed int fatsetpos(FILE* stream, fatpos_t* position)
Summary: Sets the current position of where the stream is pointing to in memory relative to the beginning of the stream.
Param stream: The stream to set the position of.
Param position: A pointer the a variable that has the value of the new position.
Returns: 0 on success, or EOF if there was error.
*/
signed int fatsetpos(FILE* stream, fatpos_t* position)
{
#ifndef FAST_FAT
#ifdef FAT32
int32 cur_cluster; // the current cluster we're pointing to
#else // FAT16
int16 cur_cluster; // the current cluster we're pointing to
#endif // #ifdef FAT32
int32 i; // pointer to memory
#endif // #ifndef FAST_FAT
// check to see if we want to just rewind the file
if(*position == 0)
{
rewind(stream);
return GOODEC;
}
// this whole process is much different and easier if we're writing or appending at a spot after EOF
// this will essentially write null characters to the file from EOF to the desired position
if(((stream->Flags & Write) || (stream->Flags & Append)) && (stream->Size < *position))
{
while(stream->Size < *position)
if(fatputc('\0', stream) == EOF)
return EOF;
return 0;
}
#ifdef FAST_FAT
stream->Cur_Char = stream->Start_Addr + *position;
#else // NO FAST_FAT
// figure out how many clusters into the file the position is to be set to
i = *position / Bytes_Per_Cluster;
cur_cluster = addr_to_cluster(stream->Start_Addr);
// head to that cluster
while(i > 0)
{
if(get_next_cluster(&cur_cluster) != GOODEC)
return EOF;
i -= 1;
}
// head to the correct cluster
stream->Cur_Char = cluster_to_addr(cur_cluster);
// now that we're in the correct cluster, tack on the remaining position
stream->Cur_Char += (*position % Bytes_Per_Cluster);
if(stream->Flags & Read)
{
// we now need to change how far it is until EOF
stream->Bytes_Until_EOF = stream->Size - *position;
// fill up the buffer
if(mmcsd_read_data(stream->Cur_Char, STREAM_BUF_SIZE, stream->Buf) != GOODEC)
{
stream->Flags |= Read_Error;
return EOF;
}
}
else
stream->Size = *position;
#endif // #ifdef FAST_FAT
return 0;
}
/*
signed int fatseek(FILE* stream, int32 offset, int origin)
Summary: This will set the position of the file stream according to the input. The EOF flag will also be cleared.
Param stream: The stream to set the position of.
Param offset: How many bytes relative of origin the file stream position will be set.
Param origin: This will be one of 3 values...
SEEK_CUR: Set position relative to the current stream position.
SEEK_END: Set position relative to the end of the stream.
SEEK_SET: Set position relative to the beginning of the stream.
Returns: 0 on success, or EOF if there was error.
*/
signed int fatseek(FILE* stream, int32 offset, int origin)
{
int32 myoffset; // since fatsetpos requires a pointer to a variable, we need this here
switch(origin)
{
case SEEK_CUR:
myoffset = stream->Cur_Char + offset;
if(fatsetpos(stream, &myoffset) != 0)
return EOF;
break;
case SEEK_END:
myoffset = stream->Size - offset;
if(fatsetpos(stream, &myoffset) != 0)
return EOF;
break;
case SEEK_SET:
myoffset = offset;
if(fatsetpos(stream, &myoffset) != 0)
return EOF;
break;
default:
return EOF;
}
// clear the EOF flag
stream->Flags &= 0xEF;
return GOODEC;
}
/*
signed int fateof(FILE* stream)
Summary: Determines whether or not the stream is at the end of the file.
Param: The stream to query for EOF.
Returns: A non-zero value if the file is at EOF,
0 if the file is not at EOF.
*/
signed int fateof(FILE* stream)
{
return stream->Flags & EOF_Reached;
}
/*
signed int fatread(void* buffer, int size, int32 num, FILE* stream)
Summary: Fills up an array with data from a stream.
Param buffer: A pointer to the beginning of an array of any type.
Param size: How many bytes each element in the array is.
Param num: How many elements to fill in the array.
Param stream: The stream to read from.
Returns: How many values were written to the array.
*/
signed int fatread(void* buffer, int size, int32 num, FILE* stream)
{
int32 i; // counter for loop
// fill up every byte
for(i = 0; i < (num * size); i += 1)
buffer[i] = fatgetc(stream);
return i;
}
/*
signed int fatwrite(void* buffer, int size, int32 count, FILE* stream )
Summary: Fills up a stream with data from an array
Param buffer: A pointer to the beginning of an array of any type.
Param size: How many bytes each element in the array is.
Param num: How many elements to write to the stream.
Param stream: The stream to write to.
Returns: How many values were written to the stream.
*/
signed int fatwrite(void* buffer, int size, int32 count, FILE* stream )
{
int32 i; // counter for loop
// write every byte
for(i = 0; i < (count * (int32)size); i += 1)
if(fatputc(buffer[i], stream) == EOF)
return EOF;
return i;
}
/*
signed int fatflush(FILE* stream)
Summary: Flushes the buffer of a given stream.
Param: The stream to flush the buffer of.
Returns: EOF if there was a problem, 0 if everything went okay
*/
signed int fatflush(FILE* stream)
{
// check to see if we have a buffer
if((stream->Flags & Write) || (stream->Flags & Append))
{
// check to see if we need to flush the buffer
if(stream->Cur_Char % STREAM_BUF_SIZE == 0)
{
// flush the buffer to the card
if(mmcsd_write_data(stream->Cur_Char - STREAM_BUF_SIZE, STREAM_BUF_SIZE, stream->Buf) != GOODEC)
{
stream->Flags |= Write_Error;
return EOF;
}
}
else
{
// flush the buffer to the card
// we need to make sure that the buffer gets flushed into the proper location, hence all this weird % math
if(mmcsd_write_data(stream->Cur_Char - (stream->Cur_Char % STREAM_BUF_SIZE), STREAM_BUF_SIZE, stream->Buf) != GOODEC)
{
stream->Flags |= Write_Error;
return EOF;
}
}
return(mmcsd_flush_buffer());
}
return 0;
}
/*
signed int remove(char* fname)
Summary: Removes a file from disk.
Param: The full path of the file to remove.
Returns: 0 on success, or EOF if there was error.
Note: This function does not work for removing directories, use rm_dir instead.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
*/
signed int remove(char* fname)
{
if(rm_file(fname) == EOF)
return EOF;
return 0;
}
/*
signed int faterror(FILE* stream)
Summary: Checks for an error in a given stream.
Param: The stream to check for an error in.
Returns: A non-zero value of there is an error in the stream,
0 if there is no error in the stream
*/
signed int faterror(FILE* stream)
{
return stream->Flags & 0xF0;
}
/*
void clearerr(FILE* stream)
Summary: Clears off all error in a given stream.
Param: The stream to clear off the errors in.
Returns: Nothing.
*/
void clearerr(FILE* stream)
{
stream->Flags &= 0x0F;
}
/*
void rewind(FILE* stream)
Summary: Sets the stream to point back to the beginning of a file.
Param: The stream to rewind.
Returns: Nothing.
*/
void rewind(FILE* stream)
{
// set the stream back to the beginning
stream->Cur_Char = stream->Start_Addr;
stream->Bytes_Until_EOF = stream->Size;
}
/*
fatpos_t fattell(FILE* stream)
Summary: Returns the current position of where the stream is pointing to relative to the beginning of the stream.
Param: The stream to return the position of.
Returns: The position of where the stream is pointing to relative to the beginning of the stream, or 0 if there was a problem.
*/
fatpos_t fattell(FILE* stream)
{
fatpos_t retval;
if(fatgetpos(stream, &retval) != 0)
return 0;
return retval;
}
/// Non-Standard C Functions ///
/*
signed int rm_file(char fname[])
Summary: Deletes a file.
Param: The full path of the file to delete.
Returns: GOODEC if everything went okay, EOF if there was a problem.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
*/
signed int rm_file(char fname[])
{
int
order,
ulinked_entry = 0xE5; // 0xE5 is put into the file's entry to indicate unlinking
int32 i;
char mode[] = "r"; // r is the safest mode to remove files with
FILE stream; // the stream that we'll be working with
// attempt to open the stream
if(fatopen(fname, mode, &stream) == EOF)
return EOF;
// we need to un-link the file's clusters from the FAT if there are clusters allocated
if(stream.Start_Addr > Root_Dir)
{
if(dealloc_clusters(addr_to_cluster(stream.Start_Addr)) == EOF)
return EOF;
}
// get rid of the first entry
i = stream.Entry_Addr;
if(mmcsd_write_data(i, 1, &ulinked_entry) == EOF)
return EOF;
// check to see if there is a long file name
get_prev_entry(&i);
if(mmcsd_read_data(i + 11, 1, &order) == EOF)
return EOF;
// get rid of all of the long file name entries if they're there
while(order == 0x0F)
{
if(mmcsd_write_data(i, 1, &ulinked_entry) == EOF)
return EOF;
if(get_prev_entry(&i) == EOF)
return EOF;
if(mmcsd_read_data(i + 11, 1, &order) == EOF)
return EOF;
}
if(fatclose(&stream) == EOF)
return EOF;
return GOODEC;
}
/*
signed int rm_dir(char dname[])
Summary: Deletes a directory.
Param: The full path of the directory to delete.
Returns: GOODEC if everything went okay, EOF if there was a problem.
Note: dname must be in the form of /Dirname/ for a directory in the root directory
/Dirname/Subdirname/ for a directory in a subdirectory of root and so on...
Note: This function cannot remove all of the files
and subdirectories of the directory. Manually remove all subdirectories
and files before calling this command.
*/
signed int rm_dir(char dname[])
{
char mode[] = "r"; // r is the safest mode to remove files with
FILE stream; // the stream that we'll be working with
// attempt to open the stream
if(fatopen(dname, mode, &stream) == EOF)
return EOF;
// jump over the . and .. entries in the directory
stream.Entry_Addr = stream.Start_Addr + 64;
// check to make sure that there isn't stuff in this directory
if(get_next_file(&stream) != EOF)
return EOF;
// since removing files is a lot like removing directories, we
// can just call rm_file
if(rm_file(dname) == EOF)
return EOF;
return GOODEC;
}
/*
signed int mk_file(char fname[])
Summary: Creates a file.
Param: The full path of the file to create.
Returns: GOODEC if everything went okay, EOF if there was a problem.
Note: This function will not create directories if parent directories don't exist.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
*/
signed int mk_file(char fname[])
{
char
filename[MAX_FILE_NAME_LENGTH], // the file name we're trying to make
mode[] = "r"; // reading is the safest mode to work in
int
buf, // buffer to hold values
entire_entry[0x20],// entire first entry
filename_pos = 0, // the current parse position of the file name we're trying to create
fname_pos; // the current parse position of the input the the function
int32 i; // pointer to memory
FILE stream; // the stream that we'll be working with
// attempt to open up to the directory
if(fatopen(fname, mode, &stream) == GOODEC)
return EOF; // we shouldn't get an GOODEC back from fatopen()
// check to see if the file is already there.
if(!(stream.Flags & File_Not_Found))
return EOF;
// make a file name
fname_pos = strrchr(fname, '/') - fname + 1;
while((fname[fname_pos] != '\0') && (filename_pos < MAX_FILE_NAME_LENGTH))
{
filename[filename_pos] = fname[fname_pos];
fname_pos += 1;
filename_pos += 1;
}
filename[filename_pos] = '\0';
// write the name
if(set_file_name(stream.Start_Addr, &i, filename) == EOF)
return EOF;
// throw in some values in the file's first entry
for(buf = 0; buf < 0x20; buf += 1)
entire_entry[buf] = 0;
// this is a file
entire_entry[0x0B] = 0x20;
// read what set_file_name gave us for the short name
if(mmcsd_read_data(i, 11, entire_entry) != GOODEC)
return EOF;
// write the entry
if(mmcsd_write_data(i, 0x20, entire_entry) != GOODEC)
return EOF;
return GOODEC;
}
/*
signed int mk_dir(char dname[])
Summary: Creates a directory.
Param: The full path of the directory to create.
Returns: GOODEC if everything went okay, EOF if there was a problem.
Note: This function will not create directories if parent directories don't exist.
Note: dname must be in the form of /Dirname/ for a directory in the root directory
/Dirname/Subdirname/ for a directory in a subdirectory of root and so on...
*/
signed int mk_dir(char dname[])
{
char
dirname[MAX_FILE_NAME_LENGTH], // the directory name we're trying to make
entire_entry[0x20], // used to hold the link entries (. and ..) to the directory and its parent
mode[] = "r"; // reading is the safest mode to work in
int
dirname_pos = 0, // the current parse position of the directory name we're trying to create
dname_pos, // the current parse position of the input the the function
j; // counter for loops
int32 i; // pointer to memory
FILE stream; // the stream that we'll be working with
// attempt to open up to the directory
if(fatopen(dname, mode, &stream) == GOODEC)
return EOF; // we shouldn't get an GOODEC back from fatopen()
// check to see if the directory is already there.
if(!(stream.Flags & File_Not_Found))
return EOF;
// make the directory name
dname_pos = strrchr(dname, '/') - dname;
dname[dname_pos] = '\0';
dname_pos = strrchr(dname, '/') - dname + 1;
while((dname[dname_pos] != '\0') && (dirname_pos < MAX_FILE_NAME_LENGTH))
{
dirname[dirname_pos] = dname[dname_pos];
dname_pos += 1;
dirname_pos += 1;
}
dirname[dirname_pos] = '\0';
dname[dname_pos] = '/';
// write the file's name
if(set_file_name(stream.Start_Addr, &i, dirname) == EOF)
return EOF;
// find and allocate an open cluster
if(get_next_free_cluster(&Next_Free_Clust) == EOF)
return EOF;
if(clear_cluster(Next_Free_Clust) == EOF)
return EOF;
#ifdef FAT32
if(write_fat(Next_Free_Clust, 0x0FFFFFFF) == EOF)
return EOF;
#else // FAT16
if(write_fat(Next_Free_Cluster, 0xFFFF) == EOF)
return EOF;
#endif // #ifdef FAT32
// throw in some values in the file's first entry
for(j = 0; j < 0x20; j += 1)
entire_entry[j] = 0;
// this is a directory
entire_entry[0x0B] = 0x10;
entire_entry[0x1A] = make8(Next_Free_Clust, 0);
entire_entry[0x1B] = make8(Next_Free_Clust, 1);
#ifdef FAT32
entire_entry[0x14] = make8(Next_Free_Clust, 2);
entire_entry[0x15] = make8(Next_Free_Clust, 3);
#endif // #ifdef FAT32
if(mmcsd_read_data(i, 11, entire_entry) != GOODEC)
return EOF;
// write the file's first entry
if(mmcsd_write_data(i, 0x20, entire_entry) != GOODEC)
return EOF;
// make the two links that point to the directory and the directory's parent
i = cluster_to_addr(Next_Free_Clust);
// put in the first link that points to the directory itself
for(j = 0; j < 0x20; j += 1)
{
if(j < 0x01)
entire_entry[j] = '.';
else if(j < 0x0B)
entire_entry[j] = 0x20;
else if(j == 0x0B)
entire_entry[j] = 0x10;
else
entire_entry[j] = 0x00;
}
entire_entry[0x1A] = make8(Next_Free_Clust, 0);
entire_entry[0x1B] = make8(Next_Free_Clust, 1);
#ifdef FAT32
entire_entry[0x14] = make8(Next_Free_Clust, 2);
entire_entry[0x15] = make8(Next_Free_Clust, 3);
#endif // #ifdef FAT32
if(mmcsd_write_data(i, 0x20, entire_entry) != GOODEC)
return EOF;
for(j = 0; j < 0x0C; j += 1)
{
if(j < 0x02)
entire_entry[j] = '.';
else if(j < 0x0B)
entire_entry[j] = 0x20;
else
entire_entry[j] = 0x10;
}
if(stream.Parent_Start_Addr == Root_Dir)
{
entire_entry[0x14] = 0x00;
entire_entry[0x15] = 0x00;
entire_entry[0x1A] = 0x00;
entire_entry[0x1B] = 0x00;
}
else
{
entire_entry[0x1A] = make8(addr_to_cluster(stream.Parent_Start_Addr), 0);
entire_entry[0x1B] = make8(addr_to_cluster(stream.Parent_Start_Addr), 1);
#ifdef FAT32
entire_entry[0x14] = make8(addr_to_cluster(stream.Parent_Start_Addr), 2);
entire_entry[0x15] = make8(addr_to_cluster(stream.Parent_Start_Addr), 3);
#endif // #ifdef FAT32
}
if(mmcsd_write_data(i + 0x20, 0x20, entire_entry) != GOODEC)
return EOF;
return GOODEC;
}
/// Functions' Utility Functions ///
/// NOTE: A library user should not need to use any of the functions in this section ///
/*
signed int set_file(char fname[], int attrib, FILE* stream)
Summary: This will set the stream to point to the specified file.
Param fname: The file name to search for.
Param attrib: The file attributes to search for. 0x10 is a directory, 0x20 is a file.
Param stream: The stream to set.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: The stream has to be pointing to the parent directory's start address when coming in to this function.
*/
signed int set_file(char fname[], int attrib, FILE* stream)
{
int
cur_attrib, // the attribute of the most recently read entry
cur_state, // the state of the most recently read entry
ec = 0; // error checking byte
int32 i; // pointer to memory
#ifndef FAST_FAT
char name_buffer[MAX_FILE_NAME_LENGTH]; // buffer to hold in the most recently read in name
#endif // #ifndef FAST_FAT
// set the memory pointer to the parent start address
i = stream->Start_Addr;
// search for the name of our target file inside of the parent directory
do
{
// read the state and the attribute of the current entry
ec += mmcsd_read_data(i, 1, &cur_state);
ec += mmcsd_read_data(i + 0x0B, 1, &cur_attrib);
if(ec != GOODEC)
{
stream->Flags |= Read_Error;
return EOF;
}
// check to make sure that this entry exists and the entry is the type we're looking for
if((cur_state != 0xE5) && (cur_attrib == attrib))
{
#ifndef FAST_FAT
// get the long file name of the current entry
if(get_file_name(i, name_buffer) == EOF)
return EOF;
// if the target entry and the long file name are equal, strcmp will return a zero
if(strcmp(fname, name_buffer) == 0)
#endif // #ifndef FAST_FAT
{
// we have found our target entry, set the current entry and break out of this function
// set stream's parent address
stream->Parent_Start_Addr = stream->Start_Addr;
ec += mmcsd_read_data(i + 0x1C, 4, &(stream->Size));
// stream->Start_Addr is going to temporarily have a cluster number
ec += mmcsd_read_data(i + 0x14, 2, (int16*)&stream->Start_Addr + 1);
ec += mmcsd_read_data(i + 0x1A, 2, &stream->Start_Addr);
if(ec != GOODEC)
{
stream->Flags |= Read_Error;
return EOF;
}
// convert stream->Start_Addr to an address
stream->Start_Addr = cluster_to_addr(stream->Start_Addr);
stream->Entry_Addr = i;
stream->Bytes_Until_EOF = stream->Size;
// set up some permission-specific parameters if we're at a file
if(attrib == 0x20)
{
stream->File_Type = Data_File;
if(stream->Flags & Write)
{
// delete all previous data in the file
stream->Bytes_Until_EOF = stream->Size = 0;
// if there is already space allocated, get rid of it
if(stream->Start_Addr >= Data_Start)
if(dealloc_clusters(addr_to_cluster(stream->Start_Addr)) == EOF)
return EOF;
stream->Cur_Char = 0;
}
if((stream->Flags & Append) && (stream->Size != 0))
{
// set the position to the end of the file and fill the buffer with the contents of the end of the file
ec = fatsetpos(stream, &(stream->Size));
if(stream->Cur_Char % STREAM_BUF_SIZE == 0)
ec += mmcsd_read_data(stream->Cur_Char - STREAM_BUF_SIZE, STREAM_BUF_SIZE, stream->Buf);
else
ec += mmcsd_read_data(stream->Cur_Char - (stream->Cur_Char % STREAM_BUF_SIZE), STREAM_BUF_SIZE, stream->Buf);
}
#ifndef FAST_FAT
if(stream->Flags & Read)
{
stream->Cur_Char = stream->Start_Addr;
// fill up the read buffer for reading
ec = mmcsd_read_data(stream->Cur_Char, STREAM_BUF_SIZE, stream->Buf);
}
#endif // #ifndef FAST_FAT
if(ec != GOODEC)
{
stream->Flags |= Read_Error;
return EOF;
}
}
else
stream->File_Type = Directory;
return GOODEC;
}
}
// check to make sure that the next iteration will give us a contiguous cluster
if(get_next_entry(&i) == EOF)
return EOF;
} while(cur_state != 0x00);
// if we reach this point, we know that the file won't be found
stream->Flags |= File_Not_Found;
return EOF;
}
/*
signed int get_file_name(int32 file_entry_addr, char name[])
Summary: This will get a name of a file.
Param file_entry_addr: The position in memory that the file's entry is.
Param name[]: The place to put the name of the file into.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int get_file_name(int32 file_entry_addr, char name[])
{
int
j, // counter for loops
k = 0, // current character in array
order, // byte to hold the current long file name order
type; // the type of entry that was just read in
int32 i; // pointer for memory
// the long file name isn't fragmented across clusters
i = file_entry_addr;
// check to make sure that this file has a long file name
if(mmcsd_read_data(i - 0x15, 1, &type) != GOODEC)
return EOF;
if(type != 0x0F)
{
// this file doesn't have a long file name
if(get_short_file_name(i, name, type) == EOF)
return EOF;
return GOODEC;
}
do
{
// head to the previous entry
if(get_prev_entry(&i) == EOF)
return EOF;
for(j = 1; j < 0x20; j += 2, k += 1)
{
if(j == 11)
j = 14;
else if(j == 26)
j = 28;
if(mmcsd_read_data(j + i, 1, &(name[k])) != GOODEC)
return EOF;
}
// now that that's done with, get the entry's order
if(mmcsd_read_data(i, 1, &order) != GOODEC)
return EOF;
} while(!(order & 0x40)); // the last entry will be 0b01xxxxxx
// end the name[] buffer with a \0
name[k] = '\0';
return GOODEC;
}
/*
signed int set_file_name(int32 parent_dir_addr, int32 entry_addr, char name[])
Summary: Creates both a short and long file name at the first free entry in a directory.
Param parent_dir_addr: The address of the parent directory to create the short file name in.
Param entry_addr: The address the function put the short file name entry.
Param name: The full file name.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int set_file_name(int32 parent_dir_addr, int32* entry_addr, char name[])
{
char sname[12]; // place to hold the short file name
signed int name_pos = 0; // the current parse position of name[]
int
chksum, // the long file name checksum
entire_entry[0x20], // the entire entry to put write onto the media
entry_pos, // the current position inside of entire_entry
long_entry = 1; // the current long entry number we're on
int32 i; // pointer to memory
// check for invalid characters
if(check_invalid_char(name) == EOF)
return EOF;
// make a short file name of this
if(make_short_file_name(parent_dir_addr, name, sname) == EOF)
return EOF;
// get a checksum for the long file name entries
chksum = long_name_chksum(sname);
// start writing the long file name
// zero out entry[]
for(entry_pos = 0; entry_pos < 0x20; entry_pos += 1)
entire_entry[entry_pos] = 0;
i = parent_dir_addr;
if(get_next_free_entry(&i) == EOF)
return EOF;
// 0x0F signifies an file entry
entire_entry[11] = 0x0F;
// since we're working in reverse, write the final long name entry
name_pos = strlen(name);
name_pos %= 13;
if(name_pos != 0)
{
// add 1 to account for the \0 terminator
name_pos += 1;
fill_entry(entire_entry, 0xFF, name_pos);
}
// start writing the long file name entries
name_pos = strlen(name);
// figure out how many entries this name will take up
long_entry = (name_pos / 13) + 1;
if(name_pos % 13 == 0)
long_entry -= 1;
// set the bit to signify this is the final entry
long_entry |= 0x40;
while(name_pos >= 0)
{
entry_pos = name_pos % 13;
if(entry_pos < 5)
entire_entry[(entry_pos << 1) + 1] = name[name_pos];
else if(entry_pos < 11)
entire_entry[(entry_pos << 1) + 4] = name[name_pos];
else
entire_entry[(entry_pos << 1) + 6] = name[name_pos];
if((entry_pos == 0)
&& (name_pos != strlen (name)))
{
entire_entry[0] = long_entry;
// clear off the bits at positions 6 and 7 if the most recent entry was the final one.
if(name_pos != 0)
long_entry &= 0x3F;
long_entry -= 1;
entire_entry[13] = chksum;
if(mmcsd_write_data(i, 0x20, entire_entry) != GOODEC)
return EOF;
if(get_next_free_entry(&i) == EOF)
return EOF;
fill_entry(entire_entry, 0x00, 0);
}
name_pos -= 1;
}
// write the short file name to the entry
if(mmcsd_write_data(i, 11, sname) != GOODEC)
return EOF;
// set the new entry addr
*entry_addr = i;
return GOODEC;
}
/*
signed int get_short_file_name(int32 file_entry_addr, char sname[], int type)
Summary: Reads a file's short file name, puts all the characters into lower case, and puts it into a buffer.
Param file_entry_addr: Where the file's entry address is located.
Param sname: The buffer to put the short file name.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int8 get_short_file_name(int32 file_entry_addr, char sname[], int8 type)
{
int8
buf,
i,
j = 0;
// one short file name has, at the most, 11 characters
for(i = 0; i < 11; ++i)
{
// read in a character
if(mmcsd_read_data(i + file_entry_addr, 1, &buf) != GOODEC)
return EOF;
// convert the character
if(buf != ' ')
{
if (i == 8 && type != 0x10) sname[j++] = '.';
sname[j++] = tolower(buf);
}
}
if (sname[j - 1] == '.') --j;
sname[j] = '\0';
return GOODEC;
}
/*
signed int make_short_file_name(int32 parent_dir_addr, char fname[], char sname[])
Summary: Creates a unique short file name in a directory.
Param parent_dir_addr: The address of the parent directory to create the short file name in.
Param fname: The full file name.
Param sname: Character array that will hold the short file name upon function completion.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int make_short_file_name(int32 parent_dir_addr, char fname[], char sname[])
{
char
val[12] = " ",
cur_fname[12] = " ",
cur_fnum[7] = " ";
int
buf,
ext_pos,
fname_parse_pos = 0,
val_parse_pos = 0;
int32
fnum = 0,
i;
// figure out where the extension position is
ext_pos = strchr(fname, '.');
// check to see if this file has an extension
if(ext_pos == 0)
{
while((val_parse_pos < 8) && (fname[fname_parse_pos] != '\0'))
{
val[val_parse_pos] = toupper(fname[fname_parse_pos]);
val_parse_pos += 1;
fname_parse_pos += 1;
// we can't have a '.' or ' ' in the short name
while((fname[fname_parse_pos] == '.') || (fname[fname_parse_pos] == ' '))
fname_parse_pos += 1;
}
}
else
{
ext_pos -= fname - 1;
while((val_parse_pos < 11) && (fname[fname_parse_pos] != '\0'))
{
val[val_parse_pos] = toupper(fname[fname_parse_pos]);
val_parse_pos += 1;
fname_parse_pos += 1;
// we can't have a '.' or ' ' in the short name
while((fname[fname_parse_pos] == '.') || (fname[fname_parse_pos] == ' '))
fname_parse_pos += 1;
// check to see if it's time to skip val_parse_pos ahead to the file extension
if(fname_parse_pos == ext_pos)
val_parse_pos = 8;
// check to see if it's time to skip name_parse_pos ahead to the file extension
else if(val_parse_pos == 8)
fname_parse_pos = ext_pos;
}
}
// now that we've got the short file name, we need to make it unique
i = parent_dir_addr;
if(mmcsd_read_data(i + 0x0B, 1, &buf) != GOODEC)
return EOF;
// keep reading until we hit empty space
while(buf != 0x00)
{
// check to see if this is a short file name entry
if((buf == 0x20) || (buf == 0x10))
{
// read in the short file name that we're currently pointing at
if(mmcsd_read_data(i, 11, cur_fname) != GOODEC)
return EOF;
cur_fname[11] = '\0';
// strcmp will return a 0 if the file name we're currently pointing at and the file name that we created above are the same
if(strcmp(cur_fname, val) == 0)
{
// we now need to create a unique file name
// increment the unique file number by one
fnum += 1;
// convert the unique file number to a string
sprintf(cur_fnum, "%lu", fnum);
// put the unique file number, along with a '~' into our short file name
fname_parse_pos = 0;
// find out the last posiiton of a space
val_parse_pos = strchr(val, ' ');
if(val_parse_pos == 0)
// if there isn't a space, then we're going to have to put the ~x at the end of the short name
val_parse_pos = 7 - strlen(cur_fnum);
else
// if there is a space, then put the ~x there
val_parse_pos -= val + 2;
// make some room for extra digits
buf = 10;
while(fnum >= buf)
{
val_parse_pos -= 1;
buf *= 10;
}
// write in the ~
val[val_parse_pos] = '~';
// write in the number
val_parse_pos += 1;
while(cur_fnum[fname_parse_pos] != '\0')
{
val[val_parse_pos] = cur_fnum[fname_parse_pos];
val_parse_pos += 1;
fname_parse_pos += 1;
}
// start the search over again to see if that unique file name/number combination is still taken up
i = parent_dir_addr;
}
}
// head to the next entry
if(get_next_entry(&i) == EOF)
{
// we're going to have to allocate another cluster
if(alloc_clusters(addr_to_cluster(i), &i) == EOF)
return EOF;
}
if(mmcsd_read_data(i + 0x0B, 1, &buf) != GOODEC)
return EOF;
}
// copy the short name into the input buffer
for(i = 0; i < 12; i += 1)
sname[i] = val[i];
return GOODEC;
}
/*
int long_name_chksum (int* FcbName)
Summary: Returns an unsigned byte checksum computed on an unsigned byte
array. The array must be 11 bytes long and is assumed to contain
a name stored in the format of a MS-DOS directory entry.
Param: Pointer to an unsigned byte array assumed to be 11 bytes long.
Returns: Sum An 8-bit unsigned checksum of the array pointed to by pFcbName.
*/
int long_name_chksum (int* pFcbName)
{
int
FcbNameLen,
Sum = 0;
for(FcbNameLen = 11; FcbNameLen != 0; FcbNameLen -= 1)
// The operation is an unsigned char rotate right
Sum = ((Sum & 1) ? 0x80 : 0) + (Sum >> 1) + *pFcbName++;
return Sum;
}
/*
signed int check_invalid_char(char fname[])
Summary: Checks the filename for any invalid characters.
Param: The name of the file to check.
Returns: EOF if an invalid character was found, GOODEC otherwise.
*/
signed int check_invalid_char(char fname[])
{
int fname_pos;
for(fname_pos = 0; (fname[fname_pos] != '\0') && (fname_pos < MAX_FILE_NAME_LENGTH); fname_pos += 1)
if(isamoung(fname[fname_pos], "\\/:*?\"<>|"))
return EOF;
return GOODEC;
}
/*
signed int get_next_free_cluster(int16* my_cluster)
Summary: Will go through the FAT and find the first unallocated cluster.
Param: Pointer to a variable that will the the starting cluster of the serach.
When a free cluster is found, the cluster number will be put into this variable.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: This gets a little slow when dealing with a card with lots of stuff on it; sorry about that.
*/
#ifdef FAT32
signed int get_next_free_cluster(int32* my_cluster)
#else
signed int get_next_free_cluster(int16* my_cluster)
#endif
{
#ifdef FAST_FAT
*my_cluster += 1;
return GOODEC;
#else // NO FAST_FAT
#ifdef FAT32
int val[4]; // buffer to hold values
int32 cur_cluster;
int32
FAT_addr, // the current address that the algorithm is on
j;
// first, convert *my_cluster to an addressable location in the FAT
FAT_addr = (*my_cluster << 2) + FAT_Start;
// the most logical place for the next free cluster would be next to the current cluster
for(j = 0; j < FAT_Length; j += 4)
{
if(mmcsd_read_data(FAT_addr + j, 4, val) != GOODEC)
return EOF;
cur_cluster = make32(val[3], val[2], val[1], val[0]);
if(cur_cluster == 0)
{
// add on the last iteration of j, this is how far into the buffer we were when the algorithm terminated
FAT_addr += j;
// convert *my_cluster back into a cluster number
*my_cluster = (FAT_addr - FAT_Start) >> 2;
return GOODEC;
}
}
#else // FAT16
int val[2];
int16 cur_cluster;
int32
FAT_addr, // the current address that the algorithm is on
j;
// first, convert *my_cluster to an addressable location in the FAT
FAT_addr = (*my_cluster << 1) + FAT_Start;
// the most logical place for the next free cluster would be next to the current cluster
for(j = 0; j < FAT_Length; j += 2)
{
if(mmcsd_read_data(FAT_addr + j, 2, val) != GOODEC)
return EOF;
cur_cluster = make16(val[1], val[0]);
if(cur_cluster == 0)
{
// add on the last iteration of j, this is how far into the buffer we were when the algorithm terminated
FAT_addr += j;
// convert *my_cluster back into a cluster number
*my_cluster = (FAT_addr - FAT_Start) >> 1;
return GOODEC;
}
}
#endif // #ifdef FAT32
// if we reach this point, we are out of disk space
return EOF;
#endif // #ifdef FAST_FAT
}
/*
signed int get_next_file(FILE* stream)
Summary: Will point the stream to the next file in the directory.
Param: The stream to move.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: This will not set the Buf or Flag parameters.
*/
signed int get_next_file(FILE* stream)
{
int32
cluster,
cur_addr,
size;
int
fileentry,
filetype;
cur_addr = stream->Entry_Addr;
do
{
// go forward an entry
if(get_next_entry(&cur_addr) == EOF)
{
stream->File_Type = None;
return EOF;
}
mmcsd_read_data(cur_addr, 1, &fileentry);
mmcsd_read_data(cur_addr + 0x0B, 1, &filetype);
if(fileentry == 0)
{
stream->File_Type = None;
return EOF;
}
} while((fileentry == 0xE5) || (filetype == 0x0F));
// change the stream's file type
if(filetype == 0x10)
stream->File_Type = Directory;
else
stream->File_Type = Data_File;
// change the stream's entry address
stream->Entry_Addr = cur_addr;
// read in the new starting cluster
mmcsd_read_data(cur_addr + 0x1A, 2, &cluster);
mmcsd_read_data(cur_addr + 0x14, 2, (int16*)&cluster + 1);
// change the stream's start adress and cur char to the beginning of the first cluster
stream->Start_Addr = stream->Cur_Char = cluster_to_addr(cluster);
// read in the new file size
mmcsd_read_data(cur_addr + 0x1C, 4, &size);
// change the stream's size and bytes until EOF
stream->Size = stream->Bytes_Until_EOF = size;
return GOODEC;
}
/*
signed int get_next_file(FILE* stream)
Summary: Will point the stream to the previous file in the directory.
Param: The stream to move.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: This will not set the Buf or Flag parameters.
*/
signed int get_prev_file(FILE* stream)
{
int32
cluster,
cur_addr,
size;
int
fileentry = 0xE5,
filetype = 0;
cur_addr = stream->Entry_Addr;
do
{
// go backward an entry
if(get_prev_entry(&cur_addr) == EOF)
{
stream->File_Type = None;
return EOF;
}
mmcsd_read_data(cur_addr, 1, &fileentry);
mmcsd_read_data(cur_addr + 0x0B, 1, &filetype);
if(fileentry == 0)
{
stream->File_Type = None;
return EOF;
}
} while((fileentry == 0xE5) || (filetype == 0x0F));
// change the stream's file type
if(filetype == 0x10)
stream->File_Type = Directory;
else
stream->File_Type = Data_File;
// change the stream's entry address
stream->Entry_Addr = cur_addr;
// read in the new starting cluster
mmcsd_read_data(cur_addr + 0x1A, 2, &cluster);
mmcsd_read_data(cur_addr + 0x14, 2, (int16*)&cluster + 1);
// change the stream's start adress and cur char to the beginning of the first cluster
stream->Start_Addr = stream->Cur_Char = addr_to_cluster(cluster);
// read in the new file size
mmcsd_read_data(cur_addr + 0x1C, 4, &size);
// change the stream's size and bytes until EOF
stream->Size = stream->Bytes_Until_EOF = size;
return GOODEC;
}
/*
signed int get_next_free_addr(int32* my_addr)
Summary: Finds the next unallocated address.
Param: Pointer to a variable that will the the starting address of the serach.
When a free address is found, the address will be put into this variable.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int get_next_free_addr(int32* my_addr)
{
int val; // buffer to hold values
int32 cur_addr; // pointer to memory
// make a copy of *my_addr
cur_addr = *my_addr;
// keep on getting addresses until we hit a free one
do
{
if(mmcsd_read_data(cur_addr, 1, &val) != GOODEC)
return EOF;
if(get_next_addr(&cur_addr) == EOF)
return EOF;
} while(val != 0);
*my_addr = cur_addr;
return GOODEC;
}
/*
signed int get_next_entry(int32* start_addr)
Summary: Gets the next entry in a directory.
Param: The address to start looking for an entry.
If an entry is found, it will be put into this variable.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int get_next_entry(int32* start_addr)
{
int32 i;
i = *start_addr;
i += 0x1F;
if(get_next_addr(&i) == EOF)
return EOF;
*start_addr = i;
return GOODEC;
}
/*
signed int get_prev_entry(int32* start_addr)
Summary: Finds the next free entry in a directory.
Param: The address to start looking for a free entry.
If a free entry is found, it will be put into this variable.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int get_prev_entry(int32* start_addr)
{
int32 i;
i = *start_addr;
i -= 0x1F;
if(get_prev_addr(&i) == EOF)
return EOF;
*start_addr = i;
return GOODEC;
}
/*
signed int get_next_free_entry(int32* start_addr)
Summary: Finds the next free entry in a directory.
Param: The address to start looking for a free entry.
If a free entry is found, it will be put into this variable.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int get_next_free_entry(int32* start_addr)
{
int buf;
int32 i;
i = *start_addr;
if(mmcsd_read_data(i, 1, &buf) != GOODEC)
return EOF;
while(buf != 0)
{
i += 0x1F;
// get the next address
if(get_next_addr(&i) == EOF)
if(alloc_clusters(addr_to_cluster(i), &i) == EOF)
return EOF;
if(mmcsd_read_data(i, 1, &buf) != GOODEC)
return EOF;
}
*start_addr = i;
return GOODEC;
}
/*
signed int alloc_clusters(int16 start_cluster, int32* new_cluster_addr)
Summary: Find, allocate, and link a free cluster.
Param start_cluster: The cluster to begin looking for free clusters. This cluster will be linked to the newfound cluster in the FAT.
Param new_cluster_addr: The address of the newly allocated cluster.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int alloc_clusters(int32 start_cluster, int32* new_cluster_addr)
#else
signed int alloc_clusters(int16 start_cluster, int32* new_cluster_addr)
#endif
{
#ifdef FAT32
int32
cur_cluster,
next_cluster;
#else // FAT16
int16
cur_cluster,
next_cluster;
#endif // #ifdef FAT32
// if we're at the end of the file's allocated space, then we need to allocate some more space
// figure out where the current character is pointing to
next_cluster = cur_cluster = start_cluster;
// get the next free cluster
if(get_next_free_cluster(&next_cluster) == EOF)
return EOF;
if(write_fat(cur_cluster, next_cluster) == EOF)
return EOF;
#ifdef FAT32
if(write_fat(next_cluster, 0x0FFFFFFF) == EOF)
return EOF;
#else // FAT16
if(write_fat(next_cluster, 0xFFFF) == EOF)
return EOF;
#endif // #ifdef FAT32
// erase all of the data in the newly linked cluster
if(clear_cluster(next_cluster) == EOF)
return EOF;
// put the current character to this position
*new_cluster_addr = cluster_to_addr(next_cluster);
return GOODEC;
}
/*
signed int dealloc_clusters(int16 start_cluster)
Summary: De-allocates linked clusters from the FAT.
Param: The starting cluster to deallocate.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int dealloc_clusters(int32 start_cluster)
#else
signed int dealloc_clusters(int16 start_cluster)
#endif
{
#ifdef FAT32
int32
cur_cluster, // the current cluster we're pointing to
next_cluster; // the next cluster we're going to point to
#else // FAT16
int16
cur_cluster, // the current cluster we're pointing to
next_cluster; // the next cluster we're going to point to
#endif // #ifdef FAT32
// figure out where the first cluster is
next_cluster = cur_cluster = start_cluster;
do
{
// get the next cluster
if(get_next_cluster(&next_cluster) == EOF)
return EOF;
// unlink the current cluster in the FAT
if(write_fat(cur_cluster, 0) == EOF)
return EOF;
cur_cluster = next_cluster;
}
#ifdef FAT32
while(cur_cluster != 0x0FFFFFFF);
#else // FAT16
while(cur_cluster != 0xFFFF);
#endif // #ifdef FAT32
return GOODEC;
}
/*
signed int clear_cluster(int16 cluster)
Summary: Clears out all of the data in a given cluster.
Param: The cluster to clear out.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int clear_cluster(int32 cluster)
#else
signed int clear_cluster(int16 cluster)
#endif
{
int
clear_entry[0x20],
j;
int16 i;
int32 start_addr;
start_addr = cluster_to_addr(cluster);
for(j = 0; j < 0x20; j += 1)
clear_entry[j] = 0;
for(i = 0; i < Bytes_Per_Cluster; i += 0x20)
if(mmcsd_write_data(start_addr + i, 0x20, clear_entry) != GOODEC)
return EOF;
return GOODEC;
}
/*
signed int write_fat(int32 cluster, int32 data)
Summary: Writes specified data about a cluster to the FAT.
Param cluster: The cluster to modify the in the FAT.
Param data: The data about the cluster to put into the FAT.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int write_fat(int32 cluster, int32 data)
{
if(mmcsd_write_data((cluster << 2) + FAT_Start, 4, &data) != GOODEC)
return EOF;
return GOODEC;
}
#else // FAT16
signed int write_fat(int16 cluster, int16 data)
{
if(mmcsd_write_data((cluster << 1) + FAT_Start, 2, &data) != GOODEC)
return EOF;
return GOODEC;
}
#endif // #ifdef FAT32
/*
signed int read_buffer(FILE* stream, int* val)
Summary: Reads from the buffer.
Param stream: The stream whose buffer to read from.
Param val: A pointer to a varaible to put the read data into.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int read_buffer(FILE* stream, int* val)
{
int i; // counter for loops
// check to see if we need to populate the buffer
if((stream->Cur_Char % STREAM_BUF_SIZE) == 0)
{
if(mmcsd_read_data(stream->Cur_Char, STREAM_BUF_SIZE, stream->Buf) != GOODEC)
{
stream->Flags |= Read_Error;
return EOF;
}
}
// grab the value at the top of the buffer
*val = stream->Buf[0];
// shift everything over 1 byte to put a new value at the top of the buffer
for(i = 0; i < 8; i += 1)
rotate_right(stream->Buf, STREAM_BUF_SIZE);
return GOODEC;
}
/*
signed int write_buffer(FILE* stream, int val)
Summary: Writes to the buffer.
Param stream: The stream whose buffer to write to.
Param val: A variable to write to the buffer.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int write_buffer(FILE* stream, int val)
{
// check to see if we should dump the buffer to the card
if(((stream->Cur_Char % STREAM_BUF_SIZE) == 0)
&& (stream->Cur_Char != stream->Start_Addr))
{
// dump the buffer to the card
if(mmcsd_write_data(stream->Cur_Char - STREAM_BUF_SIZE, STREAM_BUF_SIZE, stream->Buf) != GOODEC)
{
stream->Flags |= Write_Error;
return EOF;
}
}
// fill up a byte on the buffer
stream->Buf[stream->Cur_Char % STREAM_BUF_SIZE] = val;
return GOODEC;
}
/*
void fill_entry(char the_entry[], char val, int8 start_ind)
Summary: This will fill up the unused spots in a standard FAT entry with a value.
Param the_entry[]: The entry that will be modified.
Param val: The value to fill the entry with.
Param start_ind: The beginning index to start filling the_entry.
Returns: Nothing.
*/
void fill_entry(char the_entry[], char val, int8 start_ind)
{
int8 i;
for(i = start_ind; i < 13; i += 1)
{
if(i < 5)
{
the_entry[(i << 1) + 1] = val;
the_entry[(i << 1) + 2] = val;
}
else if(i < 11)
{
the_entry[(i << 1) + 4] = val;
the_entry[(i << 1) + 5] = val;
}
else
{
the_entry[(i << 1) + 6] = val;
the_entry[(i << 1) + 7] = val;
}
}
}
/*
void disp_timestamp(int16 timestamp)
Summary: Parses an timestamp from a file entry and displays it to the console
Param: The 16-bit timestamp code from a file entry
Returns: Nothing.
*/
void disp_timestamp(int16 timestamp)
{
// Hours:Minutes:Seconds
printf("%lu:%lu:%lu",
timestamp >> 11,
(timestamp & 0x07E0) >> 5,
(timestamp & 0x001F) << 1);
}
/*
void disp_datestamp(int16 datestamp)
Summary: Parses an datestamp from a file entry and displays it to the console
Param: The 16-bit datestamp code from a file entry
Returns: Nothing.
*/
void disp_datestamp(int16 datestamp)
{
// Month/Day/Year
printf("%lu/%lu/%lu",
(datestamp & 0x01F0) >> 5,
datestamp & 0x001F,
(1980 + (datestamp >> 9)));
}
/// Data Utility Functions ///
/*
signed int fat_init()
Summary: Initializes global variables that are essential for this library working
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
Note: This must be called before any other function calls in this library.
*/
signed int fat_init()
{
int ec = 0;
int
FATs,
Sectors_Per_Cluster;
int16
Bytes_Per_Sector,
Reserved_Sectors,
Small_Sectors;
int32
Hidden_Sectors,
Large_Sectors;
#ifdef FAT32
int32 Sectors_Per_FAT;
#else // FAT16
int16
Root_Entries,
Sectors_Per_FAT;
#endif // #ifdef FAT32
// initialize the media
ec += mmcsd_init();
// start filling up variables
ec += mmcsd_read_data(11, 2, &Bytes_Per_Sector);
ec += mmcsd_read_data(13, 1, &Sectors_Per_Cluster);
ec += mmcsd_read_data(14, 2, &Reserved_Sectors);
ec += mmcsd_read_data(16, 1, &FATs);
#ifdef FAT16
ec += mmcsd_read_data(17, 2, &Root_Entries);
#endif // #ifdef FAT16
ec += mmcsd_read_data(19, 2, &Small_Sectors);
#ifdef FAT32
ec += mmcsd_read_data(36, 4, &Sectors_Per_FAT);
#else // FAT16
ec += mmcsd_read_data(22, 2, &Sectors_Per_FAT);
#endif // #ifdef FAT32
ec += mmcsd_read_data(28, 4, &Hidden_Sectors);
ec += mmcsd_read_data(32, 4, &Large_Sectors);
#ifdef FAT16
Next_Free_Clust = 2;
#else
ec += mmcsd_read_data(0x3EC, 4, &Next_Free_Clust);
#endif
if(ec != GOODEC)
return EOF;
// figure out the size of a cluster
Bytes_Per_Cluster = Sectors_Per_Cluster * Bytes_Per_Sector;
// figure out how long one FAT is
FAT_Length = Sectors_Per_FAT * (int32)Bytes_Per_Sector;
// figure out where the FAT starts
FAT_Start = Reserved_Sectors * Bytes_Per_Sector;
// figure out where the root directory starts
Root_Dir = FAT_Start + (FATs * FAT_Length);
// figure out where data for files in the root directory starts
#ifdef FAT32
Data_Start = Root_Dir;
#else // FAT16
Data_Start = (Root_Entries * 0x20) + (Bytes_Per_Sector - 1);
Data_Start /= Bytes_Per_Sector;
Data_Start += Reserved_Sectors + (FATs * Sectors_Per_FAT);
Data_Start *= Bytes_Per_Sector;
#endif // #ifdef FAT32
return GOODEC;
}
/*
signed int get_next_cluster(int16* my_cluster)
Summary: Gets the next linked cluster from the FAT.
Param: A pointer to a variable that holds a cluster.
This variable will then have the next linked cluster when the function returns.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int get_next_cluster(int32* my_cluster)
#else
signed int get_next_cluster(int16* my_cluster)
#endif
{
// convert the current cluster into the address of where information about
// the cluster is stored in the FAT, and put this value into the current cluster
#ifdef FAT32
if(mmcsd_read_data((*my_cluster << 2) + FAT_Start, 4, my_cluster) != GOODEC)
return EOF;
#else // FAT16
if(mmcsd_read_data((*my_cluster << 1) + FAT_Start, 2, my_cluster) != GOODEC)
return EOF;
#endif // #ifdef FAT32
return GOODEC;
}
/*
signed int get_prev_cluster(int32* my_cluster)
Summary: Gets the previously linked cluster in the FAT.
Param: A pointer to a variable that holds a cluster.
This variable will then have the previous linked cluster when the function returns.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int get_prev_cluster(int32* my_cluster)
#else
signed int get_prev_cluster(int16* my_cluster)
#endif // #ifdef FAT32
{
#ifdef FAT32
int32
cur_cluster = 1,
target_cluster = 0;
#else
int16
cur_cluster = 1,
target_cluster = 0;
#endif // #ifdef FAT32
while(target_cluster != *my_cluster)
{
cur_cluster += 1;
#ifdef FAT32
if(mmcsd_read_data((cur_cluster << 2) + FAT_Start, 4, &target_cluster) != GOODEC)
return EOF;
#else // FAT16
if(mmcsd_read_data((cur_cluster << 1) + FAT_Start, 2, &target_cluster) != GOODEC)
return EOF;
#endif // #ifdef FAT32
}
#ifdef FAT32
*my_cluster = cur_cluster;
#else // FAT16
*my_cluster = cur_cluster;
#endif // #ifdef FAT32
return GOODEC;
}
/*
signed int get_next_addr(int32* my_addr)
Summary: Get the next linked address.
Param: A pointer to a variable that holds an address.
This variable will then have the next linked address when the function returns.
Returns: EOF if there was a problem with the media or we've reached the last linked cluster in the FAT, GOODEC if everything went okay.
*/
signed int8 get_next_addr(int32* my_addr)
{
int32 temp;
#ifdef FAT32
int32 c;
#else
int16 c;
#endif
// check to make sure that the next iteration will give us a contiguous address
temp = *my_addr + 1;
if((temp > Data_Start) && ((temp - Data_Start) % Bytes_Per_Cluster == 0))
{
// convert the current address into the address of where information about
// the address is stored in the FAT, and put this value into the current address
c = addr_to_cluster(temp - 1);
if(get_next_cluster(&c) == EOF)
return EOF;
if (c >=
#ifdef FAT32
0x0FFFFFF8
#else
0xFFF8
#endif
)
return EOF;
temp = cluster_to_addr(c);
}
*my_addr = temp;
return GOODEC;
}
/*
signed int get_prev_addr(int32* my_addr)
Summary: Get the next linked address.
Param: A pointer to a variable that holds an address.
This variable will then have the next linked address when the function returns.
Returns: EOF if there was a problem with the media or we've reached the last linked cluster in the FAT, GOODEC if everything went okay.
*/
signed int8 get_prev_addr(int32* my_addr)
{
int32 temp;
#ifdef FAT32
int32 c;
#else
int16 c;
#endif
temp = *my_addr;
// if we're trying to go backwards one entry from the beginning of the root,
// we won't be able to...
if(temp <= Root_Dir)
return GOODEC;
// check to make sure that the next iteration will give us a contiguous address
if((temp >= Data_Start) && ((temp - Data_Start) % Bytes_Per_Cluster == 0))
{
c = addr_to_cluster(temp);
if(get_prev_cluster(&c) == EOF)
return EOF;
temp = cluster_to_addr(c) + Bytes_Per_Cluster;
}
*my_addr = temp - 1;
return GOODEC;
}
/*
int32 cluster_to_addr(int32 cluster)
Summary: Converts a cluster number to an address.
Param: The cluster to convert.
Returns: The cluster's address.
*/
#ifdef FAT32
int32 cluster_to_addr(int32 cluster)
{
// in unit math:
// return Bytes+(Bytes / Cluster * (Clusters - Clusters))
return Root_Dir + (Bytes_Per_Cluster * (cluster - 2));
}
#else
int32 cluster_to_addr(int16 cluster)
{
if(cluster < 2)
return 0;
// in unit math:
// return Bytes + ( Bytes / Cluster * (Clusters- Clusters))
return Data_Start + ((int32)Bytes_Per_Cluster * (cluster - 2));
}
#endif
/*
int32 addr_to_cluster(int32 addr)
Summary: Converts an address to a cluster number.
Param: The address to convert.
Returns: The address's cluster.
*/
#ifdef FAT32
int32 addr_to_cluster(int32 addr)
{
// in unit math:
// return (Bytes -Bytes) / Bytes / Cluster) + Clusters
return ((addr - Root_Dir) / Bytes_Per_Cluster) + 2;
}
#else
int16 addr_to_cluster(int32 addr)
{
if(addr < Data_Start)
return 0;
// in unit math:
// return (Bytes -Bytes) /(Bytes / Cluster) + Clusters
return ((addr - Data_Start) / Bytes_Per_Cluster) + 2;
}
#endif
/*
signed int format(int32 DskSize)
Summary: Formats media with a FAT filesystem.
Param: The size of the filesystem to create in kB.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
Note: There are certain minimum and maximum size restrictions on the card and type of file system. The restrictions are as follows:
FAT16: DskSize < 2GB
FAT32: 33MB < DskSize < 32GB
In order to change the way that the drive is formatted, select the proper #define(FAT16 or FAT32) way up at the top of this file.
Note: In this context, 1kB = 1024B = 2^10B. Please don't confuse this with 10^3B, we don't want to be wasting thousands of bytes of information now, do we?
Note: DskSize has a lower limit of 64, anything lower becomes highly inefficient and runs the risk of very quick corruption.
Note: If this is called on an MMC/SD card, Windows will recognize it as a RAW filesystem.
*/
signed int format(int32 DskSize)
{
int
BPB_Media = 0xF8,
BPB_NumFATs = 1,
BPB_NumHeads = 2,
BPB_SecPerClus,
BPB_SecPerTrk = 0x20;
int16
BPB_BytsPerSec = 0x200,
i;
int32
BPB_TotSec,
BS_VolID = 0,
RootDirSectors,
TmpVal1,
TmpVal2;
char
BS_OEMName[] = "MSDOS5.0",
BS_VolLab[] = "NO NAME ";
#ifdef FAT32
int
BPB_BkBootSec = 6,
BPB_FSInfo = 1,
BPB_RootClus = 2,
BS_BootSig = 0x29,
BS_jmpBoot = 0x58,
data[0x5A];
int16
BPB_RootEntCnt = 0,
BPB_RsvdSecCnt = 32;
int32 BPB_FATSz;
char BS_FilSysType[] = "FAT32 ";
#else
int
BS_BootSig = 0x29,
BS_jmpBoot = 0x3C,
data[0x3E];
int16
BPB_FATSz,
BPB_RootEntCnt = 512,
BPB_RsvdSecCnt = 1;
char BS_FilSysType[] = "FAT12 ";
#endif // #ifdef FAT32
// initialize variables
// figure out total sectors
BPB_TotSec = (DskSize * 0x400) / BPB_BytsPerSec;
// use the magical table on page 20 of fatgen103.pdf to determine sectors per cluster
#ifdef FAT32
if(DskSize < 0x8400) // < 33 MB; this is too small
return EOF;
else if(DskSize < 0x41000) // 260 MB
BPB_SecPerClus = 1;
else if(DskSize < 0X800000) // 8 GB
BPB_SecPerClus = 8;
else if(DskSize < 0x1000000) // 16 GB
BPB_SecPerClus = 16;
else if(DskSize < 0x2000000) // 32 GB
BPB_SecPerClus = 32;
else // > 32 GB; this is too big
return EOF;
#else
if(DskSize < 0x1400) // < 5 MB
BPB_SecPerClus = 1;
else if(DskSize < 0x4000) // 16 MB
BPB_SecPerClus = 2;
else if(DskSize < 0X20000) // 128 MB
BPB_SecPerClus = 4;
else if(DskSize < 0x40000) // 256 MB
BPB_SecPerClus = 8;
else if(DskSize < 0x80000) // 512 MB
BPB_SecPerClus = 16;
else if(DskSize < 0x100000) // 1 GB
BPB_SecPerClus = 32;
else if(DskSize < 0x200000) // 2 GB
BPB_SecPerClus = 64;
else // > 2 GB; this is too big
return EOF;
#endif // #ifdef FAT32
// figure out how many sectors one FAT takes up
RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)) / BPB_BytsPerSec;
TmpVal1 = DskSize - (BPB_RsvdSecCnt + RootDirSectors);
TmpVal2 = (256 * BPB_SecPerClus) + BPB_NumFATs;
#ifdef FAT32
TmpVal2 = TmpVal2 / 2;
#endif // #ifdef FAT32
BPB_FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
// zero data
for(i = 0; i < sizeof(data); i += 1)
data[i] = 0;
// start filling up data
data[0] = 0xEB;
data[1] = BS_jmpBoot;
data[2] = 0x90;
sprintf(data + 3, "%s", BS_OEMName);
data[11] = make8(BPB_BytsPerSec, 0);
data[12] = make8(BPB_BytsPerSec, 1);
data[13] = BPB_SecPerClus;
data[14] = BPB_RsvdSecCnt;
data[16] = BPB_NumFATs;
data[21] = BPB_Media;
data[24] = BPB_SecPerTrk;
data[26] = BPB_NumHeads;
#ifdef FAT32
data[32] = make8(BPB_TotSec, 0);
data[33] = make8(BPB_TotSec, 1);
data[34] = make8(BPB_TotSec, 2);
data[35] = make8(BPB_TotSec, 3);
data[36] = make8(BPB_FATSz, 0);
data[37] = make8(BPB_FATSz, 1);
data[38] = make8(BPB_FATSz, 2);
data[39] = make8(BPB_FATSz, 3);
data[44] = BPB_RootClus;
data[48] = BPB_FSInfo;
data[50] = BPB_BkBootSec;
data[66] = BS_BootSig;
data[67] = make8(BS_VolID, 0);
data[68] = make8(BS_VolID, 1);
data[69] = make8(BS_VolID, 2);
data[70] = make8(BS_VolID, 3);
sprintf(data + 71, "%s", BS_VolLab);
sprintf(data + 82, "%s", BS_FilSysType);
// put data onto the card
// first, all the partition parameters
if(mmcsd_write_data(0, sizeof(data), data) != GOODEC)
return EOF;
// figure out where the first FAT starts
TmpVal1 = BPB_BytsPerSec * BPB_RsvdSecCnt;
// figure out where the root directory starts
TmpVal2 = TmpVal1 + (BPB_NumFATs * BPB_FATSz);
// clear out some values in data
for(i = 0; i < 0x20; i += 1)
data[i] = 0;
// get rid of everything in the root directory
clear_cluster(2);
// clear out the FAT
for(i = 0; i < BPB_FATSz; i += 0x20)
if(mmcsd_write_data(TmpVal1 + i, 0x20, data) != GOODEC)
return EOF;
// insert the first 12 entries into the FAT(s)
data[0] = 0xF8;
data[1] = 0xFF;
data[2] = 0xFF;
data[3] = 0x0F;
data[4] = 0xFF;
data[5] = 0xFF;
data[6] = 0xFF;
data[7] = 0x0F;
data[8] = 0xFF;
data[9] = 0xFF;
data[10] = 0xFF;
data[11] = 0x0F;
if(mmcsd_write_data(TmpVal1, 0x20, data) != GOODEC)
return EOF;
// reset the last cluster
i = 2;
if(mmcsd_write_data(0x3EC, 4, &i) != GOODEC)
return EOF;
#else
data[17] = make8(BPB_RootEntCnt, 0);
data[18] = make8(BPB_RootEntCnt, 1);
data[19] = make8(BPB_TotSec, 0);
data[20] = make8(BPB_TotSec, 1);
data[22] = make8(BPB_FATSz, 0);
data[23] = make8(BPB_FATSz, 1);
data[38] = BS_BootSig;
data[39] = make8(BS_VolID, 0);
data[40] = make8(BS_VolID, 1);
data[41] = make8(BS_VolID, 2);
data[42] = make8(BS_VolID, 3);
sprintf(data + 43, "%s", BS_VolLab);
sprintf(data + 54, "%s", BS_FilSysType);
// put data onto the card
// first, all the partition parameters
if(mmcsd_write_data(0, sizeof(data), data) != GOODEC)
return EOF;
// figure out where the first FAT starts
TmpVal1 = BPB_BytsPerSec * BPB_RsvdSecCnt;
// figure out where the root directory starts
TmpVal2 = TmpVal1 + (BPB_NumFATs * BPB_FATSz);
// clear out some values in data
for(i = 0; i < 0x20; i += 1)
data[i] = 0;
// get rid of everything in the root directory
for(i = 0; i < (0x20 * BPB_RootEntCnt); i += 0x20)
if(mmcsd_write_data(TmpVal2 + i, 0x20, data) != GOODEC)
return EOF;
// clear out the FAT
for(i = 0; i < BPB_FATSz; i += 0x20)
if(mmcsd_write_data(TmpVal1 + i, 0x20, data) != GOODEC)
return EOF;
// insert the first 3 entries into the FAT(s)
data[0] = 0xF8;
data[1] = 0xFF;
data[2] = 0xFF;
if(mmcsd_write_data(TmpVal1, 0x20, data) != GOODEC)
return EOF;
#endif // #ifdef FAT32
i = 0xAA55;
if(mmcsd_write_data(0x1FE, 2, &i) != GOODEC)
return EOF;
// we're going to have to re-initialize the FAT, a bunch of parameters probably just changed
fat_init();
return GOODEC;
}
/// Debugging Utility Functions ///
/*
signed int disp_folder_contents(char foldername[])
Summary: Displays the contents of a folder.
Param: The folder to display the contents of.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int disp_folder_contents(char foldername[])
{
char filename[MAX_FILE_NAME_LENGTH]; // a place to hold a file name
FILE stream; // the stream that we're going to be working with
char mode[] = "r";
if(fatopen(foldername, mode, &stream) != GOODEC)
return EOF;
// printf off a header
printf("\r\n--%s--", foldername);
// start off at the root directory
stream.Entry_Addr = stream.Start_Addr;
while(get_next_file(&stream) != EOF)
{
// get the name of the file that we are at
if(get_file_name(stream.Entry_Addr, filename) != GOODEC)
return EOF;
// make cool little "tree" branches
printf("\r\n%s", filename);
if (stream.File_Type == Directory)
putc('/');
}
fatclose(&stream);
return GOODEC;
}
/*
signed int dump_addr(int32 from, int32 to)
Summary: Display a series of addresses in a hex editor type fashion.
Param from: The beginning address to display.
Param to: The end address to display.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int dump_addr(int32 from, int32 to)
{
int
j, // counter for loops
val[0x10]; // buffer to hold values
int32 i; // pointer to memory
// print off header
printf("\r\n\r\n ");
for(i = 0; i < 0x10; i += 1)
printf("%2X ", i);
// note that the to and from values are being rounded up and down
// this makes a nice "block" map in case someone inputs a number that
// isn't evenly divisible by 0x10
for(i = (from - (from % 0x10)); i <= (to + (to % 0x10)); i += 0x10)
{
// printf memory block
printf("\r\n%lX ", i);
// fill up buffer
if(mmcsd_read_data(i, 0x10, val) != GOODEC)
return EOF;
// printf RAM in hex
for(j = 0; j < 0X10; j += 1)
printf("%X ", val[j]);
// printf spacer
printf("; ");
// printf RAM in char
for(j = 0; j < 0X10; j += 1)
{
// check for characters that will mess up the nice-looking map
if(val[j] < ' ')
val[j] = '.';
printf("%c", val[j]);
}
}
return GOODEC;
}
/*
signed int dump_clusters(int32 from, int32 to)
Summary: Display a series of clusters in a memory map.
Param from: The beginning clusters to display.
Param to: The end clusters to display.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int dump_clusters(int32 from, int32 to)
{
// convert the clusters to addresses and dump
if(dump_addr(cluster_to_addr(from), cluster_to_addr(to)) != GOODEC)
return EOF;
}
/*
void disp_fat_stats()
Summary: Display essential statistics about the FAT to the console.
Returns: Nothing.
*/
void disp_fat_stats()
{
printf("\r\n\r\n--FAT Stats--\r\n");
printf("First FAT starts at: 0x%lX\r\n", FAT_Start);
printf("Data Starts At: 0x%lX\r\n", Data_Start);
printf("Root Directory Is At: 0x%lX\r\n", Root_Dir);
printf("Bytes Per Cluster: 0x%lX\r\n", Bytes_Per_Cluster);
}
/*
signed int fatprintfinfo(FILE* stream)
Summary: Display essential statistics about the file that a stream is pointing to.
Param: The stream to print off information about.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int fatprintfinfo(FILE* stream)
{
int ec = 0;
int32 val = 0; // buffer to hold values
char name[MAX_FILE_NAME_LENGTH];
// get name
if(get_file_name(stream->Entry_Addr, name) != GOODEC)
return EOF;
// printf header
printf("\r\n\r\n--");
printf(name);
printf(" Info--");
// printf attributes
ec += mmcsd_read_data(stream->Entry_Addr + 0x0B, 1, &val);
printf("\r\nAttributes: 0x%X", val);
// printf creation date
printf("\r\nCreated: ");
ec += mmcsd_read_data(stream->Entry_Addr + 0x10, 2, &val);
disp_datestamp(val);
printf(" ");
// printf creation time
ec += mmcsd_read_data(stream->Entry_Addr + 0x0E, 2, &val);
disp_timestamp(val);
// printf modification date
printf("\r\nModified: ");
ec += mmcsd_read_data(stream->Entry_Addr + 0x18, 2, &val);
disp_datestamp(val);
printf(" ");
// printf modification time
ec += mmcsd_read_data(stream->Entry_Addr + 0x16, 2, &val);
disp_timestamp(val);
// printf starting cluster
ec += mmcsd_read_data(stream->Entry_Addr + 0x14, 2, (int16*)&val + 1);
ec += mmcsd_read_data(stream->Entry_Addr + 0x1A, 2, &val);
printf("\r\nStarting cluster: %lX", val);
// printf starting address
printf("\r\nStarting address: %lX", cluster_to_addr(val));
// printf size
ec += mmcsd_read_data(stream->Entry_Addr + 0x1C, 4, &val);
printf("\r\nSize: %lu Bytes\r\n", val);
if(ec != GOODEC)
return EOF;
return GOODEC;
}
#endif // #ifndef FAT_PIC_C
/////////////////////////////////////////////////////////////////////////
//// MMCSD.c ////
//// ////
//// This is a low-level driver for MMC and SD cards. ////
//// ////
//// --User Functions-- ////
//// ////
//// mmcsd_init(): Initializes the media. ////
//// ////
//// mmcsd_read_byte(a, p) ////
//// Reads a byte from the MMC/SD card at location a, saves to ////
//// pointer p. Returns 0 if OK, non-zero if error. ////
//// ////
//// mmcsd_read_data(a, n, p) ////
//// Reads n bytes of data from the MMC/SD card starting at address ////
//// a, saves result to pointer p. Returns 0 if OK, non-zero if ////
//// error. ////
//// ////
//// mmcsd_flush_buffer() ////
//// The two user write functions (mmcsd_write_byte() and ////
//// mmcsd_write_data()) maintain a buffer to speed up the writing ////
//// process. Whenever a read or write is performed, the write ////
//// buffer is loaded with the specified page and only the ////
//// contents of this buffer is changed. If any future writes ////
//// cross a page boundary then the buffer in RAM is written ////
//// to the MMC/SD and then the next page is loaded into the ////
//// buffer. mmcsd_flush_buffer() forces the contents in RAM ////
//// to the MMC/SD card. Returns 0 if OK, non-zero if errror. ////
//// ////
//// mmcsd_write_byte(a, d) ////
//// Writes data byte d to the MMC/SD address a. Intelligently ////
//// manages a write buffer, therefore you may need to call ////
//// mmcsd_flush_buffer() to flush the buffer. ////
//// ////
//// mmcsd_write_data(a, n, p) ////
//// Writes n bytes of data from pointer p to the MMC/SD card ////
//// starting at address a. This function intelligently manages ////
//// a write buffer, therefore if you may need to call ////
//// mmcsd_flush_buffer() to flush any buffered characters. ////
//// returns 0 if OK, non-zero if error. ////
//// ////
//// mmcsd_read_block(a, s, p) ////
//// Reads an entire page from the SD/MMC. Keep in mind that the ////
//// start of the read has to be aligned to a block ////
//// (Address % 512 = 0). Therefore s must be evenly divisible by ////
//// 512. At the application level it is much more effecient to ////
//// to use mmcsd_read_data() or mmcsd_read_byte(). Returns 0 ////
//// if successful, non-zero if error. ////
//// ////
//// mmcsd_write_block(a, s, p): ////
//// Writes an entire page to the SD/MMC. This will write an ////
//// entire page to the SD/MMC, so the address and size must be ////
//// evenly divisble by 512. At the application level it is much ////
//// more effecient to use mmcsd_write_data() or mmcsd_write_byte().////
//// Returns 0 if successful, non-zero if error. ////
//// ////
//// mmcsd_print_cid(): Displays all data in the Card Identification ////
//// Register. Note this only works on SD cards. ////
//// ////
//// mmcsd_print_csd(): Displays all data in the Card Specific Data ////
//// Register. Note this only works on SD cards. ////
//// ////
//// ////
//// --Non-User Functions-- ////
//// ////
//// mmcsd_go_idle_state(): Sends the GO_IDLE_STATE command to the ////
//// SD/MMC. ////
//// mmcsd_send_op_cond(): Sends the SEND_OP_COND command to the ////
//// SD. Note this command only works on SD. ////
//// mmcsd_send_if_cond(): Sends the SEND_IF_COND command to the ////
//// SD. Note this command only works on SD. ////
//// mmcsd_sd_status(): Sends the SD_STATUS command to the SD. Note ////
//// This command only works on SD cards. ////
//// mmcsd_send_status(): Sends the SEND_STATUS command to the ////
//// SD/MMC. ////
//// mmcsd_set_blocklen(): Sends the SET_BLOCKLEN command along with ////
//// the desired block length. ////
//// mmcsd_app_cmd(): Sends the APP_CMD command to the SD. This only ////
//// works on SD cards and is used just before any ////
//// SD-only command (e.g. send_op_cond()). ////
//// mmcsd_read_ocr(): Sends the READ_OCR command to the SD/MMC. ////
//// mmcsd_crc_on_off(): Sends the CRC_ON_OFF command to the SD/MMC ////
//// along with a bit to turn the CRC on/off. ////
//// mmcsd_send_cmd(): Sends a command and argument to the SD/MMC. ////
//// mmcsd_get_r1(): Waits for an R1 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_get_r2(): Waits for an R2 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_get_r3(): Waits for an R3 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_get_r7(): Waits for an R7 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_wait_for_token(): Waits for a specified token from the ////
//// SD/MMC. ////
//// mmcsd_crc7(): Generates a CRC7 using a pointer to some data, ////
//// and how many bytes long the data is. ////
//// mmcsd_crc16(): Generates a CRC16 using a pointer to some data, ////
//// and how many bytes long the data is. ////
//// ////
/////////////////////////////////////////////////////////////////////////
//// (C) Copyright 2007 Custom Computer Services ////
//// This source code may only be used by licensed users of the CCS ////
//// C compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use, ////
//// reproduction or distribution is permitted without written ////
//// permission. Derivative programs created using this software ////
//// in object code form are not restricted in any way. ////
/////////////////////////////////////////////////////////////////////////
#ifndef MMCSD_C
#define MMCSD_C
/////////////////////
//// ////
//// User Config ////
//// ////
/////////////////////
#include <stdint.h>
#ifndef MMCSD_SPI_XFER
#if defined(MMCSD_SPI_HW)
#use spi(MASTER, MMCSD_SPI_HW, BITS=8, MSB_FIRST, MODE=0, baud=400000, stream=mmcsd_spi)
#else
#ifndef MMCSD_PIN_SCL
#define MMCSD_PIN_SCL PIN_C3 //o
#define MMCSD_PIN_SDI PIN_C4 //i
#define MMCSD_PIN_SDO PIN_C5 //o
#define MMCSD_PIN_SELECT PIN_C2 //o
#endif
#use spi(MASTER, DI=MMCSD_PIN_SDI, DO=MMCSD_PIN_SDO, CLK=MMCSD_PIN_SCL, BITS=8, MSB_FIRST, MODE=0, baud=400000, stream=mmcsd_spi)
#endif
#define MMCSD_SPI_XFER(x) spi_xfer(mmcsd_spi, x)
#endif
////////////////////////
//// ////
//// Useful Defines ////
//// ////
////////////////////////
enum MMCSD_err
{MMCSD_GOODEC = 0,
MMCSD_IDLE = 0x01,
MMCSD_ERASE_RESET = 0x02,
MMCSD_ILLEGAL_CMD = 0x04,
MMCSD_CRC_ERR = 0x08,
MMCSD_ERASE_SEQ_ERR = 0x10,
MMCSD_ADDR_ERR = 0x20,
MMCSD_PARAM_ERR = 0x40,
RESP_TIMEOUT = 0x80};
#define GO_IDLE_STATE 0
#define SEND_OP_COND 1
#define SEND_IF_COND 8
#define SEND_CSD 9
#define SEND_CID 10
#define SD_STATUS 13
#define SEND_STATUS 13
#define SET_BLOCKLEN 16
#define READ_SINGLE_BLOCK 17
#define WRITE_BLOCK 24
#define SD_SEND_OP_COND 41
#define APP_CMD 55
#define READ_OCR 58
#define CRC_ON_OFF 59
#define IDLE_TOKEN 0x01
#define DATA_START_TOKEN 0xFE
#define MMCSD_MAX_BLOCK_SIZE 512
////////////////////////
/// ///
/// Global Variables ///
/// ///
////////////////////////
uint8_t g_mmcsd_buffer[MMCSD_MAX_BLOCK_SIZE];
int1 g_CRC_enabled;
int1 g_MMCSDBufferChanged;
uint32_t g_mmcsdBufferAddress;
uint32_t g_mmcsdPartitionOffset;
enum _card_type{SD, MMC} g_card_type;
/////////////////////////////
//// ////
//// Function Prototypes ////
//// ////
/////////////////////////////
MMCSD_err mmcsd_init();
MMCSD_err mmcsd_read_data(uint32_t address, uint16_t size, uint8_t* ptr);
MMCSD_err mmcsd_read_block(uint32_t address, uint16_t size, uint8_t* ptr);
MMCSD_err mmcsd_write_data(uint32_t address, uint16_t size, uint8_t* ptr);
MMCSD_err mmcsd_write_block(uint32_t address, uint16_t size, uint8_t* ptr);
MMCSD_err mmcsd_go_idle_state(void);
MMCSD_err mmcsd_send_op_cond(void);
MMCSD_err mmcsd_send_if_cond(uint8_t r7[]);
MMCSD_err mmcsd_print_csd();
MMCSD_err mmcsd_print_cid();
MMCSD_err mmcsd_sd_status(uint8_t r2[]);
MMCSD_err mmcsd_send_status(uint8_t r2[]);
MMCSD_err mmcsd_set_blocklen(uint32_t blocklen);
MMCSD_err mmcsd_read_single_block(uint32_t address);
MMCSD_err mmcsd_write_single_block(uint32_t address);
MMCSD_err mmcsd_sd_send_op_cond(void);
MMCSD_err mmcsd_app_cmd(void);
MMCSD_err mmcsd_read_ocr(uint8_t* r1);
MMCSD_err mmcsd_crc_on_off(int1 crc_enabled);
MMCSD_err mmcsd_send_cmd(uint8_t cmd, uint32_t arg);
MMCSD_err mmcsd_get_r1(void);
MMCSD_err mmcsd_get_r2(uint8_t r2[]);
MMCSD_err mmcsd_get_r3(uint8_t r3[]);
MMCSD_err mmcsd_get_r7(uint8_t r7[]);
MMCSD_err mmcsd_wait_for_token(uint8_t token);
uint8_t mmcsd_crc7(char *data, uint8_t length);
uint16_t mmcsd_crc16(char *data, uint8_t length);
void mmcsd_select();
void mmcsd_deselect();
/// Fast Functions ! ///
MMCSD_err mmcsd_load_buffer(void);
MMCSD_err mmcsd_flush_buffer(void);
MMCSD_err mmcsd_move_buffer(uint32_t new_addr);
MMCSD_err mmcsd_read_byte(uint32_t addr, char* data);
MMCSD_err mmcsd_write_byte(uint32_t addr, char data);
//////////////////////////////////
//// ////
//// Function Implementations ////
//// ////
//////////////////////////////////
void mmcsd_check_part(uint16_t off)
{
if (g_mmcsd_buffer[off + 0] == 0x80)
{
// active partition
uint8_t t;
t = g_mmcsd_buffer[off + 4];
if (t == 0x04 || t == 0x06 || t == 0x0B)
{
// FAT16 or FAT32 partition
g_mmcsdPartitionOffset = make32(
g_mmcsd_buffer[off + 11], g_mmcsd_buffer[off + 10],
g_mmcsd_buffer[off + 9], g_mmcsd_buffer[off + 8]) * MMCSD_MAX_BLOCK_SIZE;
}
}
}
MMCSD_err mmcsd_init()
{
uint8_t
i,
r1;
g_CRC_enabled = TRUE;
g_mmcsdBufferAddress = 0;
#if defined(MMCSD_PIN_SCL)
output_drive(MMCSD_PIN_SCL);
#endif
#if defined(MMCSD_PIN_SDO)
output_drive(MMCSD_PIN_SDO);
#endif
output_drive(MMCSD_PIN_SELECT);
#if defined(MMCSD_PIN_SDI)
output_float(MMCSD_PIN_SDI);
#endif
mmcsd_deselect();
delay_ms(15);
/* begin initialization */
i = 0;
do
{
delay_ms(1);
mmcsd_select();
r1=mmcsd_go_idle_state();
mmcsd_deselect();
i++;
if(i == 0xFF)
{
mmcsd_deselect();
return r1;
}
} while(!bit_test(r1, 0));
i = 0;
do
{
delay_ms(1);
mmcsd_select();
r1=mmcsd_send_op_cond();
mmcsd_deselect();
i++;
if(i == 0xFF)
{
mmcsd_deselect();
return r1;
}
} while(r1 & MMCSD_IDLE);
/* figure out if we have an SD or MMC */
mmcsd_select();
r1=mmcsd_app_cmd();
r1=mmcsd_sd_send_op_cond();
mmcsd_deselect();
/* an mmc will return an 0x04 here */
if(r1 == 0x04)
g_card_type = MMC;
else
g_card_type = SD;
/* set block length to 512 bytes */
mmcsd_select();
r1 = mmcsd_set_blocklen(MMCSD_MAX_BLOCK_SIZE);
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
mmcsd_deselect();
/// this would be a good time to set a higher clock speed, 20MHz
#if defined(MMCSD_SPI_HW)
#if (getenv("CLOCK") <= 80000000)
#define MMC_SPI_CLK_DIV SPI_CLK_DIV_4
#else
#if defined(SPI_CLK_DIV_8)
#define MMC_SPI_CLK_DIV SPI_CLK_DIV_8
#else
#define MMC_SPI_CLK_DIV SPI_CLK_DIV_16
#endif
#endif
#error/warning the next line will only work if using SPI1
setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_XMIT_L_TO_H | MMC_SPI_CLK_DIV);
#endif
/* turn CRCs off to speed up reading/writing */
mmcsd_select();
r1 = mmcsd_crc_on_off(0);
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
mmcsd_deselect();
r1 = mmcsd_load_buffer();
g_mmcsdPartitionOffset = 0;
mmcsd_check_part(0x1EE);
mmcsd_check_part(0x1DE);
mmcsd_check_part(0x1CE);
mmcsd_check_part(0x1BE);
return r1;
}
MMCSD_err mmcsd_read_data(uint32_t address, uint16_t size, uint8_t* ptr)
{
MMCSD_err r1;
uint16_t i; // counter for loops
for(i = 0; i < size; i++)
{
r1 = mmcsd_read_byte(address++, ptr++);
if(r1 != MMCSD_GOODEC)
return r1;
}
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_read_block(uint32_t address, uint16_t size, uint8_t* ptr)
{
MMCSD_err ec;
uint16_t i; // counter for loops
// send command
mmcsd_select();
ec = mmcsd_read_single_block(address);
if(ec != MMCSD_GOODEC)
{
mmcsd_deselect();
return ec;
}
// wait for the data start token
ec = mmcsd_wait_for_token(DATA_START_TOKEN);
if(ec != MMCSD_GOODEC)
{
mmcsd_deselect();
return ec;
}
// read in the data
for(i = 0; i < size; i += 1)
ptr[i] = MMCSD_SPI_XFER(0xFF);
if(g_CRC_enabled)
{
/* check the crc */
if(make16(MMCSD_SPI_XFER(0xFF), MMCSD_SPI_XFER(0xFF)) != mmcsd_crc16(g_mmcsd_buffer, MMCSD_MAX_BLOCK_SIZE))
{
mmcsd_deselect();
return MMCSD_CRC_ERR;
}
}
else
{
/* have the card transmit the CRC, but ignore it */
MMCSD_SPI_XFER(0xFF);
MMCSD_SPI_XFER(0xFF);
}
mmcsd_deselect();
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_write_data(uint32_t address, uint16_t size, uint8_t* ptr)
{
MMCSD_err ec;
uint16_t i; // counter for loops
for(i = 0; i < size; i++)
{
ec = mmcsd_write_byte(address++, *ptr++);
if(ec != MMCSD_GOODEC)
return ec;
}
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_write_block(uint32_t address, uint16_t size, uint8_t* ptr)
{
MMCSD_err ec;
uint16_t i;
// send command
mmcsd_select();
ec = mmcsd_write_single_block(address);
if(ec != MMCSD_GOODEC)
{
mmcsd_deselect();
return ec;
}
// send a data start token
MMCSD_SPI_XFER(DATA_START_TOKEN);
// send all the data
for(i = 0; i < size; i += 1)
{
MMCSD_SPI_XFER(ptr[i]);
}
// if the CRC is enabled we have to calculate it, otherwise just send an 0xFFFF
if(g_CRC_enabled)
MMCSD_SPI_XFER(mmcsd_crc16(ptr, size));
else
{
MMCSD_SPI_XFER(0xFF);
MMCSD_SPI_XFER(0xFF);
}
// get the error code back from the card; "data accepted" is 0bXXX00101
ec = mmcsd_get_r1();
if(ec & 0x0A)
{
mmcsd_deselect();
return ec;
}
// wait for the line to go back high, this indicates that the write is complete
while(MMCSD_SPI_XFER(0xFF) == 0);
mmcsd_deselect();
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_go_idle_state(void)
{
mmcsd_send_cmd(GO_IDLE_STATE, 0);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_send_op_cond(void)
{
mmcsd_send_cmd(SEND_OP_COND, 0);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_send_if_cond(uint8_t r7[])
{
mmcsd_send_cmd(SEND_IF_COND, 0x45A);
return mmcsd_get_r7(r7);
}
MMCSD_err mmcsd_print_csd()
{
uint8_t
buf[16],
i,
r1;
// MMCs don't support this command
if(g_card_type == MMC)
return MMCSD_PARAM_ERR;
mmcsd_select();
mmcsd_send_cmd(SEND_CSD, 0);
r1 = mmcsd_get_r1();
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
r1 = mmcsd_wait_for_token(DATA_START_TOKEN);
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
for(i = 0; i < 16; i++)
buf[i] = MMCSD_SPI_XFER(0xFF);
mmcsd_deselect();
/*
printf("\r\nCSD_STRUCTURE: %X", (buf[0] & 0x0C) >> 2);
printf("\r\nTAAC: %X", buf[1]);
printf("\r\nNSAC: %X", buf[2]);
printf("\r\nTRAN_SPEED: %X", buf[3]);
printf("\r\nCCC: %lX", (make16(buf[4], buf[5]) & 0xFFF0) >> 4);
printf("\r\nREAD_BL_LEN: %X", buf[5] & 0x0F);
printf("\r\nREAD_BL_PARTIAL: %X", (buf[6] & 0x80) >> 7);
printf("\r\nWRITE_BLK_MISALIGN: %X", (buf[6] & 0x40) >> 6);
printf("\r\nREAD_BLK_MISALIGN: %X", (buf[6] & 0x20) >> 5);
printf("\r\nDSR_IMP: %X", (buf[6] & 0x10) >> 4);
printf("\r\nC_SIZE: %lX", (((buf[6] & 0x03) << 10) | (buf[7] << 2) | ((buf[8] & 0xC0) >> 6)));
printf("\r\nVDD_R_CURR_MIN: %X", (buf[8] & 0x38) >> 3);
printf("\r\nVDD_R_CURR_MAX: %X", buf[8] & 0x07);
printf("\r\nVDD_W_CURR_MIN: %X", (buf[9] & 0xE0) >> 5);
printf("\r\nVDD_W_CURR_MAX: %X", (buf[9] & 0x1C) >> 2);
printf("\r\nC_SIZE_MULT: %X", ((buf[9] & 0x03) << 1) | ((buf[10] & 0x80) >> 7));
printf("\r\nERASE_BLK_EN: %X", (buf[10] & 0x40) >> 6);
printf("\r\nSECTOR_SIZE: %X", ((buf[10] & 0x3F) << 1) | ((buf[11] & 0x80) >> 7));
printf("\r\nWP_GRP_SIZE: %X", buf[11] & 0x7F);
printf("\r\nWP_GRP_ENABLE: %X", (buf[12] & 0x80) >> 7);
printf("\r\nR2W_FACTOR: %X", (buf[12] & 0x1C) >> 2);
printf("\r\nWRITE_BL_LEN: %X", ((buf[12] & 0x03) << 2) | ((buf[13] & 0xC0) >> 6));
printf("\r\nWRITE_BL_PARTIAL: %X", (buf[13] & 0x20) >> 5);
printf("\r\nFILE_FORMAT_GRP: %X", (buf[14] & 0x80) >> 7);
printf("\r\nCOPY: %X", (buf[14] & 0x40) >> 6);
printf("\r\nPERM_WRITE_PROTECT: %X", (buf[14] & 0x20) >> 5);
printf("\r\nTMP_WRITE_PROTECT: %X", (buf[14] & 0x10) >> 4);
printf("\r\nFILE_FORMAT: %X", (buf[14] & 0x0C) >> 2);
printf("\r\nCRC: %X", buf[15]);
*/
return r1;
}
MMCSD_err mmcsd_print_cid()
{
uint8_t
buf[16],
i,
r1;
// MMCs don't support this command
if(g_card_type == MMC)
return MMCSD_PARAM_ERR;
mmcsd_select();
mmcsd_send_cmd(SEND_CID, 0);
r1 = mmcsd_get_r1();
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
r1 = mmcsd_wait_for_token(DATA_START_TOKEN);
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
for(i = 0; i < 16; i++)
buf[i] = MMCSD_SPI_XFER(0xFF);
mmcsd_deselect();
/*
printf("\r\nManufacturer ID: %X", buf[0]);
printf("\r\nOEM/Application ID: %c%c", buf[1], buf[2]);
printf("\r\nOEM/Application ID: %c%c%c%c%c", buf[3], buf[4], buf[5], buf[6], buf[7]);
printf("\r\nProduct Revision: %X", buf[8]);
printf("\r\nSerial Number: %X%X%X%X", buf[9], buf[10], buf[11], buf[12]);
printf("\r\nManufacturer Date Code: %X%X", buf[13] & 0x0F, buf[14]);
printf("\r\nCRC-7 Checksum: %X", buf[15]);
*/
return r1;
}
MMCSD_err mmcsd_sd_status(uint8_t r2[])
{
uint8_t i;
mmcsd_select();
mmcsd_send_cmd(APP_CMD, 0);
r2[0]=mmcsd_get_r1();
mmcsd_deselect();
mmcsd_select();
mmcsd_send_cmd(SD_STATUS, 0);
for(i = 0; i < 64; i++)
MMCSD_SPI_XFER(0xFF);
mmcsd_deselect();
return mmcsd_get_r2(r2);
}
MMCSD_err mmcsd_send_status(uint8_t r2[])
{
mmcsd_send_cmd(SEND_STATUS, 0);
return mmcsd_get_r2(r2);
}
MMCSD_err mmcsd_set_blocklen(uint32_t blocklen)
{
mmcsd_send_cmd(SET_BLOCKLEN, blocklen);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_read_single_block(uint32_t address)
{
mmcsd_send_cmd(READ_SINGLE_BLOCK, address);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_write_single_block(uint32_t address)
{
mmcsd_send_cmd(WRITE_BLOCK, address);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_sd_send_op_cond(void)
{
mmcsd_send_cmd(SD_SEND_OP_COND, 0);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_app_cmd(void)
{
mmcsd_send_cmd(APP_CMD, 0);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_read_ocr(int r3[])
{
mmcsd_send_cmd(READ_OCR, 0);
return mmcsd_get_r3(r3);
}
MMCSD_err mmcsd_crc_on_off(int1 crc_enabled)
{
mmcsd_send_cmd(CRC_ON_OFF, crc_enabled);
g_CRC_enabled = crc_enabled;
return mmcsd_get_r1();
}
MMCSD_err mmcsd_send_cmd(uint8_t cmd, uint32_t arg)
{
uint8_t packet[6]; // the entire command, argument, and crc in one variable
// construct the packet
// every command on an SD card is or'ed with 0x40
packet[0] = cmd | 0x40;
packet[1] = make8(arg, 3);
packet[2] = make8(arg, 2);
packet[3] = make8(arg, 1);
packet[4] = make8(arg, 0);
// calculate the crc if needed
if(g_CRC_enabled)
packet[5] = mmcsd_crc7(packet, 5);
else
packet[5] = 0xFF;
// transfer the command and argument, with an extra 0xFF hacked in there
MMCSD_SPI_XFER(packet[0]);
MMCSD_SPI_XFER(packet[1]);
MMCSD_SPI_XFER(packet[2]);
MMCSD_SPI_XFER(packet[3]);
MMCSD_SPI_XFER(packet[4]);
MMCSD_SPI_XFER(packet[5]);
//! spi_write2(packet[0]);
//! spi_write2(packet[1]);
//! spi_write2(packet[2]);
//! spi_write2(packet[3]);
//! spi_write2(packet[4]);
//! spi_write2(packet[5]);
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_get_r1(void)
{
uint8_t
response = 0, // place to hold the response coming back from the SPI line
timeout = 0xFF; // maximum amount loops to wait for idle before getting impatient and leaving the function with an error code
// loop until timeout == 0
while(timeout)
{
// read what's on the SPI line
// the SD/MMC requires that you leave the line high when you're waiting for data from it
response = MMCSD_SPI_XFER(0xFF);
//response = MMCSD_SPI_XFER(0x00);//leave the line idle
// check to see if we got a response
if(response != 0xFF)
{
// fill in the response that we got and leave the function
return response;
}
// wait for a little bit longer
timeout--;
}
// for some reason, we didn't get a response back from the card
// return the proper error codes
return RESP_TIMEOUT;
}
MMCSD_err mmcsd_get_r2(uint8_t r2[])
{
r2[1] = mmcsd_get_r1();
r2[0] = MMCSD_SPI_XFER(0xFF);
return 0;
}
MMCSD_err mmcsd_get_r3(uint8_t r3[])
{
return mmcsd_get_r7(r3);
}
MMCSD_err mmcsd_get_r7(uint8_t r7[])
{
uint8_t i; // counter for loop
// the top byte of r7 is r1
r7[4]=mmcsd_get_r1();
// fill in the other 4 bytes
for(i = 0; i < 4; i++)
r7[3 - i] = MMCSD_SPI_XFER(0xFF);
return r7[4];
}
MMCSD_err mmcsd_wait_for_token(uint8_t token)
{
MMCSD_err r1;
// get a token
r1 = mmcsd_get_r1();
// check to see if the token we recieved was the one that we were looking for
if(r1 == token)
return MMCSD_GOODEC;
// if that wasn't right, return the error
return r1;
}
unsigned int8 mmcsd_crc7(char *data,uint8_t length)
{
uint8_t i, ibit, c, crc;
crc = 0x00; // Set initial value
for (i = 0; i < length; i++, data++)
{
c = *data;
for (ibit = 0; ibit < 8; ibit++)
{
crc = crc << 1;
if ((c ^ crc) & 0x80) crc = crc ^ 0x09; // ^ is XOR
c = c << 1;
}
crc = crc & 0x7F;
}
shift_left(&crc, 1, 1); // MMC card stores the result in the top 7 bits so shift them left 1
// Should shift in a 1 not a 0 as one of the cards I have won't work otherwise
return crc;
}
uint16_t mmcsd_crc16(char *data, uint8_t length)
{
uint8_t i, ibit, c;
uint16_t crc;
crc = 0x0000; // Set initial value
for (i = 0; i < length; i++, data++)
{
c = *data;
for (ibit = 0; ibit < 8; ibit++)
{
crc = crc << 1;
if ((c ^ crc) & 0x8000) crc = crc ^ 0x1021; // ^ is XOR
c = c << 1;
}
crc = crc & 0x7FFF;
}
shift_left(&crc, 2, 1); // MMC card stores the result in the top 7 bits so shift them left 1
// Should shift in a 1 not a 0 as one of the cards I have won't work otherwise
return crc;
}
void mmcsd_select()
{
output_low(MMCSD_PIN_SELECT);
}
void mmcsd_deselect()
{
MMCSD_SPI_XFER(0xFF);
output_high(MMCSD_PIN_SELECT);
}
MMCSD_err mmcsd_load_buffer(void)
{
g_MMCSDBufferChanged = FALSE;
return(mmcsd_read_block(g_mmcsdBufferAddress, MMCSD_MAX_BLOCK_SIZE, g_mmcsd_buffer));
}
MMCSD_err mmcsd_flush_buffer(void)
{
if (g_MMCSDBufferChanged)
{
g_MMCSDBufferChanged = FALSE;
return(mmcsd_write_block(g_mmcsdBufferAddress, MMCSD_MAX_BLOCK_SIZE, g_mmcsd_buffer));
}
return(0); //ok
}
MMCSD_err mmcsd_move_buffer(uint32_t new_addr)
{
MMCSD_err ec = MMCSD_GOODEC;
uint32_t
//cur_block,
new_block;
// make sure we're still on the same block
//cur_block = g_mmcsdBufferAddress - (g_mmcsdBufferAddress % MMCSD_MAX_BLOCK_SIZE);
new_block = new_addr - (new_addr % MMCSD_MAX_BLOCK_SIZE);
new_block += g_mmcsdPartitionOffset;
//if(cur_block != new_block)
if(g_mmcsdBufferAddress != new_block)
{
// dump the old buffer
if (g_MMCSDBufferChanged)
{
ec = mmcsd_flush_buffer();
if(ec != MMCSD_GOODEC)
return ec;
g_MMCSDBufferChanged = FALSE;
}
// figure out the best place for a block
g_mmcsdBufferAddress = new_block;
// load up a new buffer
ec = mmcsd_load_buffer();
}
return ec;
}
MMCSD_err mmcsd_read_byte(uint32_t addr, char* data)
{
MMCSD_err ec;
ec = mmcsd_move_buffer(addr);
if(ec != MMCSD_GOODEC)
{
return ec;
}
*data = g_mmcsd_buffer[addr % MMCSD_MAX_BLOCK_SIZE];
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_write_byte(uint32_t addr, char data)
{
MMCSD_err ec;
ec = mmcsd_move_buffer(addr);
if(ec != MMCSD_GOODEC)
return ec;
g_mmcsd_buffer[addr % MMCSD_MAX_BLOCK_SIZE] = data;
g_MMCSDBufferChanged = TRUE;
return MMCSD_GOODEC;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment