Click here to Skip to main content
15,884,099 members
Articles / Mobile Apps

TCP/IP Stack, FAT16 System on a Microcontroller

Rate me:
Please Sign up or sign in to vote.
4.53/5 (10 votes)
31 Jul 2011GPL35 min read 43.4K   2.1K   36  
TCP/IP Stack, FAT16 System on a Microcontroller
/*
 * file.c
 *
 *  Created on: 21/07/2011
 *      Author: lyl
 */
#include "global.h"
#include "fat.h"
#include "file.h"

//-----------------------------------------------------------------------------
// Locals
//-----------------------------------------------------------------------------
static FL_FILE			_files[FATFS_MAX_OPEN_FILES];
static int				_filelib_init = 0;
static int				_filelib_valid = 0;
static FL_FILE*			_open_file_list = NULL;
static FL_FILE*			_free_file_list = NULL;

static FL_FILE* _open_file(const char *path);
static void _free_file(FL_FILE* file);

void fl_init(void)
{	
	int i;

	// Add all file objects to free list
	for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
	{
		_files[i].next = _free_file_list;
		_free_file_list = &_files[i];
	}

	_filelib_init = 1;
}

int fl_list_opendir(const char *path, FAT_DIR_STATUS *dirls)
{
	int levels;
	UINT32 cluster = FAT32_INVALID_CLUSTER;

	// If first call to library, initialise
	CHECK_FL_INIT();

	// If path is in the root dir
	cluster =0;// fatfs_get_root_cluster();

	fatfs_list_directory_start(dirls, cluster);

	return cluster != FAT32_INVALID_CLUSTER ? 1 : 0;
}

int fl_list_readdir(FAT_DIR_STATUS *dirls, FAT_DIR_ENTRY *entry)
{
	int res = 0;

	// If first call to library, initialise
	CHECK_FL_INIT();

	res = fatfs_list_directory_next(dirls, entry);

	return res;
}



//-----------------------------------------------------------------------------
// _read_sector: Read a sector from disk to file
//-----------------------------------------------------------------------------
static int _read_sector(FL_FILE* file, UINT32 offset)
{
	UINT32 Sector = 0;
	UINT32 ClusterIdx = 0;
	UINT32 Cluster = 0;
	UINT32 i;
	UINT32 lba;

	FAT_SYSTEM * _fs;

	_fs=get_current_fat_system();

	// Find cluster index within file & sector with cluster
	ClusterIdx = offset / _fs->sectors_per_cluster;
	Sector = offset - (ClusterIdx * _fs->sectors_per_cluster);

	// Quick lookup for next link in the chain
	if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
		Cluster = file->last_fat_lookup.CurrentCluster;
	// Else walk the chain
	else
	{
		// Starting from last recorded cluster?
		if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
		{
			i = file->last_fat_lookup.ClusterIdx;
			Cluster = file->last_fat_lookup.CurrentCluster;
		}
		// Start searching from the beginning..
		else
		{
			// Set start of cluster chain to initial value
			i = 0;
			Cluster = file->startcluster;					
		}

		// Follow chain to find cluster to read
		for ( ;i<ClusterIdx; i++)
		{
			UINT32 nextCluster;
			
			// Does the entry exist in the cache?
			if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))			
			{
				// Scan file linked list to find next entry
				nextCluster = fatfs_find_next_cluster(&_fs, Cluster);

				// Push entry into cache
				fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
			}			

			Cluster = nextCluster;
		}

		// Record current cluster lookup details (if valid)
		if (Cluster != FAT32_LAST_CLUSTER)
		{
			file->last_fat_lookup.CurrentCluster = Cluster;
			file->last_fat_lookup.ClusterIdx = ClusterIdx;
		}
	}

	// If end of cluster chain then return false
	if (Cluster == FAT32_LAST_CLUSTER) 
		return 0;

	// Calculate sector address
	lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;

	char debug[20];
	FAT_SYSTEM *fs;
	fs=get_current_fat_system();
	
	DWORD size;
	size= fs->rootdir_sectors;

	sprintf(debug,"LBA:%d SEC:%d\r\n",lba,size);
	LW_ASSERT(TRUE,debug);

	// Read sector of file
	return fatfs_sector_reader(lba, size,NULL);
}


FAT_DIR_STATUS dirstat;
FAT_DIR_ENTRY dirent;

void fl_listdirectory(const char *path)
{
	int filenumber = 0;
	char debug[50];

	// If first call to library, initialise
	CHECK_FL_INIT();

	LW_ASSERT(TRUE,"\r\nNo.             Filename\r\n");

	if (fl_list_opendir(path, &dirstat))
	{

		//fl_list_readdir(&dirstat, &dirent);
		while (fl_list_readdir(&dirstat, &dirent))
		{
			if (dirent.is_dir)
			{
				sprintf(debug,"%d - %s <DIR> (0x%08lx)\r\n",++filenumber, dirent.filename, dirent.cluster);
				LW_ASSERT(TRUE,debug);
			}
			else
			{
				sprintf(debug,"%d - %s [%d bytes] (0x%08lx)\r\n",++filenumber, dirent.filename, dirent.size, dirent.cluster);
				LW_ASSERT(TRUE,debug);
				
				strcpy(debug,path);
				FL_FILE* file=_open_file(strcat(debug,dirent.filename));
				fl_fread(debug,file->filelength,file);
				LW_ASSERT(TRUE,debug);
				_free_file(file);
			}
		}
	}

	LW_ASSERT(TRUE,"\r\nEND OF FILE LIST\r\n");
}


//-----------------------------------------------------------------------------
// _open_directory: Cycle through path string to find the start cluster
// address of the highest subdir.
//-----------------------------------------------------------------------------
static int _open_directory(char *path, unsigned long *pathCluster)
{
	int levels;
	int sublevel;
	char currentfolder[FATFS_MAX_LONG_FILENAME];
	FAT_FILE_ENTRY sfEntry;
	unsigned long startcluster;
	FAT_SYSTEM * fs;

	fs=get_current_fat_system();

	// Set starting cluster to root cluster
	startcluster = fatfs_get_root_cluster(fs);

	// Find number of levels
	levels = fatfs_total_path_levels(path);

	// Cycle through each level and get the start sector
	for (sublevel=0;sublevel<(levels+1);sublevel++) 
	{
		if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
			return 0;

		// Find clusteraddress for folder (currentfolder) 
		if (fatfs_get_file_entry(fs, startcluster, currentfolder,&sfEntry))
		{
			// Check entry is folder
			if (fatfs_entry_is_dir(&sfEntry))
				startcluster = (((unsigned long)sfEntry.FstClusHI)<<16) + sfEntry.FstClusLO;
			else
				return 0;
		}
		else
			return 0;
	}

	*pathCluster = startcluster;
	return 1;
}

//-----------------------------------------------------------------------------
// _check_file_open: Returns true if the file is already open
//-----------------------------------------------------------------------------
static int _check_file_open(FL_FILE* file)
{
	FL_FILE* openFile = _open_file_list;
	
	// Compare open files
	while (openFile)
	{
		// If not the current file 
		if (openFile != file)
		{
			// Compare path and name
			//if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
			//	return 1;
		}

		openFile = openFile->next;
	}

	return 0;
}

//-----------------------------------------------------------------------------
// fl_fread: Read a block of data from the file
//-----------------------------------------------------------------------------
int fl_fread(void * buffer, int length, void *f )
{
	unsigned long sector;
	unsigned long offset;
	int copyCount;
	int bytesRead = 0;	
	int count;

	count=length;

	FL_FILE *file = (FL_FILE *)f;

	// If first call to library, initialise
	CHECK_FL_INIT();

	if (buffer==NULL || file==NULL)
		return -1;

	// No read permissions
	//if (!(file->flags & FILE_READ))
	//	return -1;

	// Nothing to be done
	//if (!count)
	//	return 0;

	// Check if read starts past end of file
	//if (file->bytenum >= file->filelength)
	//	return -1;

	// Calculate start sector
	sector = file->bytenum / FAT_SECTOR_SIZE;

	// Offset to start copying data from first sector
	offset = file->bytenum % FAT_SECTOR_SIZE;

	while (bytesRead < length)
	{		
		// Do we need to re-read the sector?
		if (file->file_data.address != sector)
		{
			// Flush un-written data to file
			//if (file->file_data.dirty)
			//	fl_fflush(file);

			// Get LBA of sector offset within file
			if (!_read_sector(file, sector))
				// Read failed - out of range (probably)
				break;

			file->file_data.address = sector;
			file->file_data.dirty = 0;
		}

		// We have upto one sector to copy
		copyCount = FAT_SECTOR_SIZE - offset;

		// Only require some of this sector?
		if (copyCount > (count - bytesRead))
			copyCount = (count - bytesRead);

		// Copy to application buffer
		memcpy( (unsigned char*)((unsigned char*)buffer + bytesRead), (unsigned char*)(file->file_data.sector + offset), copyCount);
	
		// Increase total read count 
		bytesRead += copyCount;

		// Increment file pointer
		file->bytenum += copyCount;

		// Move onto next sector and reset copy offset
		sector++;
		offset = 0;
	}	

	return bytesRead;
}

//-----------------------------------------------------------------------------
// _allocate_file: Find a slot in the open files buffer for a new file
//-----------------------------------------------------------------------------
static FL_FILE* _allocate_file(void)
{
	// Allocate free file
	FL_FILE* file = _free_file_list;

	if (file)
	{
		_free_file_list = file->next;

		// Add to open list
		file->next = _open_file_list;
		_open_file_list = file;
	}

	return file;
}

//-----------------------------------------------------------------------------
// _free_file: Free open file handle
//-----------------------------------------------------------------------------
static void _free_file(FL_FILE* file)
{
	FL_FILE* openFile = _open_file_list;
	FL_FILE* lastFile = NULL;
	
	// Remove from open list
	while (openFile)
	{
		// If the current file 
		if (openFile == file)
		{
			if (lastFile)
				lastFile->next = openFile->next;
			else
				_open_file_list = openFile->next;

			break;
		}

		lastFile = openFile;
		openFile = openFile->next;
	}

	// Add to free list
	file->next = _free_file_list;
	_free_file_list = file;
}

//-----------------------------------------------------------------------------
// _open_file: Open a file for reading
//-----------------------------------------------------------------------------
static FL_FILE* _open_file(const char *path)
{
	FL_FILE* file; 
	
	//#define struct fat_dir_entry FAT_FILE_ENTRY
	
	FAT_FILE_ENTRY sfEntry;

	// Allocate a new file handle
	file = _allocate_file();
	if (!file)
		return NULL;

	// Clear filename
	memset(file->path, '\0', sizeof(file->path));
	memset(file->filename, '\0', sizeof(file->filename));

	// Split full path into filename and directory path
	if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
	{
		_free_file(file);
		return NULL;
	}

	char debug[50];
	sprintf(debug,"Parsed File Name:%s\r\n",file->filename);
	LW_ASSERT(TRUE,debug);

	// Check if file already open
	if (_check_file_open(file))
	{
		_free_file(file);
		return NULL;
	}

	// If file is in the root dir
	if (file->path[0]==0)
		file->parentcluster = fatfs_get_root_cluster();
	else
	{
		// Find parent directory start cluster
		if (!_open_directory(file->path, &file->parentcluster))
		{
			_free_file(file);
			return NULL;
		}
	}

	LW_ASSERT(TRUE,"About to Open file\r\n");

	// Using dir cluster address search for filename
	if (fatfs_get_file_entry(get_current_fat_system(), file->parentcluster, file->filename,&sfEntry))
	{	// Make sure entry is file not dir!
			// Initialise file details
		memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
		file->filelength = fat_get_file_size(&sfEntry);
		file->bytenum = 0;
		file->startcluster =fat_get_file_cluster(&sfEntry);
		char debug[20];
		sprintf(debug,"File Cluster:%d\r\n",file->startcluster);
		LW_ASSERT(TRUE,debug);

		file->file_data.address = 0xFFFFFFFF;
		file->file_data.dirty = 0;
		file->filelength_changed = 0;

		// Quick lookup for next link in the chain
		file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
		file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;

		return file;
	}

	_free_file(file);
	return NULL;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions