Changeset - 06ea8b348829
[Not reviewed]
master/master/lib/logger.c
Show inline comments
 
new file 100644
 
/*
 
 * CFile1.c
 
 *
 
 * Created: 11/7/2012 8:05:44 PM
 
 *  Author: mkanning
 
 */ 
 
 
#include <string.h>
 
#include <avr/pgmspace.h>
 
#include <avr/sleep.h>
 
#include "sd/fat.h"
 
#include "sd/fat_config.h"
 
#include "sd/partition.h"
 
#include "sd/sd_raw.h"
 
#include "sd/sd_raw_config.h"
 
#include "logger.h"
 
 
/* 
 
	//console prompts and responses
 
 * I implemented an example application providing a simple command prompt which is accessible
 
 * via the UART at 9600 Baud. With commands similiar to the Unix shell you can browse different
 
 * directories, read and write files, create new ones and delete them again. Not all commands are
 
 * available in all software configurations.
 
 * - <tt>cat \<file\></tt>\n
 
 *   Writes a hexdump of \<file\> to the terminal.
 
 * - <tt>cd \<directory\></tt>\n
 
 *   Changes current working directory to \<directory\>.
 
 * - <tt>disk</tt>\n
 
 *   Shows card manufacturer, status, filesystem capacity and free storage space.
 
 * - <tt>init</tt>\n
 
 *   Reinitializes and reopens the memory card.
 
 * - <tt>ls</tt>\n
 
 *   Shows the content of the current directory.
 
 * - <tt>mkdir \<directory\></tt>\n
 
 *   Creates a directory called \<directory\>.
 
 * - <tt>mv \<file\> \<file_new\></tt>\n
 
 *   Renames \<file\> to \<file_new\>.
 
 * - <tt>rm \<file\></tt>\n
 
 *   Deletes \<file\>.
 
 * - <tt>sync</tt>\n
 
 *   Ensures all buffered data is written to the card.
 
 * - <tt>touch \<file\></tt>\n
 
 *   Creates \<file\>.
 
 * - <tt>write \<file\> \<offset\></tt>\n
 
 *   Writes text to \<file\>, starting from \<offset\>. The text is read
 
 *   from the UART, line by line. Finish with an empty line.
 
 
 
	//config edits
 
  * By changing the MCU* variables in the Makefile, you can use other Atmel
 
  * microcontrollers or different clock speeds. You might also want to change
 
  * the configuration defines in the files fat_config.h, partition_config.h,
 
  * sd_raw_config.h and sd-reader_config.h. For example, you could disable
 
  * write support completely if you only need read support.
 

	
 
 */
 
 
void logger_setup()
 
{
 
	while(1)
 
	{
 
		//check for sd exist/power/ready
 
		/* setup sd card slot */
 
		if(!sd_raw_init()) //sd_raw.c
 
		{
 
				#if DEBUG
 
		uart_puts_p(PSTR("MMC/SD initialization failed\n"));
 
			#endif
 
			continue;
 
		}
 
	
 
	
 
		//create partition BEGIN
 
		/* open first partition */ 
 
		struct partition_struct* partition = partition_open(sd_raw_read,
 
															sd_raw_read_interval,
 
	#if SD_RAW_WRITE_SUPPORT //probably want this to be true ??
 
															sd_raw_write,
 
															sd_raw_write_interval,
 
	#else
 
															0,
 
															0,
 
	#endif
 
															0
 
															);
 
		//check that partition was created correctly
 
		if(!partition)
 
		{
 
			/* If the partition did not open, assume the storage device
 
				* is a "superfloppy", i.e. has no MBR.
 
				*/
 
			partition = partition_open(sd_raw_read,
 
										sd_raw_read_interval,
 
	#if SD_RAW_WRITE_SUPPORT //probably want this to be true ??
 
										sd_raw_write,
 
										sd_raw_write_interval,
 
	#else
 
										0,
 
										0,
 
	#endif
 
										-1
 
										);
 
			if(!partition)
 
			{
 
	#if DEBUG
 
				uart_puts_p(PSTR("opening partition failed\n"));
 
	#endif
 
				continue;
 
			}
 
		}
 
		//open partition END
 
	
 
	
 
		//open file system BEGIN
 
		/* open file system */
 
		struct fat_fs_struct* fs = fat_open(partition);
 
		if(!fs)
 
		{
 
			#if DEBUG
 
			uart_puts_p(PSTR("opening file system failed\n"));
 
			#endif
 
			continue;
 
		}
 
		//open file system END
 
	
 
	
 
		//open directory BEGIN
 
		/* open root directory */
 
		struct fat_dir_entry_struct directory;
 
		fat_get_dir_entry_of_path(fs, "/", &directory);
 

	
 
		struct fat_dir_struct* dd = fat_open_dir(fs, &directory);
 
		if(!dd)
 
		{
 
			#if DEBUG
 
			uart_puts_p(PSTR("opening root directory failed\n"));
 
			#endif
 
			continue;
 
		}
 
		//open directory END
 
		
 
		
 
		//simplified version of console BEGIN
 
		/* provide a simple shell */
 
		char buffer[24];
 
		while(1)
 
		{
 
			/* execute command */
 
			/* search file in current directory and open it */
 
			struct fat_file_struct* fd = open_file_in_dir(fs, dd, "data.csv"); //logger.h
 
			if(!fd)
 
			{
 
				//error open file handling
 
				continue;
 
			}
 

	
 
			int32_t offset = 5;//strtolong(offset_value);
 
			if(!fat_seek_file(fd, &offset, FAT_SEEK_SET)) //seek to begin or end or what ??
 
			{
 
				//error seek to file handling
 
				
 
				fat_close_file(fd);
 
				continue;
 
			}
 

	
 
			/* read text from the shell and write it to the file */
 
			uint8_t data_len;
 
			while(1)
 
			{
 
				/* write text to file !! */
 
				if(fat_write_file(fd, (uint8_t*) buffer, data_len) != data_len)
 
				{
 
					uart_puts_p(PSTR("error writing to file\n"));
 
					break;
 
				}
 
			}
 

	
 
			fat_close_file(fd); //may want to leave file open ??
 
		}
 
		//simplified version of console END	
 
		
 
		
 
		//prepare for closing SD connection BEGIN
 
		/* close directory */
 
		fat_close_dir(dd); //fat.c
 

	
 
		/* close file system */
 
		fat_close(fs); //fat.c
 

	
 
		/* close partition */
 
		partition_close(partition); //partition.c
 
		//prepare for closing SD connection END
 
 
	}
 
}	
 
 
//writes a single line to the SD card
 
uint8_t logger_writeLine(char* dateLine, uint8_t length)
 
{
 
	
 
}
 
 
//i think opens a file so it can be read/written
 
struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name)
 
{
 
	struct fat_dir_entry_struct file_entry;
 
	if(!find_file_in_dir(fs, dd, name, &file_entry))
 
	return 0;
 

	
 
	return fat_open_file(fs, &file_entry); //fat.h
 
}
 

	
 
//i think searches for a file
 
uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry)
 
{
 
	while(fat_read_dir(dd, dir_entry))
 
	{
 
		if(strcmp(dir_entry->long_name, name) == 0)
 
		{
 
			fat_reset_dir(dd);
 
			return 1;
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
//i think this initializes the SD for read and write ??
 
\ No newline at end of file
master/master/lib/logger.h
Show inline comments
 
new file 100644
 
/*
 
 * logger.h
 
 *
 
 * Created: 11/7/2012 8:06:16 PM
 
 *  Author: mkanning
 
 */ 
 
 
 
#ifndef LOGGER_H_
 
#define LOGGER_H_
 
 
void logger_setup();
 
uint8_t logger_writeLine(char* dateLine, uint8_t length);
 
struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name);
 
uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry);
 
 
#endif /* LOGGER_H_ */
 
\ No newline at end of file
master/master/lib/sd/ChangeLog
Show inline comments
 
new file 100644
 

	
 
2012-06-12 sd-reader
 
	* fix capacity readout from csd register depending on format version
 
	* fix gcc strict-aliasing warnings (also somewhat enlarges code size)
 

	
 
2011-04-23 sd-reader
 
	* fix FAT access for cluster numbers beyond 2^15 (for FAT16) and 2^30 (for FAT32) (thanks to Darwin Engwer for testing)
 
	* correctly return disk-full condition from fat_write_file() on certain conditions
 
	* use byteorder memory access functions for fat_fs_get_free()
 
	* be more specific on the return value of fat_write_file()
 

	
 
2011-02-05 sd-reader
 
	* implement renaming a file or directory
 
	* rewrite byteorder handling to fix unaligned memory accesses on 32-bit and probably 16-bit architectures
 
	* make fat_create_file() not return failure if the file already exists
 
	* make the "cat" output respect the count of bytes actually read
 
	* document how to use fat_seek_file() for retrieving the file position
 

	
 
2010-10-10 sd-reader
 
	* Fix equal file names when reading two successive 8.3 directory entries.
 
	* Fix calculation of cluster positions beyond 4GB (32 bit integer overflow).
 
	* Fix endless looping of directory listing (occured with valid entry at end of last cluster).
 

	
 
2010-01-10 sd-reader
 
	* Make LFN support configurable.
 
	* Ignore LFN directory entries without 8.3 name.
 
	* Ignore LFN directory entries which do not match the 8.3 name's checksum.
 
	* Implement delayed directory entry updates (disabled by default) (thanks to Adam Mayer).
 
	* Speedup search for free cluster.
 
	* Fix memory leak when using the "init" command (thanks to Tibor Vilhan).
 
	* Fix ATmega328P-specific pin mappings.
 
	* Add some of the picoPower MCU variants.
 

	
 
2009-03-30 sd-reader
 
	* Make 8.3 basename and/or extension lowercase when told by Windows NT and later.
 
	* Add ATmega328 pin configuration.
 
	* Fix MMC/SD/SDHC distinction.
 
	* Fix raw block read/write buffering (thanks to Kurt Sterckx).
 
	* Fix fat size calculation for FAT16 when configured with FAT32.
 
	* Fix compilation for read-only configurations.
 
	* Fix card lock detection.
 
	* Make it easier to link with a C++ application (thanks to Jérôme Despatis).
 

	
 
2008-11-21 sd-reader
 
	* Support for SDHC cards (disabled by default).
 
	* Support for FAT32 (disabled by default).
 

	
 
2008-06-08 sd-reader
 
	* New "init" command to allow reinitialization of memory card.
 
	* Fix searching through multi-cluster directories.
 
	* Fix reading directory entries spanning a cluster border (backport from mega-eth).
 
	* Do not abort the whole lfn entry when the file name is too long, just drop single characters (backport from mega-eth).
 
	* Change fat16_get_dir_entry_of_path() to ignore a slash at the end (backport from mega-eth).
 
	* Make listing a directory's content much faster (backport from mega-eth).
 
	* Shrink code size by centralizing cluster offset calculation (backport from mega-eth).
 
	* Some other small fixes and optimizations.
 

	
 
2007-12-13 sd-reader
 
	* Dual-license the major implementation modules under GPL and LGPL.
 

	
 
2007-06-03 sd-reader
 
	* Fix reading beyond cached block (by Benjamin Meier).
 
	* Implement support for reading and writing file modification dates/times.
 
	  (Thanks to Torsten Seeboth for testing.)
 

	
 
2007-03-01 sd-reader
 
	* Avoid LFN directory entries for the "." and ".." directory references.
 
	  This prevented Windows from deleting directories.
 
	* Handle special case where the 8.3 filename begins with 0xe5.
 
	* Fix return value of fat16_delete_file() when deleting empty files.
 
	* Fix fat16_clear_cluster() which was zeroing only 16 of every 32 bytes.
 

	
 
2007-01-20 sd-reader
 
	* Fix directory creation.
 
	  - Correctly create "." and ".." directory entries (8.3 <-> lfn versions).
 
	  - Correctly clear cluster containing the directory entries for new directory.
 

	
 
2006-11-01 sd-reader
 
	* Implement creation and deletion of directories.
 
	* Clear the directory entries of new directory clusters.
 
	* Prevent linkage against printf().
 
	* Make the use of malloc()/free() optional.
 

	
 
2006-09-01 sd-reader
 
	* Fix shortening files.
 
	* Fix free disk space calculation.
 

	
 
2006-08-24 sd-reader
 
	* Improve sleep handling.
 
	* Display extended card information on boot and
 
	  when executing the "disk" shell command.
 
	* Correctly determine FAT type by cluster count.
 
	* Fix cluster allocation beyond card capacity.
 

	
 
2006-08-16 sd-reader
 
	* Provide FAT16 capacity and usage information.
 
	* Implement the backspace key in the mini shell.
 
	* Enter idle mode when waiting for uart activity.
 
	* Make the Card Select pin MCU dependent as well.
 
	* Add mini shell commands to documentation.
 

	
 
2006-08-08 sd-reader
 
	* Thanks go to Torsten Seeboth for his ongoing efforts
 
	  to test changes, fix regressions and give suggestions.
 
	  Many of the changes below were initiated by him.
 
	* Much more reliable card initialization.
 
	* Highly improved performance
 
	  - optional write buffering
 
	  - better cluster handling
 
	  - remove unneeded SPI access when reading from buffered block
 
	  - use highest spi frequency after card initialization
 
	* Add superfloppy support.
 
	* Better checks when opening a FAT16 filesystem.
 
	* Provide SPI pin mappings for commonly used ATmegas.
 
	* Fix resizing files, hangs could occur.
 
	* Fix overflow when creating files with names longer than 31 characters.
 
	* Fix numerous other small things.
 

	
 
2006-03-19 sd-reader
 
	* Fix speed regressions.
 

	
 
2006-03-16 sd-reader
 
	* Initial release.
 

	
master/master/lib/sd/FAQ
Show inline comments
 
new file 100644
 
Frequently Asked Questions for sd-reader
 
========================================
 

	
 
General
 
-------
 

	
 
Q: Which cards are supported?
 
A: All MMC/SD/SDHC/miniSD/microSD/microSDHC should work, although not all variants have been tested.
 
   Some very old (low capacity) cards might be broken as well. Cards with a capacity of 16 MB or
 
   less are usually formatted with FAT12, so these are supported in raw mode only, if at all.
 

	
 
Q: What data rates can I expect?
 
A: See the benchmark page on the homepage.
 
   http://www.roland-riegel.de/sd-reader/benchmarks/
 

	
 
Q: Are there boards available for purchase?
 
A: No.
 

	
 
Hardware
 
--------
 

	
 
Q: Where can I find the schematic?
 
A: Get it on the homepage.
 
   http://www.roland-riegel.de/sd-reader/sd-reader_circuit_latest.zip
 

	
 
Q: What if my card socket has no Card-Detect and/or Card-Lock switches?
 
A: Change sd_raw_config.h such that it looks like
 

	
 
   #define configure_pin_available() /* nothing */
 
   #define configure_pin_locked() /* nothing */
 

	
 
   #define get_pin_available() 0
 
   #define get_pin_locked() 1 
 

	
 
Q: All attempts to write to the card fail, although reading works. What's the problem?
 
A: Enable write support within sd_raw_config.h. And probably, your card socket has no Card-lock
 
   detection (see question above).
 

	
 
Q: The card initialization fails. What can I do?
 
A: Usually this is some kind of hardware problem.
 
   *  Check the physical connections.
 
   *  Keep the signal lines to the card as short as possible.
 
   *  Do not use diodes to derive the card's supply voltage. Use a 3.3V voltage regulator instead.
 
   *  Have a stable, buffered power supply and use capacitors for correct voltage regulator
 
      operation (see the schematics linked above).
 
   *  Use extra capacitors of 50uF and 100nF as close to the card as possible.
 
   *  When using an integrated level shifter or no level shifting at all (see the next question),
 
      try adding a pullup of 50k from the data-out line of the card to 3.3V.
 
   *  Make sure the limiting frequency of the level shifter is not exceeded. Have a look into its
 
      datasheet!
 
   *  Check the signals with a scope.
 

	
 
Q: What alternatives to resistor level shifting exist?
 
A: If you want to use additional devices with SPI or the resistor solution appears too ugly, there
 
   are two possibilities. Either operate the whole circuit with a single 3.3V supply and no level
 
   shifting at all or use a level shifting IC which interfaces the memory card to the AVR.
 
   Depending on your requirements, adequate devices could include MAX3378E, MAX3392E, MAX3395E,
 
   74LVC245 and 74HCT4050 (optionally together with 74HCT125). Please check the datasheets for the
 
   required DC/AC characteristics!
 

	
 
Software
 
--------
 

	
 
Q: What's the software license?
 
A: GPLv2 or (for most parts) LGPLv2.1. Before using a file, read its license terms included at the
 
   beginning of the file.
 

	
 
Q: What's the programming language used?
 
A: It's C, in compliance with the ISO C99 standard.
 

	
 
Q: What are these .patch-files provided?
 
A: Those record the source code differences between the old and new release. If you edited your
 
   private sd-reader version, it might be easier to apply the patch files using the "patch" utility
 
   common on Unix-like systems, rather than manually inserting the changes by hand. For Windows
 
   users, the GnuWin32 project provides a port of "patch".
 

	
 
Q: Where can I learn more about the library interface and how to use it?
 
A: Look into the HTML documentation provided online or within each source distribution. Also take
 
   the provided main.c as an example application and as a starting point.
 

	
 
Q: How do I adapt it to an ATmegaXYZ and my circuit in particular?
 
A: Add your MCU-specific pin configuration to sd_raw_config.h. Some commonly used ones are already
 
   included.
 

	
 
Q: How do I adapt it to a different MCU clock?
 
A: Change the MCU_FREQ variable within the Makefile.
 

	
 
Q: How do I adapt it to some different MCU architecture?
 
A: Change sd_raw_init(), sd_raw_send_byte(), sd_raw_rec_byte() within sd_raw.c and the pin
 
   definitions within sd_raw_config.h appropriately.
 

	
 
Q: Can the library be used with Arduino?
 
A: Yes. I do not have any experience with Arduino myself, but people report that it works with some
 
   minor modifications to the library code needed due to some different compiler settings. Please
 
   search the web for details.
 

	
 
Q: Can I use software SPI?
 
A: Yes, but the code is not prepared for it.
 

	
 
Q: My application crashes somewhere in the lib. Is there some bug hidden?
 
A: There might be a bug. Much more likely, however, is that you experience memory problems,
 
   especially heap/stack collisions. The crashes can appear everywhere, but typically this is not
 
   the place where the real problem is. Try to minimize the size of structures and other memory
 
   blocks your application uses. Sum up the amount of memory your application allocates with global
 
   and local variables and the memory you allocate dynamically with malloc(). The avr-nm utility
 
   also helps a lot here. When called with the "--print-size --size-sort" parameters, it lists all
 
   symbols and their code/memory size within the given file. See the avr-libc FAQ and the nm manual
 
   for further information.
 
   http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_ramoverlap
 
   http://sourceware.org/binutils/docs/binutils/nm.html
 

	
 
Q: Opening the FAT filesystem fails. What can I do?
 
A: Make sure there is a FAT16 or FAT32 filesystem on the card. This usually isn't possible for old
 
   cards with 16 MB or less. For larger ones, format the cards using the OS utilities, like the
 
   Windows Explorer, the Unix/Linux mkdosfs command or special SD card format tools.
 
   http://panasonic.jp/support/global/cs/sd/download/sd_formatter.html
 
   http://www.sdcard.org/consumers/formatter/
 

	
 
Q: Writing to the card returns no failure, but when checking the file's content the data is not
 
   there. What happens?
 
A: For performance reasons, writing to the card is always buffered. Before pulling the card out of
 
   its socket (or issuing a reset of the MCU), make sure sd_raw_sync() gets called such that all
 
   buffered data is written out to permanent card storage.
 

	
master/master/lib/sd/byteordering.c
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#include "byteordering.h"
 

	
 
/**
 
 * \addtogroup byteordering
 
 *
 
 * Architecture-dependent handling of byte-ordering.
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * Byte-order handling implementation (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \author Roland Riegel
 
 */
 

	
 
#if DOXYGEN || SWAP_NEEDED
 

	
 
/**
 
 * \internal
 
 * Swaps the bytes of a 16-bit integer.
 
 *
 
 * \param[in] i A 16-bit integer which to swap.
 
 * \returns The swapped 16-bit integer.
 
 */
 
uint16_t swap16(uint16_t i)
 
{
 
    return SWAP16(i);
 
}
 

	
 
/**
 
 * \internal
 
 * Swaps the bytes of a 32-bit integer.
 
 *
 
 * \param[in] i A 32-bit integer which to swap.
 
 * \returns The swapped 32-bit integer.
 
 */
 
uint32_t swap32(uint32_t i)
 
{
 
    return SWAP32(i);
 
}
 

	
 
#endif
 

	
 
/**
 
 * Reads a 16-bit integer from memory in little-endian byte order.
 
 *
 
 * \param[in] p Pointer from where to read the integer.
 
 * \returns The 16-bit integer read from memory.
 
 */
 
uint16_t read16(const uint8_t* p)
 
{
 
    return (((uint16_t) p[1]) << 8) |
 
           (((uint16_t) p[0]) << 0);
 
}
 

	
 
/**
 
 * Reads a 32-bit integer from memory in little-endian byte order.
 
 *
 
 * \param[in] p Pointer from where to read the integer.
 
 * \returns The 32-bit integer read from memory.
 
 */
 
uint32_t read32(const uint8_t* p)
 
{
 
    return (((uint32_t) p[3]) << 24) |
 
           (((uint32_t) p[2]) << 16) |
 
           (((uint32_t) p[1]) <<  8) |
 
           (((uint32_t) p[0]) <<  0);
 
}
 

	
 
/**
 
 * Writes a 16-bit integer into memory in little-endian byte order.
 
 *
 
 * \param[in] p Pointer where to write the integer to.
 
 * \param[in] i The 16-bit integer to write.
 
 */
 
void write16(uint8_t* p, uint16_t i)
 
{
 
    p[1] = (uint8_t) ((i & 0xff00) >> 8);
 
    p[0] = (uint8_t) ((i & 0x00ff) >> 0);
 
}
 

	
 
/**
 
 * Writes a 32-bit integer into memory in little-endian byte order.
 
 *
 
 * \param[in] p Pointer where to write the integer to.
 
 * \param[in] i The 32-bit integer to write.
 
 */
 
void write32(uint8_t* p, uint32_t i)
 
{
 
    p[3] = (uint8_t) ((i & 0xff000000) >> 24);
 
    p[2] = (uint8_t) ((i & 0x00ff0000) >> 16);
 
    p[1] = (uint8_t) ((i & 0x0000ff00) >>  8);
 
    p[0] = (uint8_t) ((i & 0x000000ff) >>  0);
 
}
 

	
 
/**
 
 * @}
 
 */
 

	
master/master/lib/sd/byteordering.h
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#ifndef BYTEORDERING_H
 
#define BYTEORDERING_H
 

	
 
#include <stdint.h>
 

	
 
#ifdef __cplusplus
 
extern "C"
 
{
 
#endif
 

	
 
/**
 
 * \addtogroup byteordering
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * Byte-order handling header (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \author Roland Riegel
 
 */
 

	
 
#define SWAP16(val) ((((uint16_t) (val)) << 8) | \
 
                     (((uint16_t) (val)) >> 8)   \
 
                    )
 
#define SWAP32(val) (((((uint32_t) (val)) & 0x000000ff) << 24) | \
 
                     ((((uint32_t) (val)) & 0x0000ff00) <<  8) | \
 
                     ((((uint32_t) (val)) & 0x00ff0000) >>  8) | \
 
                     ((((uint32_t) (val)) & 0xff000000) >> 24)   \
 
                    )
 

	
 
#if LITTLE_ENDIAN || __AVR__
 
#define SWAP_NEEDED 0
 
#elif BIG_ENDIAN
 
#define SWAP_NEEDED 1
 
#else
 
#error "Endianess undefined! Please define LITTLE_ENDIAN=1 or BIG_ENDIAN=1."
 
#endif
 

	
 
/**
 
 * \def HTOL16(val)
 
 *
 
 * Converts a 16-bit integer from host byte order to little-endian byte order.
 
 *
 
 * Use this macro for compile time constants only. For variable values
 
 * use the function htol16() instead. This saves code size.
 
 *
 
 * \param[in] val A 16-bit integer in host byte order.
 
 * \returns The given 16-bit integer converted to little-endian byte order.
 
 */
 
/**
 
 * \def HTOL32(val)
 
 *
 
 * Converts a 32-bit integer from host byte order to little-endian byte order.
 
 *
 
 * Use this macro for compile time constants only. For variable values
 
 * use the function htol32() instead. This saves code size.
 
 *
 
 * \param[in] val A 32-bit integer in host byte order.
 
 * \returns The given 32-bit integer converted to little-endian byte order.
 
 */
 
/**
 
 * \def LTOH16(val)
 
 *
 
 * Converts a 16-bit integer from little-endian byte order to host byte order.
 
 *
 
 * Use this macro for compile time constants only. For variable values
 
 * use the function ltoh16() instead. This saves code size.
 
 *
 
 * \param[in] val A 16-bit integer in little-endian byte order.
 
 * \returns The given 16-bit integer converted to host byte order.
 
 */
 
/**
 
 * \def LTOH32(val)
 
 *
 
 * Converts a 32-bit integer from little-endian byte order to host byte order.
 
 *
 
 * Use this macro for compile time constants only. For variable values
 
 * use the function ltoh32() instead. This saves code size.
 
 *
 
 * \param[in] val A 32-bit integer in little-endian byte order.
 
 * \returns The given 32-bit integer converted to host byte order.
 
 */
 

	
 
#if SWAP_NEEDED
 
#define HTOL16(val) SWAP16(val)
 
#define HTOL32(val) SWAP32(val)
 
#define LTOH16(val) SWAP16(val)
 
#define LTOH32(val) SWAP32(val)
 
#else
 
#define HTOL16(val) (val)
 
#define HTOL32(val) (val)
 
#define LTOH16(val) (val)
 
#define LTOH32(val) (val)
 
#endif
 

	
 
#if DOXYGEN
 

	
 
/**
 
 * Converts a 16-bit integer from host byte order to little-endian byte order.
 
 *
 
 * Use this function on variable values instead of the
 
 * macro HTOL16(). This saves code size.
 
 *
 
 * \param[in] h A 16-bit integer in host byte order.
 
 * \returns The given 16-bit integer converted to little-endian byte order.
 
 */
 
uint16_t htol16(uint16_t h);
 

	
 
/**
 
 * Converts a 32-bit integer from host byte order to little-endian byte order.
 
 *
 
 * Use this function on variable values instead of the
 
 * macro HTOL32(). This saves code size.
 
 *
 
 * \param[in] h A 32-bit integer in host byte order.
 
 * \returns The given 32-bit integer converted to little-endian byte order.
 
 */
 
uint32_t htol32(uint32_t h);
 

	
 
/**
 
 * Converts a 16-bit integer from little-endian byte order to host byte order.
 
 *
 
 * Use this function on variable values instead of the
 
 * macro LTOH16(). This saves code size.
 
 *
 
 * \param[in] l A 16-bit integer in little-endian byte order.
 
 * \returns The given 16-bit integer converted to host byte order.
 
 */
 
uint16_t ltoh16(uint16_t l);
 

	
 
/**
 
 * Converts a 32-bit integer from little-endian byte order to host byte order.
 
 *
 
 * Use this function on variable values instead of the
 
 * macro LTOH32(). This saves code size.
 
 *
 
 * \param[in] l A 32-bit integer in little-endian byte order.
 
 * \returns The given 32-bit integer converted to host byte order.
 
 */
 
uint32_t ltoh32(uint32_t l);
 

	
 
#elif SWAP_NEEDED
 

	
 
#define htol16(h) swap16(h)
 
#define htol32(h) swap32(h)
 
#define ltoh16(l) swap16(l)
 
#define ltoh32(l) swap32(l)
 

	
 
#else
 

	
 
#define htol16(h) (h)
 
#define htol32(h) (h)
 
#define ltoh16(l) (l)
 
#define ltoh32(l) (l)
 

	
 
#endif
 

	
 
uint16_t read16(const uint8_t* p);
 
uint32_t read32(const uint8_t* p);
 
void write16(uint8_t* p, uint16_t i);
 
void write32(uint8_t* p, uint32_t i);
 

	
 
/**
 
 * @}
 
 */
 

	
 
#if SWAP_NEEDED
 
uint16_t swap16(uint16_t i);
 
uint32_t swap32(uint32_t i);
 
#endif
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
master/master/lib/sd/doc/Thumbs.db
Show inline comments
 
new file 100644
 
binary diff not shown
master/master/lib/sd/fat.c
Show inline comments
 
new file 100644
 

	
 
/* 
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#include "byteordering.h"
 
#include "partition.h"
 
#include "fat.h"
 
#include "fat_config.h"
 
#include "sd-reader_config.h"
 

	
 
#include <string.h>
 

	
 
#if USE_DYNAMIC_MEMORY
 
    #include <stdlib.h>
 
#endif
 

	
 
/**
 
 * \addtogroup fat FAT support
 
 *
 
 * This module implements FAT16/FAT32 read and write access.
 
 * 
 
 * The following features are supported:
 
 * - File names up to 31 characters long.
 
 * - Unlimited depth of subdirectories.
 
 * - Short 8.3 and long filenames.
 
 * - Creating and deleting files.
 
 * - Reading and writing from and to files.
 
 * - File resizing.
 
 * - File sizes of up to 4 gigabytes.
 
 * 
 
 * @{
 
 */
 
/**
 
 * \file
 
 * FAT implementation (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \author Roland Riegel
 
 */
 

	
 
/**
 
 * \addtogroup fat_config FAT configuration
 
 * Preprocessor defines to configure the FAT implementation.
 
 */
 

	
 
/**
 
 * \addtogroup fat_fs FAT access
 
 * Basic functions for handling a FAT filesystem.
 
 */
 

	
 
/**
 
 * \addtogroup fat_file FAT file functions
 
 * Functions for managing files.
 
 */
 

	
 
/**
 
 * \addtogroup fat_dir FAT directory functions
 
 * Functions for managing directories.
 
 */
 

	
 
/**
 
 * @}
 
 */
 

	
 
#define FAT16_CLUSTER_FREE 0x0000
 
#define FAT16_CLUSTER_RESERVED_MIN 0xfff0
 
#define FAT16_CLUSTER_RESERVED_MAX 0xfff6
 
#define FAT16_CLUSTER_BAD 0xfff7
 
#define FAT16_CLUSTER_LAST_MIN 0xfff8
 
#define FAT16_CLUSTER_LAST_MAX 0xffff
 

	
 
#define FAT32_CLUSTER_FREE 0x00000000
 
#define FAT32_CLUSTER_RESERVED_MIN 0x0ffffff0
 
#define FAT32_CLUSTER_RESERVED_MAX 0x0ffffff6
 
#define FAT32_CLUSTER_BAD 0x0ffffff7
 
#define FAT32_CLUSTER_LAST_MIN 0x0ffffff8
 
#define FAT32_CLUSTER_LAST_MAX 0x0fffffff
 

	
 
#define FAT_DIRENTRY_DELETED 0xe5
 
#define FAT_DIRENTRY_LFNLAST (1 << 6)
 
#define FAT_DIRENTRY_LFNSEQMASK ((1 << 6) - 1)
 

	
 
/* Each entry within the directory table has a size of 32 bytes
 
 * and either contains a 8.3 DOS-style file name or a part of a
 
 * long file name, which may consist of several directory table
 
 * entries at once.
 
 *
 
 * multi-byte integer values are stored little-endian!
 
 *
 
 * 8.3 file name entry:
 
 * ====================
 
 * offset  length  description
 
 *      0       8  name (space padded)
 
 *      8       3  extension (space padded)
 
 *     11       1  attributes (FAT_ATTRIB_*)
 
 *
 
 * long file name (lfn) entry ordering for a single file name:
 
 * ===========================================================
 
 * LFN entry n
 
 *     ...
 
 * LFN entry 2
 
 * LFN entry 1
 
 * 8.3 entry (see above)
 
 * 
 
 * lfn entry:
 
 * ==========
 
 * offset  length  description
 
 *      0       1  ordinal field
 
 *      1       2  unicode character 1
 
 *      3       3  unicode character 2
 
 *      5       3  unicode character 3
 
 *      7       3  unicode character 4
 
 *      9       3  unicode character 5
 
 *     11       1  attribute (always 0x0f)
 
 *     12       1  type (reserved, always 0)
 
 *     13       1  checksum
 
 *     14       2  unicode character 6
 
 *     16       2  unicode character 7
 
 *     18       2  unicode character 8
 
 *     20       2  unicode character 9
 
 *     22       2  unicode character 10
 
 *     24       2  unicode character 11
 
 *     26       2  cluster (unused, always 0)
 
 *     28       2  unicode character 12
 
 *     30       2  unicode character 13
 
 * 
 
 * The ordinal field contains a descending number, from n to 1.
 
 * For the n'th lfn entry the ordinal field is or'ed with 0x40.
 
 * For deleted lfn entries, the ordinal field is set to 0xe5.
 
 */
 

	
 
struct fat_header_struct
 
{
 
    offset_t size;
 

	
 
    offset_t fat_offset;
 
    uint32_t fat_size;
 

	
 
    uint16_t sector_size;
 
    uint16_t cluster_size;
 

	
 
    offset_t cluster_zero_offset;
 

	
 
    offset_t root_dir_offset;
 
#if FAT_FAT32_SUPPORT
 
    cluster_t root_dir_cluster;
 
#endif
 
};
 

	
 
struct fat_fs_struct
 
{
 
    struct partition_struct* partition;
 
    struct fat_header_struct header;
 
    cluster_t cluster_free;
 
};
 

	
 
struct fat_file_struct
 
{
 
    struct fat_fs_struct* fs;
 
    struct fat_dir_entry_struct dir_entry;
 
    offset_t pos;
 
    cluster_t pos_cluster;
 
};
 

	
 
struct fat_dir_struct
 
{
 
    struct fat_fs_struct* fs;
 
    struct fat_dir_entry_struct dir_entry;
 
    cluster_t entry_cluster;
 
    uint16_t entry_offset;
 
};
 

	
 
struct fat_read_dir_callback_arg
 
{
 
    struct fat_dir_entry_struct* dir_entry;
 
    uintptr_t bytes_read;
 
#if FAT_LFN_SUPPORT
 
    uint8_t checksum;
 
#endif
 
    uint8_t finished;
 
};
 

	
 
struct fat_usage_count_callback_arg
 
{
 
    cluster_t cluster_count;
 
    uintptr_t buffer_size;
 
};
 

	
 
#if !USE_DYNAMIC_MEMORY
 
static struct fat_fs_struct fat_fs_handles[FAT_FS_COUNT];
 
static struct fat_file_struct fat_file_handles[FAT_FILE_COUNT];
 
static struct fat_dir_struct fat_dir_handles[FAT_DIR_COUNT];
 
#endif
 

	
 
static uint8_t fat_read_header(struct fat_fs_struct* fs);
 
static cluster_t fat_get_next_cluster(const struct fat_fs_struct* fs, cluster_t cluster_num);
 
static offset_t fat_cluster_offset(const struct fat_fs_struct* fs, cluster_t cluster_num);
 
static uint8_t fat_dir_entry_read_callback(uint8_t* buffer, offset_t offset, void* p);
 
#if FAT_LFN_SUPPORT
 
static uint8_t fat_calc_83_checksum(const uint8_t* file_name_83);
 
#endif
 

	
 
static uint8_t fat_get_fs_free_16_callback(uint8_t* buffer, offset_t offset, void* p);
 
#if FAT_FAT32_SUPPORT
 
static uint8_t fat_get_fs_free_32_callback(uint8_t* buffer, offset_t offset, void* p);
 
#endif
 

	
 
#if FAT_WRITE_SUPPORT
 
static cluster_t fat_append_clusters(struct fat_fs_struct* fs, cluster_t cluster_num, cluster_t count);
 
static uint8_t fat_free_clusters(struct fat_fs_struct* fs, cluster_t cluster_num);
 
static uint8_t fat_terminate_clusters(struct fat_fs_struct* fs, cluster_t cluster_num);
 
static uint8_t fat_clear_cluster(const struct fat_fs_struct* fs, cluster_t cluster_num);
 
static uintptr_t fat_clear_cluster_callback(uint8_t* buffer, offset_t offset, void* p);
 
static offset_t fat_find_offset_for_dir_entry(struct fat_fs_struct* fs, const struct fat_dir_struct* parent, const struct fat_dir_entry_struct* dir_entry);
 
static uint8_t fat_write_dir_entry(const struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry);
 
#if FAT_DATETIME_SUPPORT
 
static void fat_set_file_modification_date(struct fat_dir_entry_struct* dir_entry, uint16_t year, uint8_t month, uint8_t day);
 
static void fat_set_file_modification_time(struct fat_dir_entry_struct* dir_entry, uint8_t hour, uint8_t min, uint8_t sec);
 
#endif
 
#endif
 

	
 
/**
 
 * \ingroup fat_fs
 
 * Opens a FAT filesystem.
 
 *
 
 * \param[in] partition Discriptor of partition on which the filesystem resides.
 
 * \returns 0 on error, a FAT filesystem descriptor on success.
 
 * \see fat_close
 
 */
 
struct fat_fs_struct* fat_open(struct partition_struct* partition)
 
{
 
    if(!partition ||
 
#if FAT_WRITE_SUPPORT
 
       !partition->device_write ||
 
       !partition->device_write_interval
 
#else
 
       0
 
#endif
 
      )
 
        return 0;
 

	
 
#if USE_DYNAMIC_MEMORY
 
    struct fat_fs_struct* fs = malloc(sizeof(*fs));
 
    if(!fs)
 
        return 0;
 
#else
 
    struct fat_fs_struct* fs = fat_fs_handles;
 
    uint8_t i;
 
    for(i = 0; i < FAT_FS_COUNT; ++i)
 
    {
 
        if(!fs->partition)
 
            break;
 

	
 
        ++fs;
 
    }
 
    if(i >= FAT_FS_COUNT)
 
        return 0;
 
#endif
 

	
 
    memset(fs, 0, sizeof(*fs));
 

	
 
    fs->partition = partition;
 
    if(!fat_read_header(fs))
 
    {
 
#if USE_DYNAMIC_MEMORY
 
        free(fs);
 
#else
 
        fs->partition = 0;
 
#endif
 
        return 0;
 
    }
 
    
 
    return fs;
 
}
 

	
 
/**
 
 * \ingroup fat_fs
 
 * Closes a FAT filesystem.
 
 *
 
 * When this function returns, the given filesystem descriptor
 
 * will be invalid.
 
 *
 
 * \param[in] fs The filesystem to close.
 
 * \see fat_open
 
 */
 
void fat_close(struct fat_fs_struct* fs)
 
{
 
    if(!fs)
 
        return;
 

	
 
#if USE_DYNAMIC_MEMORY
 
    free(fs);
 
#else
 
    fs->partition = 0;
 
#endif
 
}
 

	
 
/**
 
 * \ingroup fat_fs
 
 * Reads and parses the header of a FAT filesystem.
 
 *
 
 * \param[in,out] fs The filesystem for which to parse the header.
 
 * \returns 0 on failure, 1 on success.
 
 */
 
uint8_t fat_read_header(struct fat_fs_struct* fs)
 
{
 
    if(!fs)
 
        return 0;
 

	
 
    struct partition_struct* partition = fs->partition;
 
    if(!partition)
 
        return 0;
 

	
 
    /* read fat parameters */
 
#if FAT_FAT32_SUPPORT
 
    uint8_t buffer[37];
 
#else
 
    uint8_t buffer[25];
 
#endif
 
    offset_t partition_offset = (offset_t) partition->offset * 512;
 
    if(!partition->device_read(partition_offset + 0x0b, buffer, sizeof(buffer)))
 
        return 0;
 

	
 
    uint16_t bytes_per_sector = read16(&buffer[0x00]);
 
    uint16_t reserved_sectors = read16(&buffer[0x03]);
 
    uint8_t sectors_per_cluster = buffer[0x02];
 
    uint8_t fat_copies = buffer[0x05];
 
    uint16_t max_root_entries = read16(&buffer[0x06]);
 
    uint16_t sector_count_16 = read16(&buffer[0x08]);
 
    uint16_t sectors_per_fat = read16(&buffer[0x0b]);
 
    uint32_t sector_count = read32(&buffer[0x15]);
 
#if FAT_FAT32_SUPPORT
 
    uint32_t sectors_per_fat32 = read32(&buffer[0x19]);
 
    uint32_t cluster_root_dir = read32(&buffer[0x21]);
 
#endif
 

	
 
    if(sector_count == 0)
 
    {
 
        if(sector_count_16 == 0)
 
            /* illegal volume size */
 
            return 0;
 
        else
 
            sector_count = sector_count_16;
 
    }
 
#if FAT_FAT32_SUPPORT
 
    if(sectors_per_fat != 0)
 
        sectors_per_fat32 = sectors_per_fat;
 
    else if(sectors_per_fat32 == 0)
 
        /* this is neither FAT16 nor FAT32 */
 
        return 0;
 
#else
 
    if(sectors_per_fat == 0)
 
        /* this is not a FAT16 */
 
        return 0;
 
#endif
 

	
 
    /* determine the type of FAT we have here */
 
    uint32_t data_sector_count = sector_count
 
                                 - reserved_sectors
 
#if FAT_FAT32_SUPPORT
 
                                 - sectors_per_fat32 * fat_copies
 
#else
 
                                 - (uint32_t) sectors_per_fat * fat_copies
 
#endif
 
                                 - ((max_root_entries * 32 + bytes_per_sector - 1) / bytes_per_sector);
 
    uint32_t data_cluster_count = data_sector_count / sectors_per_cluster;
 
    if(data_cluster_count < 4085)
 
        /* this is a FAT12, not supported */
 
        return 0;
 
    else if(data_cluster_count < 65525)
 
        /* this is a FAT16 */
 
        partition->type = PARTITION_TYPE_FAT16;
 
    else
 
        /* this is a FAT32 */
 
        partition->type = PARTITION_TYPE_FAT32;
 

	
 
    /* fill header information */
 
    struct fat_header_struct* header = &fs->header;
 
    memset(header, 0, sizeof(*header));
 
    
 
    header->size = (offset_t) sector_count * bytes_per_sector;
 

	
 
    header->fat_offset = /* jump to partition */
 
                         partition_offset +
 
                         /* jump to fat */
 
                         (offset_t) reserved_sectors * bytes_per_sector;
 
    header->fat_size = (data_cluster_count + 2) * (partition->type == PARTITION_TYPE_FAT16 ? 2 : 4);
 

	
 
    header->sector_size = bytes_per_sector;
 
    header->cluster_size = (uint16_t) bytes_per_sector * sectors_per_cluster;
 

	
 
#if FAT_FAT32_SUPPORT
 
    if(partition->type == PARTITION_TYPE_FAT16)
 
#endif
 
    {
 
        header->root_dir_offset = /* jump to fats */
 
                                  header->fat_offset +
 
                                  /* jump to root directory entries */
 
                                  (offset_t) fat_copies * sectors_per_fat * bytes_per_sector;
 

	
 
        header->cluster_zero_offset = /* jump to root directory entries */
 
                                      header->root_dir_offset +
 
                                      /* skip root directory entries */
 
                                      (offset_t) max_root_entries * 32;
 
    }
 
#if FAT_FAT32_SUPPORT
 
    else
 
    {
 
        header->cluster_zero_offset = /* jump to fats */
 
                                      header->fat_offset +
 
                                      /* skip fats */
 
                                      (offset_t) fat_copies * sectors_per_fat32 * bytes_per_sector;
 

	
 
        header->root_dir_cluster = cluster_root_dir;
 
    }
 
#endif
 

	
 
    return 1;
 
}
 

	
 
/**
 
 * \ingroup fat_fs
 
 * Retrieves the next following cluster of a given cluster.
 
 *
 
 * Using the filesystem file allocation table, this function returns
 
 * the number of the cluster containing the data directly following
 
 * the data within the cluster with the given number.
 
 *
 
 * \param[in] fs The filesystem for which to determine the next cluster.
 
 * \param[in] cluster_num The number of the cluster for which to determine its successor.
 
 * \returns The wanted cluster number, or 0 on error.
 
 */
 
cluster_t fat_get_next_cluster(const struct fat_fs_struct* fs, cluster_t cluster_num)
 
{
 
    if(!fs || cluster_num < 2)
 
        return 0;
 

	
 
#if FAT_FAT32_SUPPORT
 
    if(fs->partition->type == PARTITION_TYPE_FAT32)
 
    {
 
        /* read appropriate fat entry */
 
        uint32_t fat_entry;
 
        if(!fs->partition->device_read(fs->header.fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)))
 
            return 0;
 

	
 
        /* determine next cluster from fat */
 
        cluster_num = ltoh32(fat_entry);
 
        
 
        if(cluster_num == FAT32_CLUSTER_FREE ||
 
           cluster_num == FAT32_CLUSTER_BAD ||
 
           (cluster_num >= FAT32_CLUSTER_RESERVED_MIN && cluster_num <= FAT32_CLUSTER_RESERVED_MAX) ||
 
           (cluster_num >= FAT32_CLUSTER_LAST_MIN && cluster_num <= FAT32_CLUSTER_LAST_MAX))
 
            return 0;
 
    }
 
    else
 
#endif
 
    {
 
        /* read appropriate fat entry */
 
        uint16_t fat_entry;
 
        if(!fs->partition->device_read(fs->header.fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)))
 
            return 0;
 

	
 
        /* determine next cluster from fat */
 
        cluster_num = ltoh16(fat_entry);
 
        
 
        if(cluster_num == FAT16_CLUSTER_FREE ||
 
           cluster_num == FAT16_CLUSTER_BAD ||
 
           (cluster_num >= FAT16_CLUSTER_RESERVED_MIN && cluster_num <= FAT16_CLUSTER_RESERVED_MAX) ||
 
           (cluster_num >= FAT16_CLUSTER_LAST_MIN && cluster_num <= FAT16_CLUSTER_LAST_MAX))
 
            return 0;
 
    }
 

	
 
    return cluster_num;
 
}
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_fs
 
 * Appends a new cluster chain to an existing one.
 
 *
 
 * Set cluster_num to zero to create a completely new one.
 
 *
 
 * \param[in] fs The file system on which to operate.
 
 * \param[in] cluster_num The cluster to which to append the new chain.
 
 * \param[in] count The number of clusters to allocate.
 
 * \returns 0 on failure, the number of the first new cluster on success.
 
 */
 
cluster_t fat_append_clusters(struct fat_fs_struct* fs, cluster_t cluster_num, cluster_t count)
 
{
 
    if(!fs)
 
        return 0;
 

	
 
    device_read_t device_read = fs->partition->device_read;
 
    device_write_t device_write = fs->partition->device_write;
 
    offset_t fat_offset = fs->header.fat_offset;
 
    cluster_t count_left = count;
 
    cluster_t cluster_current = fs->cluster_free;
 
    cluster_t cluster_next = 0;
 
    cluster_t cluster_count;
 
    uint16_t fat_entry16;
 
#if FAT_FAT32_SUPPORT
 
    uint32_t fat_entry32;
 
    uint8_t is_fat32 = (fs->partition->type == PARTITION_TYPE_FAT32);
 

	
 
    if(is_fat32)
 
        cluster_count = fs->header.fat_size / sizeof(fat_entry32);
 
    else
 
#endif
 
        cluster_count = fs->header.fat_size / sizeof(fat_entry16);
 

	
 
    fs->cluster_free = 0;
 
    for(cluster_t cluster_left = cluster_count; cluster_left > 0; --cluster_left, ++cluster_current)
 
    {
 
        if(cluster_current < 2 || cluster_current >= cluster_count)
 
            cluster_current = 2;
 

	
 
#if FAT_FAT32_SUPPORT
 
        if(is_fat32)
 
        {
 
            if(!device_read(fat_offset + (offset_t) cluster_current * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32)))
 
                return 0;
 
        }
 
        else
 
#endif
 
        {
 
            if(!device_read(fat_offset + (offset_t) cluster_current * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16)))
 
                return 0;
 
        }
 

	
 
#if FAT_FAT32_SUPPORT
 
        if(is_fat32)
 
        {
 
            /* check if this is a free cluster */
 
            if(fat_entry32 != HTOL32(FAT32_CLUSTER_FREE))
 
                continue;
 

	
 
            /* If we don't need this free cluster for the
 
             * current allocation, we keep it in mind for
 
             * the next time.
 
             */
 
            if(count_left == 0)
 
            {
 
                fs->cluster_free = cluster_current;
 
                break;
 
            }
 

	
 
            /* allocate cluster */
 
            if(cluster_next == 0)
 
                fat_entry32 = HTOL32(FAT32_CLUSTER_LAST_MAX);
 
            else
 
                fat_entry32 = htol32(cluster_next);
 

	
 
            if(!device_write(fat_offset + (offset_t) cluster_current * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32)))
 
                break;
 
        }
 
        else
 
#endif
 
        {
 
            /* check if this is a free cluster */
 
            if(fat_entry16 != HTOL16(FAT16_CLUSTER_FREE))
 
                continue;
 

	
 
            /* If we don't need this free cluster for the
 
             * current allocation, we keep it in mind for
 
             * the next time.
 
             */
 
            if(count_left == 0)
 
            {
 
                fs->cluster_free = cluster_current;
 
                break;
 
            }
 

	
 
            /* allocate cluster */
 
            if(cluster_next == 0)
 
                fat_entry16 = HTOL16(FAT16_CLUSTER_LAST_MAX);
 
            else
 
                fat_entry16 = htol16((uint16_t) cluster_next);
 

	
 
            if(!device_write(fat_offset + (offset_t) cluster_current * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16)))
 
                break;
 
        }
 

	
 
        cluster_next = cluster_current;
 
        --count_left;
 
    }
 

	
 
    do
 
    {
 
        if(count_left > 0)
 
            break;
 

	
 
        /* We allocated a new cluster chain. Now join
 
         * it with the existing one (if any).
 
         */
 
        if(cluster_num >= 2)
 
        {
 
#if FAT_FAT32_SUPPORT
 
            if(is_fat32)
 
            {
 
                fat_entry32 = htol32(cluster_next);
 

	
 
                if(!device_write(fat_offset + (offset_t) cluster_num * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32)))
 
                    break;
 
            }
 
            else
 
#endif
 
            {
 
                fat_entry16 = htol16((uint16_t) cluster_next);
 

	
 
                if(!device_write(fat_offset + (offset_t) cluster_num * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16)))
 
                    break;
 
            }
 
        }
 

	
 
        return cluster_next;
 

	
 
    } while(0);
 

	
 
    /* No space left on device or writing error.
 
     * Free up all clusters already allocated.
 
     */
 
    fat_free_clusters(fs, cluster_next);
 

	
 
    return 0;
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_fs
 
 * Frees a cluster chain, or a part thereof.
 
 *
 
 * Marks the specified cluster and all clusters which are sequentially
 
 * referenced by it as free. They may then be used again for future
 
 * file allocations.
 
 *
 
 * \note If this function is used for freeing just a part of a cluster
 
 *       chain, the new end of the chain is not correctly terminated
 
 *       within the FAT. Use fat_terminate_clusters() instead.
 
 *
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in] cluster_num The starting cluster of the chain which to free.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_terminate_clusters
 
 */
 
uint8_t fat_free_clusters(struct fat_fs_struct* fs, cluster_t cluster_num)
 
{
 
    if(!fs || cluster_num < 2)
 
        return 0;
 

	
 
    offset_t fat_offset = fs->header.fat_offset;
 
#if FAT_FAT32_SUPPORT
 
    if(fs->partition->type == PARTITION_TYPE_FAT32)
 
    {
 
        uint32_t fat_entry;
 
        while(cluster_num)
 
        {
 
            if(!fs->partition->device_read(fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)))
 
                return 0;
 

	
 
            /* get next cluster of current cluster before freeing current cluster */
 
            uint32_t cluster_num_next = ltoh32(fat_entry);
 

	
 
            if(cluster_num_next == FAT32_CLUSTER_FREE)
 
                return 1;
 
            if(cluster_num_next == FAT32_CLUSTER_BAD ||
 
               (cluster_num_next >= FAT32_CLUSTER_RESERVED_MIN &&
 
                cluster_num_next <= FAT32_CLUSTER_RESERVED_MAX
 
               )
 
              )
 
                return 0;
 
            if(cluster_num_next >= FAT32_CLUSTER_LAST_MIN && cluster_num_next <= FAT32_CLUSTER_LAST_MAX)
 
                cluster_num_next = 0;
 

	
 
            /* We know we will free the cluster, so remember it as
 
             * free for the next allocation.
 
             */
 
            if(!fs->cluster_free)
 
                fs->cluster_free = cluster_num;
 

	
 
            /* free cluster */
 
            fat_entry = HTOL32(FAT32_CLUSTER_FREE);
 
            fs->partition->device_write(fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry));
 

	
 
            /* We continue in any case here, even if freeing the cluster failed.
 
             * The cluster is lost, but maybe we can still free up some later ones.
 
             */
 

	
 
            cluster_num = cluster_num_next;
 
        }
 
    }
 
    else
 
#endif
 
    {
 
        uint16_t fat_entry;
 
        while(cluster_num)
 
        {
 
            if(!fs->partition->device_read(fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)))
 
                return 0;
 

	
 
            /* get next cluster of current cluster before freeing current cluster */
 
            uint16_t cluster_num_next = ltoh16(fat_entry);
 

	
 
            if(cluster_num_next == FAT16_CLUSTER_FREE)
 
                return 1;
 
            if(cluster_num_next == FAT16_CLUSTER_BAD ||
 
               (cluster_num_next >= FAT16_CLUSTER_RESERVED_MIN &&
 
                cluster_num_next <= FAT16_CLUSTER_RESERVED_MAX
 
               )
 
              )
 
                return 0;
 
            if(cluster_num_next >= FAT16_CLUSTER_LAST_MIN && cluster_num_next <= FAT16_CLUSTER_LAST_MAX)
 
                cluster_num_next = 0;
 

	
 
            /* free cluster */
 
            fat_entry = HTOL16(FAT16_CLUSTER_FREE);
 
            fs->partition->device_write(fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry));
 

	
 
            /* We continue in any case here, even if freeing the cluster failed.
 
             * The cluster is lost, but maybe we can still free up some later ones.
 
             */
 

	
 
            cluster_num = cluster_num_next;
 
        }
 
    }
 

	
 
    return 1;
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_fs
 
 * Frees a part of a cluster chain and correctly terminates the rest.
 
 *
 
 * Marks the specified cluster as the new end of a cluster chain and
 
 * frees all following clusters.
 
 *
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in] cluster_num The new end of the cluster chain.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_free_clusters
 
 */
 
uint8_t fat_terminate_clusters(struct fat_fs_struct* fs, cluster_t cluster_num)
 
{
 
    if(!fs || cluster_num < 2)
 
        return 0;
 

	
 
    /* fetch next cluster before overwriting the cluster entry */
 
    cluster_t cluster_num_next = fat_get_next_cluster(fs, cluster_num);
 

	
 
    /* mark cluster as the last one */
 
#if FAT_FAT32_SUPPORT
 
    if(fs->partition->type == PARTITION_TYPE_FAT32)
 
    {
 
        uint32_t fat_entry = HTOL32(FAT32_CLUSTER_LAST_MAX);
 
        if(!fs->partition->device_write(fs->header.fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)))
 
            return 0;
 
    }
 
    else
 
#endif
 
    {
 
        uint16_t fat_entry = HTOL16(FAT16_CLUSTER_LAST_MAX);
 
        if(!fs->partition->device_write(fs->header.fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)))
 
            return 0;
 
    }
 

	
 
    /* free remaining clusters */
 
    if(cluster_num_next)
 
        return fat_free_clusters(fs, cluster_num_next);
 
    else
 
        return 1;
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_fs
 
 * Clears a single cluster.
 
 *
 
 * The complete cluster is filled with zeros.
 
 *
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in] cluster_num The cluster to clear.
 
 * \returns 0 on failure, 1 on success.
 
 */
 
uint8_t fat_clear_cluster(const struct fat_fs_struct* fs, cluster_t cluster_num)
 
{
 
    if(cluster_num < 2)
 
        return 0;
 

	
 
    offset_t cluster_offset = fat_cluster_offset(fs, cluster_num);
 

	
 
    uint8_t zero[16];
 
    memset(zero, 0, sizeof(zero));
 
    return fs->partition->device_write_interval(cluster_offset,
 
                                                zero,
 
                                                fs->header.cluster_size,
 
                                                fat_clear_cluster_callback,
 
                                                0
 
                                               );
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_fs
 
 * Callback function for clearing a cluster.
 
 */
 
uintptr_t fat_clear_cluster_callback(uint8_t* buffer, offset_t offset, void* p)
 
{
 
    return 16;
 
}
 
#endif
 

	
 
/**
 
 * \ingroup fat_fs
 
 * Calculates the offset of the specified cluster.
 
 *
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in] cluster_num The cluster whose offset to calculate.
 
 * \returns The cluster offset.
 
 */
 
offset_t fat_cluster_offset(const struct fat_fs_struct* fs, cluster_t cluster_num)
 
{
 
    if(!fs || cluster_num < 2)
 
        return 0;
 

	
 
    return fs->header.cluster_zero_offset + (offset_t) (cluster_num - 2) * fs->header.cluster_size;
 
}
 

	
 
/**
 
 * \ingroup fat_file
 
 * Retrieves the directory entry of a path.
 
 *
 
 * The given path may both describe a file or a directory.
 
 *
 
 * \param[in] fs The FAT filesystem on which to search.
 
 * \param[in] path The path of which to read the directory entry.
 
 * \param[out] dir_entry The directory entry to fill.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_read_dir
 
 */
 
uint8_t fat_get_dir_entry_of_path(struct fat_fs_struct* fs, const char* path, struct fat_dir_entry_struct* dir_entry)
 
{
 
    if(!fs || !path || path[0] == '\0' || !dir_entry)
 
        return 0;
 

	
 
    if(path[0] == '/')
 
        ++path;
 

	
 
    /* begin with the root directory */
 
    memset(dir_entry, 0, sizeof(*dir_entry));
 
    dir_entry->attributes = FAT_ATTRIB_DIR;
 

	
 
    while(1)
 
    {
 
        if(path[0] == '\0')
 
            return 1;
 

	
 
        struct fat_dir_struct* dd = fat_open_dir(fs, dir_entry);
 
        if(!dd)
 
            break;
 

	
 
        /* extract the next hierarchy we will search for */
 
        const char* sub_path = strchr(path, '/');
 
        uint8_t length_to_sep;
 
        if(sub_path)
 
        {
 
            length_to_sep = sub_path - path;
 
            ++sub_path;
 
        }
 
        else
 
        {
 
            length_to_sep = strlen(path);
 
            sub_path = path + length_to_sep;
 
        }
 
        
 
        /* read directory entries */
 
        while(fat_read_dir(dd, dir_entry))
 
        {
 
            /* check if we have found the next hierarchy */
 
            if((strlen(dir_entry->long_name) != length_to_sep ||
 
                strncmp(path, dir_entry->long_name, length_to_sep) != 0))
 
                continue;
 

	
 
            fat_close_dir(dd);
 
            dd = 0;
 

	
 
            if(path[length_to_sep] == '\0')
 
                /* we iterated through the whole path and have found the file */
 
                return 1;
 

	
 
            if(dir_entry->attributes & FAT_ATTRIB_DIR)
 
            {
 
                /* we found a parent directory of the file we are searching for */
 
                path = sub_path;
 
                break;
 
            }
 

	
 
            /* a parent of the file exists, but not the file itself */
 
            return 0;
 
        }
 

	
 
        fat_close_dir(dd);
 
    }
 
    
 
    return 0;
 
}
 

	
 
/**
 
 * \ingroup fat_file
 
 * Opens a file on a FAT filesystem.
 
 *
 
 * \param[in] fs The filesystem on which the file to open lies.
 
 * \param[in] dir_entry The directory entry of the file to open.
 
 * \returns The file handle, or 0 on failure.
 
 * \see fat_close_file
 
 */
 
struct fat_file_struct* fat_open_file(struct fat_fs_struct* fs, const struct fat_dir_entry_struct* dir_entry)
 
{
 
    if(!fs || !dir_entry || (dir_entry->attributes & FAT_ATTRIB_DIR))
 
        return 0;
 

	
 
#if USE_DYNAMIC_MEMORY
 
    struct fat_file_struct* fd = malloc(sizeof(*fd));
 
    if(!fd)
 
        return 0;
 
#else
 
    struct fat_file_struct* fd = fat_file_handles;
 
    uint8_t i;
 
    for(i = 0; i < FAT_FILE_COUNT; ++i)
 
    {
 
        if(!fd->fs)
 
            break;
 

	
 
        ++fd;
 
    }
 
    if(i >= FAT_FILE_COUNT)
 
        return 0;
 
#endif
 
    
 
    memcpy(&fd->dir_entry, dir_entry, sizeof(*dir_entry));
 
    fd->fs = fs;
 
    fd->pos = 0;
 
    fd->pos_cluster = dir_entry->cluster;
 

	
 
    return fd;
 
}
 

	
 
/**
 
 * \ingroup fat_file
 
 * Closes a file.
 
 *
 
 * \param[in] fd The file handle of the file to close.
 
 * \see fat_open_file
 
 */
 
void fat_close_file(struct fat_file_struct* fd)
 
{
 
    if(fd)
 
    {
 
#if FAT_DELAY_DIRENTRY_UPDATE
 
        /* write directory entry */
 
        fat_write_dir_entry(fd->fs, &fd->dir_entry);
 
#endif
 

	
 
#if USE_DYNAMIC_MEMORY
 
        free(fd);
 
#else
 
        fd->fs = 0;
 
#endif
 
    }
 
}
 

	
 
/**
 
 * \ingroup fat_file
 
 * Reads data from a file.
 
 * 
 
 * The data requested is read from the current file location.
 
 *
 
 * \param[in] fd The file handle of the file from which to read.
 
 * \param[out] buffer The buffer into which to write.
 
 * \param[in] buffer_len The amount of data to read.
 
 * \returns The number of bytes read, 0 on end of file, or -1 on failure.
 
 * \see fat_write_file
 
 */
 
intptr_t fat_read_file(struct fat_file_struct* fd, uint8_t* buffer, uintptr_t buffer_len)
 
{
 
    /* check arguments */
 
    if(!fd || !buffer || buffer_len < 1)
 
        return -1;
 

	
 
    /* determine number of bytes to read */
 
    if(fd->pos + buffer_len > fd->dir_entry.file_size)
 
        buffer_len = fd->dir_entry.file_size - fd->pos;
 
    if(buffer_len == 0)
 
        return 0;
 
    
 
    uint16_t cluster_size = fd->fs->header.cluster_size;
 
    cluster_t cluster_num = fd->pos_cluster;
 
    uintptr_t buffer_left = buffer_len;
 
    uint16_t first_cluster_offset = (uint16_t) (fd->pos & (cluster_size - 1));
 

	
 
    /* find cluster in which to start reading */
 
    if(!cluster_num)
 
    {
 
        cluster_num = fd->dir_entry.cluster;
 
        
 
        if(!cluster_num)
 
        {
 
            if(!fd->pos)
 
                return 0;
 
            else
 
                return -1;
 
        }
 

	
 
        if(fd->pos)
 
        {
 
            uint32_t pos = fd->pos;
 
            while(pos >= cluster_size)
 
            {
 
                pos -= cluster_size;
 
                cluster_num = fat_get_next_cluster(fd->fs, cluster_num);
 
                if(!cluster_num)
 
                    return -1;
 
            }
 
        }
 
    }
 
    
 
    /* read data */
 
    do
 
    {
 
        /* calculate data size to copy from cluster */
 
        offset_t cluster_offset = fat_cluster_offset(fd->fs, cluster_num) + first_cluster_offset;
 
        uint16_t copy_length = cluster_size - first_cluster_offset;
 
        if(copy_length > buffer_left)
 
            copy_length = buffer_left;
 

	
 
        /* read data */
 
        if(!fd->fs->partition->device_read(cluster_offset, buffer, copy_length))
 
            return buffer_len - buffer_left;
 

	
 
        /* calculate new file position */
 
        buffer += copy_length;
 
        buffer_left -= copy_length;
 
        fd->pos += copy_length;
 

	
 
        if(first_cluster_offset + copy_length >= cluster_size)
 
        {
 
            /* we are on a cluster boundary, so get the next cluster */
 
            if((cluster_num = fat_get_next_cluster(fd->fs, cluster_num)))
 
            {
 
                first_cluster_offset = 0;
 
            }
 
            else
 
            {
 
                fd->pos_cluster = 0;
 
                return buffer_len - buffer_left;
 
            }
 
        }
 

	
 
        fd->pos_cluster = cluster_num;
 

	
 
    } while(buffer_left > 0); /* check if we are done */
 

	
 
    return buffer_len;
 
}
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_file
 
 * Writes data to a file.
 
 * 
 
 * The data is written to the current file location.
 
 *
 
 * \param[in] fd The file handle of the file to which to write.
 
 * \param[in] buffer The buffer from which to read the data to be written.
 
 * \param[in] buffer_len The amount of data to write.
 
 * \returns The number of bytes written (0 or something less than \c buffer_len on disk full) or -1 on failure.
 
 * \see fat_read_file
 
 */
 
intptr_t fat_write_file(struct fat_file_struct* fd, const uint8_t* buffer, uintptr_t buffer_len)
 
{
 
    /* check arguments */
 
    if(!fd || !buffer || buffer_len < 1)
 
        return -1;
 
    if(fd->pos > fd->dir_entry.file_size)
 
        return -1;
 

	
 
    uint16_t cluster_size = fd->fs->header.cluster_size;
 
    cluster_t cluster_num = fd->pos_cluster;
 
    uintptr_t buffer_left = buffer_len;
 
    uint16_t first_cluster_offset = (uint16_t) (fd->pos & (cluster_size - 1));
 

	
 
    /* find cluster in which to start writing */
 
    if(!cluster_num)
 
    {
 
        cluster_num = fd->dir_entry.cluster;
 
        
 
        if(!cluster_num)
 
        {
 
            if(!fd->pos)
 
            {
 
                /* empty file */
 
                fd->dir_entry.cluster = cluster_num = fat_append_clusters(fd->fs, 0, 1);
 
                if(!cluster_num)
 
                    return 0;
 
            }
 
            else
 
            {
 
                return -1;
 
            }
 
        }
 

	
 
        if(fd->pos)
 
        {
 
            uint32_t pos = fd->pos;
 
            cluster_t cluster_num_next;
 
            while(pos >= cluster_size)
 
            {
 
                pos -= cluster_size;
 
                cluster_num_next = fat_get_next_cluster(fd->fs, cluster_num);
 
                if(!cluster_num_next)
 
                {
 
                    if(pos != 0)
 
                        return -1; /* current file position points beyond end of file */
 

	
 
                    /* the file exactly ends on a cluster boundary, and we append to it */
 
                    cluster_num_next = fat_append_clusters(fd->fs, cluster_num, 1);
 
                    if(!cluster_num_next)
 
                        return 0;
 
                }
 

	
 
                cluster_num = cluster_num_next;
 
            }
 
        }
 
    }
 
    
 
    /* write data */
 
    do
 
    {
 
        /* calculate data size to write to cluster */
 
        offset_t cluster_offset = fat_cluster_offset(fd->fs, cluster_num) + first_cluster_offset;
 
        uint16_t write_length = cluster_size - first_cluster_offset;
 
        if(write_length > buffer_left)
 
            write_length = buffer_left;
 

	
 
        /* write data which fits into the current cluster */
 
        if(!fd->fs->partition->device_write(cluster_offset, buffer, write_length))
 
            break;
 

	
 
        /* calculate new file position */
 
        buffer += write_length;
 
        buffer_left -= write_length;
 
        fd->pos += write_length;
 

	
 
        if(first_cluster_offset + write_length >= cluster_size)
 
        {
 
            /* we are on a cluster boundary, so get the next cluster */
 
            cluster_t cluster_num_next = fat_get_next_cluster(fd->fs, cluster_num);
 
            if(!cluster_num_next && buffer_left > 0)
 
                /* we reached the last cluster, append a new one */
 
                cluster_num_next = fat_append_clusters(fd->fs, cluster_num, 1);
 
            if(!cluster_num_next)
 
            {
 
                fd->pos_cluster = 0;
 
                break;
 
            }
 

	
 
            cluster_num = cluster_num_next;
 
            first_cluster_offset = 0;
 
        }
 

	
 
        fd->pos_cluster = cluster_num;
 

	
 
    } while(buffer_left > 0); /* check if we are done */
 

	
 
    /* update directory entry */
 
    if(fd->pos > fd->dir_entry.file_size)
 
    {
 
#if !FAT_DELAY_DIRENTRY_UPDATE
 
        uint32_t size_old = fd->dir_entry.file_size;
 
#endif
 

	
 
        /* update file size */
 
        fd->dir_entry.file_size = fd->pos;
 

	
 
#if !FAT_DELAY_DIRENTRY_UPDATE
 
        /* write directory entry */
 
        if(!fat_write_dir_entry(fd->fs, &fd->dir_entry))
 
        {
 
            /* We do not return an error here since we actually wrote
 
             * some data to disk. So we calculate the amount of data
 
             * we wrote to disk and which lies within the old file size.
 
             */
 
            buffer_left = fd->pos - size_old;
 
            fd->pos = size_old;
 
        }
 
#endif
 
    }
 

	
 
    return buffer_len - buffer_left;
 
}
 
#endif
 

	
 
/**
 
 * \ingroup fat_file
 
 * Repositions the read/write file offset.
 
 *
 
 * Changes the file offset where the next call to fat_read_file()
 
 * or fat_write_file() starts reading/writing.
 
 *
 
 * If the new offset is beyond the end of the file, fat_resize_file()
 
 * is implicitly called, i.e. the file is expanded.
 
 *
 
 * The new offset can be given in different ways determined by
 
 * the \c whence parameter:
 
 * - \b FAT_SEEK_SET: \c *offset is relative to the beginning of the file.
 
 * - \b FAT_SEEK_CUR: \c *offset is relative to the current file position.
 
 * - \b FAT_SEEK_END: \c *offset is relative to the end of the file.
 
 *
 
 * The resulting absolute offset is written to the location the \c offset
 
 * parameter points to.
 
 *
 
 * Calling this function can also be used to retrieve the current file position:
 
   \code
 
   int32_t file_pos = 0;
 
   if(!fat_seek_file(fd, &file_pos, FAT_SEEK_CUR))
 
   {
 
       // error
 
   }
 
   // file_pos now contains the absolute file position
 
   \endcode
 
 * 
 
 * \param[in] fd The file decriptor of the file on which to seek.
 
 * \param[in,out] offset A pointer to the new offset, as affected by the \c whence
 
 *                   parameter. The function writes the new absolute offset
 
 *                   to this location before it returns.
 
 * \param[in] whence Affects the way \c offset is interpreted, see above.
 
 * \returns 0 on failure, 1 on success.
 
 */
 
uint8_t fat_seek_file(struct fat_file_struct* fd, int32_t* offset, uint8_t whence)
 
{
 
    if(!fd || !offset)
 
        return 0;
 

	
 
    uint32_t new_pos = fd->pos;
 
    switch(whence)
 
    {
 
        case FAT_SEEK_SET:
 
            new_pos = *offset;
 
            break;
 
        case FAT_SEEK_CUR:
 
            new_pos += *offset;
 
            break;
 
        case FAT_SEEK_END:
 
            new_pos = fd->dir_entry.file_size + *offset;
 
            break;
 
        default:
 
            return 0;
 
    }
 

	
 
    if(new_pos > fd->dir_entry.file_size
 
#if FAT_WRITE_SUPPORT
 
       && !fat_resize_file(fd, new_pos)
 
#endif
 
       )
 
        return 0;
 

	
 
    fd->pos = new_pos;
 
    fd->pos_cluster = 0;
 

	
 
    *offset = (int32_t) new_pos;
 
    return 1;
 
}
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_file
 
 * Resizes a file to have a specific size.
 
 *
 
 * Enlarges or shrinks the file pointed to by the file descriptor to have
 
 * exactly the specified size.
 
 *
 
 * If the file is truncated, all bytes having an equal or larger offset
 
 * than the given size are lost. If the file is expanded, the additional
 
 * bytes are allocated.
 
 *
 
 * \note Please be aware that this function just allocates or deallocates disk
 
 * space, it does not explicitely clear it. To avoid data leakage, this
 
 * must be done manually.
 
 *
 
 * \param[in] fd The file decriptor of the file which to resize.
 
 * \param[in] size The new size of the file.
 
 * \returns 0 on failure, 1 on success.
 
 */
 
uint8_t fat_resize_file(struct fat_file_struct* fd, uint32_t size)
 
{
 
    if(!fd)
 
        return 0;
 

	
 
    cluster_t cluster_num = fd->dir_entry.cluster;
 
    uint16_t cluster_size = fd->fs->header.cluster_size;
 
    uint32_t size_new = size;
 

	
 
    do
 
    {
 
        if(cluster_num == 0 && size_new == 0)
 
            /* the file stays empty */
 
            break;
 

	
 
        /* seek to the next cluster as long as we need the space */
 
        while(size_new > cluster_size)
 
        {
 
            /* get next cluster of file */
 
            cluster_t cluster_num_next = fat_get_next_cluster(fd->fs, cluster_num);
 
            if(cluster_num_next)
 
            {
 
                cluster_num = cluster_num_next;
 
                size_new -= cluster_size;
 
            }
 
            else
 
            {
 
                break;
 
            }
 
        }
 

	
 
        if(size_new > cluster_size || cluster_num == 0)
 
        {
 
            /* Allocate new cluster chain and append
 
             * it to the existing one, if available.
 
             */
 
            cluster_t cluster_count = (size_new + cluster_size - 1) / cluster_size;
 
            cluster_t cluster_new_chain = fat_append_clusters(fd->fs, cluster_num, cluster_count);
 
            if(!cluster_new_chain)
 
                return 0;
 

	
 
            if(!cluster_num)
 
            {
 
                cluster_num = cluster_new_chain;
 
                fd->dir_entry.cluster = cluster_num;
 
            }
 
        }
 

	
 
        /* write new directory entry */
 
        fd->dir_entry.file_size = size;
 
        if(size == 0)
 
            fd->dir_entry.cluster = 0;
 
        if(!fat_write_dir_entry(fd->fs, &fd->dir_entry))
 
            return 0;
 

	
 
        if(size == 0)
 
        {
 
            /* free all clusters of file */
 
            fat_free_clusters(fd->fs, cluster_num);
 
        }
 
        else if(size_new <= cluster_size)
 
        {
 
            /* free all clusters no longer needed */
 
            fat_terminate_clusters(fd->fs, cluster_num);
 
        }
 

	
 
    } while(0);
 

	
 
    /* correct file position */
 
    if(size < fd->pos)
 
    {
 
        fd->pos = size;
 
        fd->pos_cluster = 0;
 
    }
 

	
 
    return 1;
 
}
 
#endif
 

	
 
/**
 
 * \ingroup fat_dir
 
 * Opens a directory.
 
 *
 
 * \param[in] fs The filesystem on which the directory to open resides.
 
 * \param[in] dir_entry The directory entry which stands for the directory to open.
 
 * \returns An opaque directory descriptor on success, 0 on failure.
 
 * \see fat_close_dir
 
 */
 
struct fat_dir_struct* fat_open_dir(struct fat_fs_struct* fs, const struct fat_dir_entry_struct* dir_entry)
 
{
 
    if(!fs || !dir_entry || !(dir_entry->attributes & FAT_ATTRIB_DIR))
 
        return 0;
 

	
 
#if USE_DYNAMIC_MEMORY
 
    struct fat_dir_struct* dd = malloc(sizeof(*dd));
 
    if(!dd)
 
        return 0;
 
#else
 
    struct fat_dir_struct* dd = fat_dir_handles;
 
    uint8_t i;
 
    for(i = 0; i < FAT_DIR_COUNT; ++i)
 
    {
 
        if(!dd->fs)
 
            break;
 

	
 
        ++dd;
 
    }
 
    if(i >= FAT_DIR_COUNT)
 
        return 0;
 
#endif
 
    
 
    memcpy(&dd->dir_entry, dir_entry, sizeof(*dir_entry));
 
    dd->fs = fs;
 
    dd->entry_cluster = dir_entry->cluster;
 
    dd->entry_offset = 0;
 

	
 
    return dd;
 
}
 

	
 
/**
 
 * \ingroup fat_dir
 
 * Closes a directory descriptor.
 
 *
 
 * This function destroys a directory descriptor which was
 
 * previously obtained by calling fat_open_dir(). When this
 
 * function returns, the given descriptor will be invalid.
 
 *
 
 * \param[in] dd The directory descriptor to close.
 
 * \see fat_open_dir
 
 */
 
void fat_close_dir(struct fat_dir_struct* dd)
 
{
 
    if(dd)
 
#if USE_DYNAMIC_MEMORY
 
        free(dd);
 
#else
 
        dd->fs = 0;
 
#endif
 
}
 

	
 
/**
 
 * \ingroup fat_dir
 
 * Reads the next directory entry contained within a parent directory.
 
 *
 
 * \param[in] dd The descriptor of the parent directory from which to read the entry.
 
 * \param[out] dir_entry Pointer to a buffer into which to write the directory entry information.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_reset_dir
 
 */
 
uint8_t fat_read_dir(struct fat_dir_struct* dd, struct fat_dir_entry_struct* dir_entry)
 
{
 
    if(!dd || !dir_entry)
 
        return 0;
 

	
 
    /* get current position of directory handle */
 
    struct fat_fs_struct* fs = dd->fs;
 
    const struct fat_header_struct* header = &fs->header;
 
    uint16_t cluster_size = header->cluster_size;
 
    cluster_t cluster_num = dd->entry_cluster;
 
    uint16_t cluster_offset = dd->entry_offset;
 
    struct fat_read_dir_callback_arg arg;
 

	
 
    if(cluster_offset >= cluster_size)
 
    {
 
        /* The latest call hit the border of the last cluster in
 
         * the chain, but it still returned a directory entry.
 
         * So we now reset the handle and signal the caller the
 
         * end of the listing.
 
         */
 
        fat_reset_dir(dd);
 
        return 0;
 
    }
 

	
 
    /* reset callback arguments */
 
    memset(&arg, 0, sizeof(arg));
 
    memset(dir_entry, 0, sizeof(*dir_entry));
 
    arg.dir_entry = dir_entry;
 

	
 
    /* check if we read from the root directory */
 
    if(cluster_num == 0)
 
    {
 
#if FAT_FAT32_SUPPORT
 
        if(fs->partition->type == PARTITION_TYPE_FAT32)
 
            cluster_num = header->root_dir_cluster;
 
        else
 
#endif
 
            cluster_size = header->cluster_zero_offset - header->root_dir_offset;
 
    }
 

	
 
    /* read entries */
 
    uint8_t buffer[32];
 
    while(!arg.finished)
 
    {
 
        /* read directory entries up to the cluster border */
 
        uint16_t cluster_left = cluster_size - cluster_offset;
 
        offset_t pos = cluster_offset;
 
        if(cluster_num == 0)
 
            pos += header->root_dir_offset;
 
        else
 
            pos += fat_cluster_offset(fs, cluster_num);
 

	
 
        arg.bytes_read = 0;
 
        if(!fs->partition->device_read_interval(pos,
 
                                                buffer,
 
                                                sizeof(buffer),
 
                                                cluster_left,
 
                                                fat_dir_entry_read_callback,
 
                                                &arg)
 
          )
 
            return 0;
 

	
 
        cluster_offset += arg.bytes_read;
 

	
 
        if(cluster_offset >= cluster_size)
 
        {
 
            /* we reached the cluster border and switch to the next cluster */
 

	
 
            /* get number of next cluster */
 
            if((cluster_num = fat_get_next_cluster(fs, cluster_num)) != 0)
 
            {
 
                cluster_offset = 0;
 
                continue;
 
            }
 

	
 
            /* we are at the end of the cluster chain */
 
            if(!arg.finished)
 
            {
 
                /* directory entry not found, reset directory handle */
 
                fat_reset_dir(dd);
 
                return 0;
 
            }
 
            else
 
            {
 
                /* The current execution of the function has been successful,
 
                 * so we can not signal an end of the directory listing to
 
                 * the caller, but must wait for the next call. So we keep an
 
                 * invalid cluster offset to mark this directory handle's
 
                 * traversal as finished.
 
                 */
 
            }
 

	
 
            break;
 
        }
 
    }
 

	
 
    dd->entry_cluster = cluster_num;
 
    dd->entry_offset = cluster_offset;
 

	
 
    return arg.finished;
 
}
 

	
 
/**
 
 * \ingroup fat_dir
 
 * Resets a directory handle.
 
 *
 
 * Resets the directory handle such that reading restarts
 
 * with the first directory entry.
 
 *
 
 * \param[in] dd The directory handle to reset.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_read_dir
 
 */
 
uint8_t fat_reset_dir(struct fat_dir_struct* dd)
 
{
 
    if(!dd)
 
        return 0;
 

	
 
    dd->entry_cluster = dd->dir_entry.cluster;
 
    dd->entry_offset = 0;
 
    return 1;
 
}
 

	
 
/**
 
 * \ingroup fat_fs
 
 * Callback function for reading a directory entry.
 
 *
 
 * Interprets a raw directory entry and puts the contained
 
 * information into a fat_dir_entry_struct structure.
 
 * 
 
 * For a single file there may exist multiple directory
 
 * entries. All except the last one are lfn entries, which
 
 * contain parts of the long filename. The last directory
 
 * entry is a traditional 8.3 style one. It contains all
 
 * other information like size, cluster, date and time.
 
 * 
 
 * \param[in] buffer A pointer to 32 bytes of raw data.
 
 * \param[in] offset The absolute offset of the raw data.
 
 * \param[in,out] p An argument structure controlling operation.
 
 * \returns 0 on failure or completion, 1 if reading has
 
 *          to be continued
 
 */
 
uint8_t fat_dir_entry_read_callback(uint8_t* buffer, offset_t offset, void* p)
 
{
 
    struct fat_read_dir_callback_arg* arg = p;
 
    struct fat_dir_entry_struct* dir_entry = arg->dir_entry;
 

	
 
    arg->bytes_read += 32;
 

	
 
    /* skip deleted or empty entries */
 
    if(buffer[0] == FAT_DIRENTRY_DELETED || !buffer[0])
 
    {
 
#if FAT_LFN_SUPPORT
 
        arg->checksum = 0;
 
#endif
 
        return 1;
 
    }
 

	
 
#if !FAT_LFN_SUPPORT
 
    /* skip lfn entries */
 
    if(buffer[11] == 0x0f)
 
        return 1;
 
#endif
 

	
 
    char* long_name = dir_entry->long_name;
 
#if FAT_LFN_SUPPORT
 
    if(buffer[11] == 0x0f)
 
    {
 
        /* checksum validation */
 
        if(arg->checksum == 0 || arg->checksum != buffer[13])
 
        {
 
            /* reset directory entry */
 
            memset(dir_entry, 0, sizeof(*dir_entry));
 

	
 
            arg->checksum = buffer[13];
 
            dir_entry->entry_offset = offset;
 
        }
 

	
 
        /* lfn supports unicode, but we do not, for now.
 
         * So we assume pure ascii and read only every
 
         * second byte.
 
         */
 
        uint16_t char_offset = ((buffer[0] & 0x3f) - 1) * 13;
 
        const uint8_t char_mapping[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 };
 
        for(uint8_t i = 0; i <= 12 && char_offset + i < sizeof(dir_entry->long_name) - 1; ++i)
 
            long_name[char_offset + i] = buffer[char_mapping[i]];
 

	
 
        return 1;
 
    }
 
    else
 
#endif
 
    {
 
#if FAT_LFN_SUPPORT
 
        /* if we do not have a long name or the previous lfn does not match, take the 8.3 name */
 
        if(long_name[0] == '\0' || arg->checksum != fat_calc_83_checksum(buffer))
 
#endif
 
        {
 
            /* reset directory entry */
 
            memset(dir_entry, 0, sizeof(*dir_entry));
 
            dir_entry->entry_offset = offset;
 

	
 
            uint8_t i;
 
            for(i = 0; i < 8; ++i)
 
            {
 
                if(buffer[i] == ' ')
 
                    break;
 
                long_name[i] = buffer[i];
 

	
 
                /* Windows NT and later versions do not store lfn entries
 
                 * for 8.3 names which have a lowercase basename, extension
 
                 * or both when everything else is uppercase. They use two
 
                 * extra bits to signal a lowercase basename or extension.
 
                 */
 
                if((buffer[12] & 0x08) && buffer[i] >= 'A' && buffer[i] <= 'Z')
 
                    long_name[i] += 'a' - 'A';
 
            }
 
            if(long_name[0] == 0x05)
 
                long_name[0] = (char) FAT_DIRENTRY_DELETED;
 

	
 
            if(buffer[8] != ' ')
 
            {
 
                long_name[i++] = '.';
 

	
 
                uint8_t j = 8;
 
                for(; j < 11; ++j)
 
                {
 
                    if(buffer[j] == ' ')
 
                        break;
 
                    long_name[i] = buffer[j];
 

	
 
                    /* See above for the lowercase 8.3 name handling of
 
                     * Windows NT and later.
 
                     */
 
                    if((buffer[12] & 0x10) && buffer[j] >= 'A' && buffer[j] <= 'Z')
 
                        long_name[i] += 'a' - 'A';
 

	
 
                    ++i;
 
                }
 
            } 
 

	
 
            long_name[i] = '\0';
 
        }
 
        
 
        /* extract properties of file and store them within the structure */
 
        dir_entry->attributes = buffer[11];
 
        dir_entry->cluster = read16(&buffer[26]);
 
#if FAT_FAT32_SUPPORT
 
        dir_entry->cluster |= ((cluster_t) read16(&buffer[20])) << 16;
 
#endif
 
        dir_entry->file_size = read32(&buffer[28]);
 

	
 
#if FAT_DATETIME_SUPPORT
 
        dir_entry->modification_time = read16(&buffer[22]);
 
        dir_entry->modification_date = read16(&buffer[24]);
 
#endif
 

	
 
        arg->finished = 1;
 
        return 0;
 
    }
 
}
 

	
 
#if DOXYGEN || FAT_LFN_SUPPORT
 
/**
 
 * \ingroup fat_fs
 
 * Calculates the checksum for 8.3 names used within the
 
 * corresponding lfn directory entries.
 
 *
 
 * \param[in] file_name_83 The 11-byte file name buffer.
 
 * \returns The checksum of the given file name.
 
 */
 
uint8_t fat_calc_83_checksum(const uint8_t* file_name_83)
 
{
 
    uint8_t checksum = file_name_83[0];
 
    for(uint8_t i = 1; i < 11; ++i)
 
        checksum = ((checksum >> 1) | (checksum << 7)) + file_name_83[i];
 

	
 
    return checksum;
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_fs
 
 * Searches for space where to store a directory entry.
 
 *
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in] parent The directory in which to search.
 
 * \param[in] dir_entry The directory entry for which to search space.
 
 * \returns 0 on failure, a device offset on success.
 
 */
 
offset_t fat_find_offset_for_dir_entry(struct fat_fs_struct* fs, const struct fat_dir_struct* parent, const struct fat_dir_entry_struct* dir_entry)
 
{
 
    if(!fs || !dir_entry)
 
        return 0;
 

	
 
    /* search for a place where to write the directory entry to disk */
 
#if FAT_LFN_SUPPORT
 
    uint8_t free_dir_entries_needed = (strlen(dir_entry->long_name) + 12) / 13 + 1;
 
    uint8_t free_dir_entries_found = 0;
 
#endif
 
    cluster_t cluster_num = parent->dir_entry.cluster;
 
    offset_t dir_entry_offset = 0;
 
    offset_t offset = 0;
 
    offset_t offset_to = 0;
 
#if FAT_FAT32_SUPPORT
 
    uint8_t is_fat32 = (fs->partition->type == PARTITION_TYPE_FAT32);
 
#endif
 

	
 
    if(cluster_num == 0)
 
    {
 
#if FAT_FAT32_SUPPORT
 
        if(is_fat32)
 
        {
 
            cluster_num = fs->header.root_dir_cluster;
 
        }
 
        else
 
#endif
 
        {
 
            /* we read/write from the root directory entry */
 
            offset = fs->header.root_dir_offset;
 
            offset_to = fs->header.cluster_zero_offset;
 
            dir_entry_offset = offset;
 
        }
 
    }
 
    
 
    while(1)
 
    {
 
        if(offset == offset_to)
 
        {
 
            if(cluster_num == 0)
 
                /* We iterated through the whole root directory and
 
                 * could not find enough space for the directory entry.
 
                 */
 
                return 0;
 

	
 
            if(offset)
 
            {
 
                /* We reached a cluster boundary and have to
 
                 * switch to the next cluster.
 
                 */
 

	
 
                cluster_t cluster_next = fat_get_next_cluster(fs, cluster_num);
 
                if(!cluster_next)
 
                {
 
                    cluster_next = fat_append_clusters(fs, cluster_num, 1);
 
                    if(!cluster_next)
 
                        return 0;
 

	
 
                    /* we appended a new cluster and know it is free */
 
                    dir_entry_offset = fs->header.cluster_zero_offset +
 
                                       (offset_t) (cluster_next - 2) * fs->header.cluster_size;
 

	
 
                    /* clear cluster to avoid garbage directory entries */
 
                    fat_clear_cluster(fs, cluster_next);
 

	
 
                    break;
 
                }
 
                cluster_num = cluster_next;
 
            }
 

	
 
            offset = fat_cluster_offset(fs, cluster_num);
 
            offset_to = offset + fs->header.cluster_size;
 
            dir_entry_offset = offset;
 
#if FAT_LFN_SUPPORT
 
            free_dir_entries_found = 0;
 
#endif
 
        }
 
        
 
        /* read next lfn or 8.3 entry */
 
        uint8_t first_char;
 
        if(!fs->partition->device_read(offset, &first_char, sizeof(first_char)))
 
            return 0;
 

	
 
        /* check if we found a free directory entry */
 
        if(first_char == FAT_DIRENTRY_DELETED || !first_char)
 
        {
 
            /* check if we have the needed number of available entries */
 
#if FAT_LFN_SUPPORT
 
            ++free_dir_entries_found;
 
            if(free_dir_entries_found >= free_dir_entries_needed)
 
#endif
 
                break;
 

	
 
            offset += 32;
 
        }
 
        else
 
        {
 
            offset += 32;
 
            dir_entry_offset = offset;
 
#if FAT_LFN_SUPPORT
 
            free_dir_entries_found = 0;
 
#endif
 
        }
 
    }
 

	
 
    return dir_entry_offset;
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_fs
 
 * Writes a directory entry to disk.
 
 *
 
 * \note The file name is not checked for invalid characters.
 
 *
 
 * \note The generation of the short 8.3 file name is quite
 
 * simple. The first eight characters are used for the filename.
 
 * The extension, if any, is made up of the first three characters
 
 * following the last dot within the long filename. If the
 
 * filename (without the extension) is longer than eight characters,
 
 * the lower byte of the cluster number replaces the last two
 
 * characters to avoid name clashes. In any other case, it is your
 
 * responsibility to avoid name clashes.
 
 *
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in] dir_entry The directory entry to write.
 
 * \returns 0 on failure, 1 on success.
 
 */
 
uint8_t fat_write_dir_entry(const struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry)
 
{
 
    if(!fs || !dir_entry)
 
        return 0;
 
    
 
#if FAT_DATETIME_SUPPORT
 
    {
 
        uint16_t year;
 
        uint8_t month;
 
        uint8_t day;
 
        uint8_t hour;
 
        uint8_t min;
 
        uint8_t sec;
 

	
 
        fat_get_datetime(&year, &month, &day, &hour, &min, &sec);
 
        fat_set_file_modification_date(dir_entry, year, month, day);
 
        fat_set_file_modification_time(dir_entry, hour, min, sec);
 
    }
 
#endif
 

	
 
    device_write_t device_write = fs->partition->device_write;
 
    offset_t offset = dir_entry->entry_offset;
 
    const char* name = dir_entry->long_name;
 
    uint8_t name_len = strlen(name);
 
#if FAT_LFN_SUPPORT
 
    uint8_t lfn_entry_count = (name_len + 12) / 13;
 
#endif
 
    uint8_t buffer[32];
 

	
 
    /* write 8.3 entry */
 

	
 
    /* generate 8.3 file name */
 
    memset(&buffer[0], ' ', 11);
 
    char* name_ext = strrchr(name, '.');
 
    if(name_ext && *++name_ext)
 
    {
 
        uint8_t name_ext_len = strlen(name_ext);
 
        name_len -= name_ext_len + 1;
 

	
 
        if(name_ext_len > 3)
 
#if FAT_LFN_SUPPORT
 
            name_ext_len = 3;
 
#else
 
            return 0;
 
#endif
 
        
 
        memcpy(&buffer[8], name_ext, name_ext_len);
 
    }
 
    
 
    if(name_len <= 8)
 
    {
 
        memcpy(buffer, name, name_len);
 

	
 
#if FAT_LFN_SUPPORT
 
        /* For now, we create lfn entries for all files,
 
         * except the "." and ".." directory references.
 
         * This is to avoid difficulties with capitalization,
 
         * as 8.3 filenames allow uppercase letters only.
 
         *
 
         * Theoretically it would be possible to leave
 
         * the 8.3 entry alone if the basename and the
 
         * extension have no mixed capitalization.
 
         */
 
        if(name[0] == '.' &&
 
           ((name[1] == '.' && name[2] == '\0') ||
 
            name[1] == '\0')
 
          )
 
            lfn_entry_count = 0;
 
#endif
 
    }
 
    else
 
    {
 
#if FAT_LFN_SUPPORT
 
        memcpy(buffer, name, 8);
 

	
 
        /* Minimize 8.3 name clashes by appending
 
         * the lower byte of the cluster number.
 
         */
 
        uint8_t num = dir_entry->cluster & 0xff;
 

	
 
        buffer[6] = (num < 0xa0) ? ('0' + (num >> 4)) : ('a' + (num >> 4));
 
        num &= 0x0f;
 
        buffer[7] = (num < 0x0a) ? ('0' + num) : ('a' + num);
 
#else
 
        return 0;
 
#endif
 
    }
 
    if(buffer[0] == FAT_DIRENTRY_DELETED)
 
        buffer[0] = 0x05;
 

	
 
    /* fill directory entry buffer */
 
    memset(&buffer[11], 0, sizeof(buffer) - 11);
 
    buffer[0x0b] = dir_entry->attributes;
 
#if FAT_DATETIME_SUPPORT
 
    write16(&buffer[0x16], dir_entry->modification_time);
 
    write16(&buffer[0x18], dir_entry->modification_date);
 
#endif
 
#if FAT_FAT32_SUPPORT
 
    write16(&buffer[0x14], (uint16_t) (dir_entry->cluster >> 16));
 
#endif
 
    write16(&buffer[0x1a], dir_entry->cluster);
 
    write32(&buffer[0x1c], dir_entry->file_size);
 

	
 
    /* write to disk */
 
#if FAT_LFN_SUPPORT
 
    if(!device_write(offset + (uint16_t) lfn_entry_count * 32, buffer, sizeof(buffer)))
 
#else
 
    if(!device_write(offset, buffer, sizeof(buffer)))
 
#endif
 
        return 0;
 
    
 
#if FAT_LFN_SUPPORT
 
    /* calculate checksum of 8.3 name */
 
    uint8_t checksum = fat_calc_83_checksum(buffer);
 
    
 
    /* write lfn entries */
 
    for(uint8_t lfn_entry = lfn_entry_count; lfn_entry > 0; --lfn_entry)
 
    {
 
        memset(buffer, 0xff, sizeof(buffer));
 
        
 
        /* set file name */
 
        const char* long_name_curr = name + (lfn_entry - 1) * 13;
 
        uint8_t i = 1;
 
        while(i < 0x1f)
 
        {
 
            buffer[i++] = *long_name_curr;
 
            buffer[i++] = 0;
 

	
 
            switch(i)
 
            {
 
                case 0x0b:
 
                    i = 0x0e;
 
                    break;
 
                case 0x1a:
 
                    i = 0x1c;
 
                    break;
 
            }
 

	
 
            if(!*long_name_curr++)
 
                break;
 
        }
 
        
 
        /* set index of lfn entry */
 
        buffer[0x00] = lfn_entry;
 
        if(lfn_entry == lfn_entry_count)
 
            buffer[0x00] |= FAT_DIRENTRY_LFNLAST;
 

	
 
        /* mark as lfn entry */
 
        buffer[0x0b] = 0x0f;
 

	
 
        /* set 8.3 checksum */
 
        buffer[0x0d] = checksum;
 

	
 
        /* clear reserved bytes */
 
        buffer[0x0c] = 0;
 
        buffer[0x1a] = 0;
 
        buffer[0x1b] = 0;
 

	
 
        /* write entry */
 
        device_write(offset, buffer, sizeof(buffer));
 
    
 
        offset += sizeof(buffer);
 
    }
 
#endif
 
    
 
    return 1;
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_file
 
 * Creates a file.
 
 *
 
 * Creates a file and obtains the directory entry of the
 
 * new file. If the file to create already exists, the
 
 * directory entry of the existing file will be returned
 
 * within the dir_entry parameter.
 
 *
 
 * \note The file name is not checked for invalid characters.
 
 *
 
 * \note The generation of the short 8.3 file name is quite
 
 * simple. The first eight characters are used for the filename.
 
 * The extension, if any, is made up of the first three characters
 
 * following the last dot within the long filename. If the
 
 * filename (without the extension) is longer than eight characters,
 
 * the lower byte of the cluster number replaces the last two
 
 * characters to avoid name clashes. In any other case, it is your
 
 * responsibility to avoid name clashes.
 
 *
 
 * \param[in] parent The handle of the directory in which to create the file.
 
 * \param[in] file The name of the file to create.
 
 * \param[out] dir_entry The directory entry to fill for the new (or existing) file.
 
 * \returns 0 on failure, 1 on success, 2 if the file already existed.
 
 * \see fat_delete_file
 
 */
 
uint8_t fat_create_file(struct fat_dir_struct* parent, const char* file, struct fat_dir_entry_struct* dir_entry)
 
{
 
    if(!parent || !file || !file[0] || !dir_entry)
 
        return 0;
 

	
 
    /* check if the file already exists */
 
    while(1)
 
    {
 
        if(!fat_read_dir(parent, dir_entry))
 
            break;
 

	
 
        if(strcmp(file, dir_entry->long_name) == 0)
 
        {
 
            fat_reset_dir(parent);
 
            return 2;
 
        }
 
    }
 

	
 
    struct fat_fs_struct* fs = parent->fs;
 

	
 
    /* prepare directory entry with values already known */
 
    memset(dir_entry, 0, sizeof(*dir_entry));
 
    strncpy(dir_entry->long_name, file, sizeof(dir_entry->long_name) - 1);
 

	
 
    /* find place where to store directory entry */
 
    if(!(dir_entry->entry_offset = fat_find_offset_for_dir_entry(fs, parent, dir_entry)))
 
        return 0;
 
    
 
    /* write directory entry to disk */
 
    if(!fat_write_dir_entry(fs, dir_entry))
 
        return 0;
 
    
 
    return 1;
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_file
 
 * Deletes a file or directory.
 
 *
 
 * If a directory is deleted without first deleting its
 
 * subdirectories and files, disk space occupied by these
 
 * files will get wasted as there is no chance to release
 
 * it and mark it as free.
 
 * 
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in] dir_entry The directory entry of the file to delete.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_create_file
 
 */
 
uint8_t fat_delete_file(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry)
 
{
 
    if(!fs || !dir_entry)
 
        return 0;
 

	
 
    /* get offset of the file's directory entry */
 
    offset_t dir_entry_offset = dir_entry->entry_offset;
 
    if(!dir_entry_offset)
 
        return 0;
 

	
 
#if FAT_LFN_SUPPORT
 
    uint8_t buffer[12];
 
    while(1)
 
    {
 
        /* read directory entry */
 
        if(!fs->partition->device_read(dir_entry_offset, buffer, sizeof(buffer)))
 
            return 0;
 
        
 
        /* mark the directory entry as deleted */
 
        buffer[0] = FAT_DIRENTRY_DELETED;
 
        
 
        /* write back entry */
 
        if(!fs->partition->device_write(dir_entry_offset, buffer, sizeof(buffer)))
 
            return 0;
 

	
 
        /* check if we deleted the whole entry */
 
        if(buffer[11] != 0x0f)
 
            break;
 

	
 
        dir_entry_offset += 32;
 
    }
 
#else
 
    /* mark the directory entry as deleted */
 
    uint8_t first_char = FAT_DIRENTRY_DELETED;
 
    if(!fs->partition->device_write(dir_entry_offset, &first_char, 1))
 
        return 0;
 
#endif
 

	
 
    /* We deleted the directory entry. The next thing to do is
 
     * marking all occupied clusters as free.
 
     */
 
    return (dir_entry->cluster == 0 || fat_free_clusters(fs, dir_entry->cluster));
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_file
 
 * Moves or renames a file.
 
 *
 
 * Changes a file's name, optionally moving it into another
 
 * directory as well. Before calling this function, the
 
 * target file name must not exist. Moving a file to a
 
 * different filesystem (i.e. \a parent_new doesn't lie on
 
 * \a fs) is not supported.
 
 * 
 
 * After successfully renaming (and moving) the file, the
 
 * given directory entry is updated such that it points to
 
 * the file's new location.
 
 *
 
 * \note The notes which apply to fat_create_file() also
 
 * apply to this function.
 
 *
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in,out] dir_entry The directory entry of the file to move.
 
 * \param[in] parent_new The handle of the new parent directory of the file.
 
 * \param[in] file_new The file's new name.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_create_file, fat_delete_file, fat_move_dir
 
 */
 
uint8_t fat_move_file(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry, struct fat_dir_struct* parent_new, const char* file_new)
 
{
 
    if(!fs || !dir_entry || !parent_new || (file_new && !file_new[0]))
 
        return 0;
 
    if(fs != parent_new->fs)
 
        return 0;
 

	
 
    /* use existing file name if none has been specified */
 
    if(!file_new)
 
        file_new = dir_entry->long_name;
 

	
 
    /* create file with new file name */
 
    struct fat_dir_entry_struct dir_entry_new;
 
    if(!fat_create_file(parent_new, file_new, &dir_entry_new))
 
        return 0;
 

	
 
    /* copy members of directory entry which do not change with rename */
 
    dir_entry_new.attributes = dir_entry->attributes;
 
#if FAT_DATETIME_SUPPORT
 
    dir_entry_new.modification_time = dir_entry->modification_time;
 
    dir_entry_new.modification_date = dir_entry->modification_date;
 
#endif
 
    dir_entry_new.cluster = dir_entry->cluster;
 
    dir_entry_new.file_size = dir_entry->file_size;
 

	
 
    /* make the new file name point to the old file's content */
 
    if(!fat_write_dir_entry(fs, &dir_entry_new))
 
    {
 
        fat_delete_file(fs, &dir_entry_new);
 
        return 0;
 
    }
 
    
 
    /* delete the old file, but not its clusters, which have already been remapped above */
 
    dir_entry->cluster = 0;
 
    if(!fat_delete_file(fs, dir_entry))
 
        return 0;
 

	
 
    *dir_entry = dir_entry_new;
 
    return 1;
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_WRITE_SUPPORT
 
/**
 
 * \ingroup fat_dir
 
 * Creates a directory.
 
 *
 
 * Creates a directory and obtains its directory entry.
 
 * If the directory to create already exists, its
 
 * directory entry will be returned within the dir_entry
 
 * parameter.
 
 *
 
 * \note The notes which apply to fat_create_file() also
 
 * apply to this function.
 
 *
 
 * \param[in] parent The handle of the parent directory of the new directory.
 
 * \param[in] dir The name of the directory to create.
 
 * \param[out] dir_entry The directory entry to fill for the new directory.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_delete_dir
 
 */
 
uint8_t fat_create_dir(struct fat_dir_struct* parent, const char* dir, struct fat_dir_entry_struct* dir_entry)
 
{
 
    if(!parent || !dir || !dir[0] || !dir_entry)
 
        return 0;
 

	
 
    /* check if the file or directory already exists */
 
    while(fat_read_dir(parent, dir_entry))
 
    {
 
        if(strcmp(dir, dir_entry->long_name) == 0)
 
        {
 
            fat_reset_dir(parent);
 
            return 0;
 
        }
 
    }
 

	
 
    struct fat_fs_struct* fs = parent->fs;
 

	
 
    /* allocate cluster which will hold directory entries */
 
    cluster_t dir_cluster = fat_append_clusters(fs, 0, 1);
 
    if(!dir_cluster)
 
        return 0;
 

	
 
    /* clear cluster to prevent bogus directory entries */
 
    fat_clear_cluster(fs, dir_cluster);
 
    
 
    memset(dir_entry, 0, sizeof(*dir_entry));
 
    dir_entry->attributes = FAT_ATTRIB_DIR;
 

	
 
    /* create "." directory self reference */
 
    dir_entry->entry_offset = fs->header.cluster_zero_offset +
 
                              (offset_t) (dir_cluster - 2) * fs->header.cluster_size;
 
    dir_entry->long_name[0] = '.';
 
    dir_entry->cluster = dir_cluster;
 
    if(!fat_write_dir_entry(fs, dir_entry))
 
    {
 
        fat_free_clusters(fs, dir_cluster);
 
        return 0;
 
    }
 

	
 
    /* create ".." parent directory reference */
 
    dir_entry->entry_offset += 32;
 
    dir_entry->long_name[1] = '.';
 
    dir_entry->cluster = parent->dir_entry.cluster;
 
    if(!fat_write_dir_entry(fs, dir_entry))
 
    {
 
        fat_free_clusters(fs, dir_cluster);
 
        return 0;
 
    }
 

	
 
    /* fill directory entry */
 
    strncpy(dir_entry->long_name, dir, sizeof(dir_entry->long_name) - 1);
 
    dir_entry->cluster = dir_cluster;
 

	
 
    /* find place where to store directory entry */
 
    if(!(dir_entry->entry_offset = fat_find_offset_for_dir_entry(fs, parent, dir_entry)))
 
    {
 
        fat_free_clusters(fs, dir_cluster);
 
        return 0;
 
    }
 

	
 
    /* write directory to disk */
 
    if(!fat_write_dir_entry(fs, dir_entry))
 
    {
 
        fat_free_clusters(fs, dir_cluster);
 
        return 0;
 
    }
 

	
 
    return 1;
 
}
 
#endif
 

	
 
/**
 
 * \ingroup fat_dir
 
 * Deletes a directory.
 
 *
 
 * This is just a synonym for fat_delete_file().
 
 * If a directory is deleted without first deleting its
 
 * subdirectories and files, disk space occupied by these
 
 * files will get wasted as there is no chance to release
 
 * it and mark it as free.
 
 * 
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in] dir_entry The directory entry of the directory to delete.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_create_dir
 
 */
 
#ifdef DOXYGEN
 
uint8_t fat_delete_dir(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry);
 
#endif
 

	
 
/**
 
 * \ingroup fat_dir
 
 * Moves or renames a directory.
 
 *
 
 * This is just a synonym for fat_move_file().
 
 * 
 
 * \param[in] fs The filesystem on which to operate.
 
 * \param[in,out] dir_entry The directory entry of the directory to move.
 
 * \param[in] parent_new The handle of the new parent directory.
 
 * \param[in] dir_new The directory's new name.
 
 * \returns 0 on failure, 1 on success.
 
 * \see fat_create_dir, fat_delete_dir, fat_move_file
 
 */
 
#ifdef DOXYGEN
 
uint8_t fat_move_dir(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry, struct fat_dir_struct* parent_new, const char* dir_new);
 
#endif
 

	
 
#if DOXYGEN || FAT_DATETIME_SUPPORT
 
/**
 
 * \ingroup fat_file
 
 * Returns the modification date of a file.
 
 *
 
 * \param[in] dir_entry The directory entry of which to return the modification date.
 
 * \param[out] year The year the file was last modified.
 
 * \param[out] month The month the file was last modified.
 
 * \param[out] day The day the file was last modified.
 
 */
 
void fat_get_file_modification_date(const struct fat_dir_entry_struct* dir_entry, uint16_t* year, uint8_t* month, uint8_t* day)
 
{
 
    if(!dir_entry)
 
        return;
 

	
 
    *year = 1980 + ((dir_entry->modification_date >> 9) & 0x7f);
 
    *month = (dir_entry->modification_date >> 5) & 0x0f;
 
    *day = (dir_entry->modification_date >> 0) & 0x1f;
 
}
 
#endif
 

	
 
#if DOXYGEN || FAT_DATETIME_SUPPORT
 
/**
 
 * \ingroup fat_file
 
 * Returns the modification time of a file.
 
 *
 
 * \param[in] dir_entry The directory entry of which to return the modification time.
 
 * \param[out] hour The hour the file was last modified.
 
 * \param[out] min The min the file was last modified.
 
 * \param[out] sec The sec the file was last modified.
 
 */
 
void fat_get_file_modification_time(const struct fat_dir_entry_struct* dir_entry, uint8_t* hour, uint8_t* min, uint8_t* sec)
 
{
 
    if(!dir_entry)
 
        return;
 

	
 
    *hour = (dir_entry->modification_time >> 11) & 0x1f;
 
    *min = (dir_entry->modification_time >> 5) & 0x3f;
 
    *sec = ((dir_entry->modification_time >> 0) & 0x1f) * 2;
 
}
 
#endif
 

	
 
#if DOXYGEN || (FAT_WRITE_SUPPORT && FAT_DATETIME_SUPPORT)
 
/**
 
 * \ingroup fat_file
 
 * Sets the modification time of a date.
 
 *
 
 * \param[in] dir_entry The directory entry for which to set the modification date.
 
 * \param[in] year The year the file was last modified.
 
 * \param[in] month The month the file was last modified.
 
 * \param[in] day The day the file was last modified.
 
 */
 
void fat_set_file_modification_date(struct fat_dir_entry_struct* dir_entry, uint16_t year, uint8_t month, uint8_t day)
 
{
 
    if(!dir_entry)
 
        return;
 

	
 
    dir_entry->modification_date =
 
        ((year - 1980) << 9) |
 
        ((uint16_t) month << 5) |
 
        ((uint16_t) day << 0);
 
}
 
#endif
 

	
 
#if DOXYGEN || (FAT_WRITE_SUPPORT && FAT_DATETIME_SUPPORT)
 
/**
 
 * \ingroup fat_file
 
 * Sets the modification time of a file.
 
 *
 
 * \param[in] dir_entry The directory entry for which to set the modification time.
 
 * \param[in] hour The year the file was last modified.
 
 * \param[in] min The month the file was last modified.
 
 * \param[in] sec The day the file was last modified.
 
 */
 
void fat_set_file_modification_time(struct fat_dir_entry_struct* dir_entry, uint8_t hour, uint8_t min, uint8_t sec)
 
{
 
    if(!dir_entry)
 
        return;
 

	
 
    dir_entry->modification_time =
 
        ((uint16_t) hour << 11) |
 
        ((uint16_t) min << 5) |
 
        ((uint16_t) sec >> 1) ;
 
}
 
#endif
 

	
 
/**
 
 * \ingroup fat_fs
 
 * Returns the amount of total storage capacity of the filesystem in bytes.
 
 *
 
 * \param[in] fs The filesystem on which to operate.
 
 * \returns 0 on failure, the filesystem size in bytes otherwise.
 
 */
 
offset_t fat_get_fs_size(const struct fat_fs_struct* fs)
 
{
 
    if(!fs)
 
        return 0;
 

	
 
#if FAT_FAT32_SUPPORT
 
    if(fs->partition->type == PARTITION_TYPE_FAT32)
 
        return (offset_t) (fs->header.fat_size / 4 - 2) * fs->header.cluster_size;
 
    else
 
#endif
 
        return (offset_t) (fs->header.fat_size / 2 - 2) * fs->header.cluster_size;
 
}
 

	
 
/**
 
 * \ingroup fat_fs
 
 * Returns the amount of free storage capacity on the filesystem in bytes.
 
 *
 
 * \note As the FAT filesystem is cluster based, this function does not
 
 *       return continuous values but multiples of the cluster size.
 
 *
 
 * \param[in] fs The filesystem on which to operate.
 
 * \returns 0 on failure, the free filesystem space in bytes otherwise.
 
 */
 
offset_t fat_get_fs_free(const struct fat_fs_struct* fs)
 
{
 
    if(!fs)
 
        return 0;
 

	
 
    uint8_t fat[32];
 
    struct fat_usage_count_callback_arg count_arg;
 
    count_arg.cluster_count = 0;
 
    count_arg.buffer_size = sizeof(fat);
 

	
 
    offset_t fat_offset = fs->header.fat_offset;
 
    uint32_t fat_size = fs->header.fat_size;
 
    while(fat_size > 0)
 
    {
 
        uintptr_t length = UINTPTR_MAX - 1;
 
        if(fat_size < length)
 
            length = fat_size;
 

	
 
        if(!fs->partition->device_read_interval(fat_offset,
 
                                                fat,
 
                                                sizeof(fat),
 
                                                length,
 
#if FAT_FAT32_SUPPORT
 
                                                (fs->partition->type == PARTITION_TYPE_FAT16) ?
 
                                                    fat_get_fs_free_16_callback :
 
                                                    fat_get_fs_free_32_callback,
 
#else
 
                                                fat_get_fs_free_16_callback,
 
#endif
 
                                                &count_arg
 
                                               )
 
          )
 
            return 0;
 

	
 
        fat_offset += length;
 
        fat_size -= length;
 
    }
 

	
 
    return (offset_t) count_arg.cluster_count * fs->header.cluster_size;
 
}
 

	
 
/**
 
 * \ingroup fat_fs
 
 * Callback function used for counting free clusters in a FAT.
 
 */
 
uint8_t fat_get_fs_free_16_callback(uint8_t* buffer, offset_t offset, void* p)
 
{
 
    struct fat_usage_count_callback_arg* count_arg = (struct fat_usage_count_callback_arg*) p;
 
    uintptr_t buffer_size = count_arg->buffer_size;
 

	
 
    for(uintptr_t i = 0; i < buffer_size; i += 2, buffer += 2)
 
    {
 
        uint16_t cluster = read16(buffer);
 
        if(cluster == HTOL16(FAT16_CLUSTER_FREE))
 
            ++(count_arg->cluster_count);
 
    }
 

	
 
    return 1;
 
}
 

	
 
#if DOXYGEN || FAT_FAT32_SUPPORT
 
/**
 
 * \ingroup fat_fs
 
 * Callback function used for counting free clusters in a FAT32.
 
 */
 
uint8_t fat_get_fs_free_32_callback(uint8_t* buffer, offset_t offset, void* p)
 
{
 
    struct fat_usage_count_callback_arg* count_arg = (struct fat_usage_count_callback_arg*) p;
 
    uintptr_t buffer_size = count_arg->buffer_size;
 

	
 
    for(uintptr_t i = 0; i < buffer_size; i += 4, buffer += 4)
 
    {
 
        uint32_t cluster = read32(buffer);
 
        if(cluster == HTOL32(FAT32_CLUSTER_FREE))
 
            ++(count_arg->cluster_count);
 
    }
 

	
 
    return 1;
 
}
 
#endif
 

	
master/master/lib/sd/fat.h
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#ifndef FAT_H
 
#define FAT_H
 

	
 
#include <stdint.h>
 
#include "fat_config.h"
 

	
 
#ifdef __cplusplus
 
extern "C"
 
{
 
#endif
 

	
 
/**
 
 * \addtogroup fat
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * FAT header (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \author Roland Riegel
 
 */
 

	
 
/**
 
 * \addtogroup fat_file
 
 * @{
 
 */
 

	
 
/** The file is read-only. */
 
#define FAT_ATTRIB_READONLY (1 << 0)
 
/** The file is hidden. */
 
#define FAT_ATTRIB_HIDDEN (1 << 1)
 
/** The file is a system file. */
 
#define FAT_ATTRIB_SYSTEM (1 << 2)
 
/** The file is empty and has the volume label as its name. */
 
#define FAT_ATTRIB_VOLUME (1 << 3)
 
/** The file is a directory. */
 
#define FAT_ATTRIB_DIR (1 << 4)
 
/** The file has to be archived. */
 
#define FAT_ATTRIB_ARCHIVE (1 << 5)
 

	
 
/** The given offset is relative to the beginning of the file. */
 
#define FAT_SEEK_SET 0
 
/** The given offset is relative to the current read/write position. */
 
#define FAT_SEEK_CUR 1
 
/** The given offset is relative to the end of the file. */
 
#define FAT_SEEK_END 2
 

	
 
/**
 
 * @}
 
 */
 

	
 
struct partition_struct;
 
struct fat_fs_struct;
 
struct fat_file_struct;
 
struct fat_dir_struct;
 

	
 
/**
 
 * \ingroup fat_file
 
 * Describes a directory entry.
 
 */
 
struct fat_dir_entry_struct
 
{
 
    /** The file's name, truncated to 31 characters. */
 
    char long_name[32];
 
    /** The file's attributes. Mask of the FAT_ATTRIB_* constants. */
 
    uint8_t attributes;
 
#if FAT_DATETIME_SUPPORT
 
    /** Compressed representation of modification time. */
 
    uint16_t modification_time;
 
    /** Compressed representation of modification date. */
 
    uint16_t modification_date;
 
#endif
 
    /** The cluster in which the file's first byte resides. */
 
    cluster_t cluster;
 
    /** The file's size. */
 
    uint32_t file_size;
 
    /** The total disk offset of this directory entry. */
 
    offset_t entry_offset;
 
};
 

	
 
struct fat_fs_struct* fat_open(struct partition_struct* partition);
 
void fat_close(struct fat_fs_struct* fs);
 

	
 
struct fat_file_struct* fat_open_file(struct fat_fs_struct* fs, const struct fat_dir_entry_struct* dir_entry);
 
void fat_close_file(struct fat_file_struct* fd);
 
intptr_t fat_read_file(struct fat_file_struct* fd, uint8_t* buffer, uintptr_t buffer_len);
 
intptr_t fat_write_file(struct fat_file_struct* fd, const uint8_t* buffer, uintptr_t buffer_len);
 
uint8_t fat_seek_file(struct fat_file_struct* fd, int32_t* offset, uint8_t whence);
 
uint8_t fat_resize_file(struct fat_file_struct* fd, uint32_t size);
 

	
 
struct fat_dir_struct* fat_open_dir(struct fat_fs_struct* fs, const struct fat_dir_entry_struct* dir_entry);
 
void fat_close_dir(struct fat_dir_struct* dd);
 
uint8_t fat_read_dir(struct fat_dir_struct* dd, struct fat_dir_entry_struct* dir_entry);
 
uint8_t fat_reset_dir(struct fat_dir_struct* dd);
 

	
 
uint8_t fat_create_file(struct fat_dir_struct* parent, const char* file, struct fat_dir_entry_struct* dir_entry);
 
uint8_t fat_delete_file(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry);
 
uint8_t fat_move_file(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry, struct fat_dir_struct* parent_new, const char* file_new);
 
uint8_t fat_create_dir(struct fat_dir_struct* parent, const char* dir, struct fat_dir_entry_struct* dir_entry);
 
#define fat_delete_dir fat_delete_file
 
#define fat_move_dir fat_move_file
 

	
 
void fat_get_file_modification_date(const struct fat_dir_entry_struct* dir_entry, uint16_t* year, uint8_t* month, uint8_t* day);
 
void fat_get_file_modification_time(const struct fat_dir_entry_struct* dir_entry, uint8_t* hour, uint8_t* min, uint8_t* sec);
 

	
 
uint8_t fat_get_dir_entry_of_path(struct fat_fs_struct* fs, const char* path, struct fat_dir_entry_struct* dir_entry);
 

	
 
offset_t fat_get_fs_size(const struct fat_fs_struct* fs);
 
offset_t fat_get_fs_free(const struct fat_fs_struct* fs);
 

	
 
/**
 
 * @}
 
 */
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
master/master/lib/sd/fat_config.h
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#ifndef FAT_CONFIG_H
 
#define FAT_CONFIG_H
 

	
 
#include <stdint.h>
 
#include "sd_raw_config.h"
 

	
 
#ifdef __cplusplus
 
extern "C"
 
{
 
#endif
 

	
 
/**
 
 * \addtogroup fat
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * FAT configuration (license: GPLv2 or LGPLv2.1)
 
 */
 

	
 
/**
 
 * \ingroup fat_config
 
 * Controls FAT write support.
 
 *
 
 * Set to 1 to enable FAT write support, set to 0 to disable it.
 
 */
 
#define FAT_WRITE_SUPPORT SD_RAW_WRITE_SUPPORT
 

	
 
/**
 
 * \ingroup fat_config
 
 * Controls FAT long filename (LFN) support.
 
 *
 
 * Set to 1 to enable LFN support, set to 0 to disable it.
 
 */
 
#define FAT_LFN_SUPPORT 1
 

	
 
/**
 
 * \ingroup fat_config
 
 * Controls FAT date and time support.
 
 * 
 
 * Set to 1 to enable FAT date and time stamping support.
 
 */
 
#define FAT_DATETIME_SUPPORT 0
 

	
 
/**
 
 * \ingroup fat_config
 
 * Controls FAT32 support.
 
 *
 
 * Set to 1 to enable FAT32 support.
 
 */
 
#define FAT_FAT32_SUPPORT SD_RAW_SDHC
 

	
 
/**
 
 * \ingroup fat_config
 
 * Controls updates of directory entries.
 
 *
 
 * Set to 1 to delay directory entry updates until the file is closed.
 
 * This can boost performance significantly, but may cause data loss
 
 * if the file is not properly closed.
 
 */
 
#define FAT_DELAY_DIRENTRY_UPDATE 0
 

	
 
/**
 
 * \ingroup fat_config
 
 * Determines the function used for retrieving current date and time.
 
 *
 
 * Define this to the function call which shall be used to retrieve
 
 * current date and time.
 
 *
 
 * \note Used only when FAT_DATETIME_SUPPORT is 1.
 
 *
 
 * \param[out] year Pointer to a \c uint16_t which receives the current year.
 
 * \param[out] month Pointer to a \c uint8_t which receives the current month.
 
 * \param[out] day Pointer to a \c uint8_t which receives the current day.
 
 * \param[out] hour Pointer to a \c uint8_t which receives the current hour.
 
 * \param[out] min Pointer to a \c uint8_t which receives the current minute.
 
 * \param[out] sec Pointer to a \c uint8_t which receives the current sec.
 
 */
 
#define fat_get_datetime(year, month, day, hour, min, sec) \
 
    get_datetime(year, month, day, hour, min, sec)
 
/* forward declaration for the above */
 
void get_datetime(uint16_t* year, uint8_t* month, uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec);
 

	
 
/**
 
 * \ingroup fat_config
 
 * Maximum number of filesystem handles.
 
 */
 
#define FAT_FS_COUNT 1
 

	
 
/**
 
 * \ingroup fat_config
 
 * Maximum number of file handles.
 
 */
 
#define FAT_FILE_COUNT 1
 

	
 
/**
 
 * \ingroup fat_config
 
 * Maximum number of directory handles.
 
 */
 
#define FAT_DIR_COUNT 2
 

	
 
/**
 
 * @}
 
 */
 

	
 
#if FAT_FAT32_SUPPORT
 
    typedef uint32_t cluster_t;
 
#else
 
    typedef uint16_t cluster_t;
 
#endif
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
master/master/lib/sd/partition.c
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#include "byteordering.h"
 
#include "partition.h"
 
#include "partition_config.h"
 
#include "sd-reader_config.h"
 

	
 
#include <string.h>
 

	
 
#if USE_DYNAMIC_MEMORY
 
    #include <stdlib.h>
 
#endif
 

	
 
/**
 
 * \addtogroup partition Partition table support
 
 *
 
 * Support for reading partition tables and access to partitions.
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * Partition table implementation (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \author Roland Riegel
 
 */
 

	
 
/**
 
 * \addtogroup partition_config Configuration of partition table support
 
 * Preprocessor defines to configure the partition support.
 
 */
 

	
 
#if !USE_DYNAMIC_MEMORY
 
static struct partition_struct partition_handles[PARTITION_COUNT];
 
#endif
 

	
 
/**
 
 * Opens a partition.
 
 *
 
 * Opens a partition by its index number and returns a partition
 
 * handle which describes the opened partition.
 
 *
 
 * \note This function does not support extended partitions.
 
 *
 
 * \param[in] device_read A function pointer which is used to read from the disk.
 
 * \param[in] device_read_interval A function pointer which is used to read in constant intervals from the disk.
 
 * \param[in] device_write A function pointer which is used to write to the disk.
 
 * \param[in] device_write_interval A function pointer which is used to write a data stream to disk.
 
 * \param[in] index The index of the partition which should be opened, range 0 to 3.
 
 *                  A negative value is allowed as well. In this case, the partition opened is
 
 *                  not checked for existance, begins at offset zero, has a length of zero
 
 *                  and is of an unknown type. Use this in case you want to open the whole device
 
 *                  as a single partition (e.g. for "super floppy" use).
 
 * \returns 0 on failure, a partition descriptor on success.
 
 * \see partition_close
 
 */
 
struct partition_struct* partition_open(device_read_t device_read, device_read_interval_t device_read_interval, device_write_t device_write, device_write_interval_t device_write_interval, int8_t index)
 
{
 
    struct partition_struct* new_partition = 0;
 
    uint8_t buffer[0x10];
 

	
 
    if(!device_read || !device_read_interval || index >= 4)
 
        return 0;
 

	
 
    if(index >= 0)
 
    {
 
        /* read specified partition table index */
 
        if(!device_read(0x01be + index * 0x10, buffer, sizeof(buffer)))
 
            return 0;
 

	
 
        /* abort on empty partition entry */
 
        if(buffer[4] == 0x00)
 
            return 0;
 
    }
 

	
 
    /* allocate partition descriptor */
 
#if USE_DYNAMIC_MEMORY
 
    new_partition = malloc(sizeof(*new_partition));
 
    if(!new_partition)
 
        return 0;
 
#else
 
    new_partition = partition_handles;
 
    uint8_t i;
 
    for(i = 0; i < PARTITION_COUNT; ++i)
 
    {
 
        if(new_partition->type == PARTITION_TYPE_FREE)
 
            break;
 

	
 
        ++new_partition;
 
    }
 
    if(i >= PARTITION_COUNT)
 
        return 0;
 
#endif
 

	
 
    memset(new_partition, 0, sizeof(*new_partition));
 

	
 
    /* fill partition descriptor */
 
    new_partition->device_read = device_read;
 
    new_partition->device_read_interval = device_read_interval;
 
    new_partition->device_write = device_write;
 
    new_partition->device_write_interval = device_write_interval;
 

	
 
    if(index >= 0)
 
    {
 
        new_partition->type = buffer[4];
 
        new_partition->offset = read32(&buffer[8]);
 
        new_partition->length = read32(&buffer[12]);
 
    }
 
    else
 
    {
 
        new_partition->type = 0xff;
 
    }
 

	
 
    return new_partition;
 
}
 

	
 
/**
 
 * Closes a partition.
 
 *
 
 * This function destroys a partition descriptor which was
 
 * previously obtained from a call to partition_open().
 
 * When this function returns, the given descriptor will be
 
 * invalid.
 
 *
 
 * \param[in] partition The partition descriptor to destroy.
 
 * \returns 0 on failure, 1 on success.
 
 * \see partition_open
 
 */
 
uint8_t partition_close(struct partition_struct* partition)
 
{
 
    if(!partition)
 
        return 0;
 

	
 
    /* destroy partition descriptor */
 
#if USE_DYNAMIC_MEMORY
 
    free(partition);
 
#else
 
    partition->type = PARTITION_TYPE_FREE;
 
#endif
 

	
 
    return 1;
 
}
 

	
 
/**
 
 * @}
 
 */
 

	
master/master/lib/sd/partition.h
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#ifndef PARTITION_H
 
#define PARTITION_H
 

	
 
#include <stdint.h>
 
#include "sd_raw_config.h"
 
#include "partition_config.h"
 

	
 
#ifdef __cplusplus
 
extern "C"
 
{
 
#endif
 

	
 
/**
 
 * \addtogroup partition
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * Partition table header (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \author Roland Riegel
 
 */
 

	
 
/**
 
 * The partition table entry is not used.
 
 */
 
#define PARTITION_TYPE_FREE 0x00
 
/**
 
 * The partition contains a FAT12 filesystem.
 
 */
 
#define PARTITION_TYPE_FAT12 0x01
 
/**
 
 * The partition contains a FAT16 filesystem with 32MB maximum.
 
 */
 
#define PARTITION_TYPE_FAT16_32MB 0x04
 
/**
 
 * The partition is an extended partition with its own partition table.
 
 */
 
#define PARTITION_TYPE_EXTENDED 0x05
 
/**
 
 * The partition contains a FAT16 filesystem.
 
 */
 
#define PARTITION_TYPE_FAT16 0x06
 
/**
 
 * The partition contains a FAT32 filesystem.
 
 */
 
#define PARTITION_TYPE_FAT32 0x0b
 
/**
 
 * The partition contains a FAT32 filesystem with LBA.
 
 */
 
#define PARTITION_TYPE_FAT32_LBA 0x0c
 
/**
 
 * The partition contains a FAT16 filesystem with LBA.
 
 */
 
#define PARTITION_TYPE_FAT16_LBA 0x0e
 
/**
 
 * The partition is an extended partition with LBA.
 
 */
 
#define PARTITION_TYPE_EXTENDED_LBA 0x0f
 
/**
 
 * The partition has an unknown type.
 
 */
 
#define PARTITION_TYPE_UNKNOWN 0xff
 

	
 
/**
 
 * A function pointer used to read from the partition.
 
 *
 
 * \param[in] offset The offset on the device where to start reading.
 
 * \param[out] buffer The buffer into which to place the data.
 
 * \param[in] length The count of bytes to read.
 
 */
 
typedef uint8_t (*device_read_t)(offset_t offset, uint8_t* buffer, uintptr_t length);
 
/**
 
 * A function pointer passed to a \c device_read_interval_t.
 
 *
 
 * \param[in] buffer The buffer which contains the data just read.
 
 * \param[in] offset The offset from which the data in \c buffer was read.
 
 * \param[in] p An opaque pointer.
 
 * \see device_read_interval_t
 
 */
 
typedef uint8_t (*device_read_callback_t)(uint8_t* buffer, offset_t offset, void* p);
 
/**
 
 * A function pointer used to continuously read units of \c interval bytes
 
 * and call a callback function.
 
 *
 
 * This function starts reading at the specified offset. Every \c interval bytes,
 
 * it calls the callback function with the associated data buffer.
 
 *
 
 * By returning zero, the callback may stop reading.
 
 *
 
 * \param[in] offset Offset from which to start reading.
 
 * \param[in] buffer Pointer to a buffer which is at least interval bytes in size.
 
 * \param[in] interval Number of bytes to read before calling the callback function.
 
 * \param[in] length Number of bytes to read altogether.
 
 * \param[in] callback The function to call every interval bytes.
 
 * \param[in] p An opaque pointer directly passed to the callback function.
 
 * \returns 0 on failure, 1 on success
 
 * \see device_read_t
 
 */
 
typedef uint8_t (*device_read_interval_t)(offset_t offset, uint8_t* buffer, uintptr_t interval, uintptr_t length, device_read_callback_t callback, void* p);
 
/**
 
 * A function pointer used to write to the partition.
 
 *
 
 * \param[in] offset The offset on the device where to start writing.
 
 * \param[in] buffer The buffer which to write.
 
 * \param[in] length The count of bytes to write.
 
 */
 
typedef uint8_t (*device_write_t)(offset_t offset, const uint8_t* buffer, uintptr_t length);
 
/**
 
 * A function pointer passed to a \c device_write_interval_t.
 
 *
 
 * \param[in] buffer The buffer which receives the data to write.
 
 * \param[in] offset The offset to which the data in \c buffer will be written.
 
 * \param[in] p An opaque pointer.
 
 * \returns The number of bytes put into \c buffer
 
 * \see device_write_interval_t
 
 */
 
typedef uintptr_t (*device_write_callback_t)(uint8_t* buffer, offset_t offset, void* p);
 
/**
 
 * A function pointer used to continuously write a data stream obtained from
 
 * a callback function.
 
 *
 
 * This function starts writing at the specified offset. To obtain the
 
 * next bytes to write, it calls the callback function. The callback fills the
 
 * provided data buffer and returns the number of bytes it has put into the buffer.
 
 *
 
 * By returning zero, the callback may stop writing.
 
 *
 
 * \param[in] offset Offset where to start writing.
 
 * \param[in] buffer Pointer to a buffer which is used for the callback function.
 
 * \param[in] length Number of bytes to write in total. May be zero for endless writes.
 
 * \param[in] callback The function used to obtain the bytes to write.
 
 * \param[in] p An opaque pointer directly passed to the callback function.
 
 * \returns 0 on failure, 1 on success
 
 * \see device_write_t
 
 */
 
typedef uint8_t (*device_write_interval_t)(offset_t offset, uint8_t* buffer, uintptr_t length, device_write_callback_t callback, void* p);
 

	
 
/**
 
 * Describes a partition.
 
 */
 
struct partition_struct
 
{
 
    /**
 
     * The function which reads data from the partition.
 
     *
 
     * \note The offset given to this function is relative to the whole disk,
 
     *       not to the start of the partition.
 
     */
 
    device_read_t device_read;
 
    /**
 
     * The function which repeatedly reads a constant amount of data from the partition.
 
     *
 
     * \note The offset given to this function is relative to the whole disk,
 
     *       not to the start of the partition.
 
     */
 
    device_read_interval_t device_read_interval;
 
    /**
 
     * The function which writes data to the partition.
 
     *
 
     * \note The offset given to this function is relative to the whole disk,
 
     *       not to the start of the partition.
 
     */
 
    device_write_t device_write;
 
    /**
 
     * The function which repeatedly writes data to the partition.
 
     *
 
     * \note The offset given to this function is relative to the whole disk,
 
     *       not to the start of the partition.
 
     */
 
    device_write_interval_t device_write_interval;
 

	
 
    /**
 
     * The type of the partition.
 
     *
 
     * Compare this value to the PARTITION_TYPE_* constants.
 
     */
 
    uint8_t type;
 
    /**
 
     * The offset in blocks on the disk where this partition starts.
 
     */
 
    uint32_t offset;
 
    /**
 
     * The length in blocks of this partition.
 
     */
 
    uint32_t length;
 
};
 

	
 
struct partition_struct* partition_open(device_read_t device_read, device_read_interval_t device_read_interval, device_write_t device_write, device_write_interval_t device_write_interval, int8_t index);
 
uint8_t partition_close(struct partition_struct* partition);
 

	
 
/**
 
 * @}
 
 */
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
master/master/lib/sd/partition_config.h
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#ifndef PARTITION_CONFIG_H
 
#define PARTITION_CONFIG_H
 

	
 
#ifdef __cplusplus
 
extern "C"
 
{
 
#endif
 

	
 
/**
 
 * \addtogroup partition
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * Partition configuration (license: GPLv2 or LGPLv2.1)
 
 */
 

	
 
/**
 
 * \ingroup partition_config
 
 * Maximum number of partition handles.
 
 */
 
#define PARTITION_COUNT 1
 

	
 
/**
 
 * @}
 
 */
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
master/master/lib/sd/sd-reader_config.h
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#ifndef SD_READER_CONFIG_H
 
#define SD_READER_CONFIG_H
 

	
 
#ifdef __cplusplus
 
extern "C"
 
{
 
#endif
 

	
 
/**
 
 * \addtogroup config Sd-reader configuration
 
 *
 
 * @{
 
 */
 

	
 
/**
 
 * \file
 
 * Common sd-reader configuration used by all modules (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \note This file contains only configuration items relevant to
 
 * all sd-reader implementation files. For module specific configuration
 
 * options, please see the files fat_config.h, partition_config.h
 
 * and sd_raw_config.h.
 
 */
 

	
 
/**
 
 * Controls allocation of memory.
 
 *
 
 * Set to 1 to use malloc()/free() for allocation of structures
 
 * like file and directory handles, set to 0 to use pre-allocated
 
 * fixed-size handle arrays.
 
 */
 
#define USE_DYNAMIC_MEMORY 0
 

	
 
/**
 
 * @}
 
 */
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
master/master/lib/sd/sd_raw.c
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#include <string.h>
 
#include <avr/io.h>
 
#include "sd_raw.h"
 

	
 
/**
 
 * \addtogroup sd_raw MMC/SD/SDHC card raw access
 
 *
 
 * This module implements read and write access to MMC, SD
 
 * and SDHC cards. It serves as a low-level driver for the
 
 * higher level modules such as partition and file system
 
 * access.
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * MMC/SD/SDHC raw access implementation (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \author Roland Riegel
 
 */
 

	
 
/**
 
 * \addtogroup sd_raw_config MMC/SD configuration
 
 * Preprocessor defines to configure the MMC/SD support.
 
 */
 

	
 
/**
 
 * @}
 
 */
 

	
 
/* commands available in SPI mode */
 

	
 
/* CMD0: response R1 */
 
#define CMD_GO_IDLE_STATE 0x00
 
/* CMD1: response R1 */
 
#define CMD_SEND_OP_COND 0x01
 
/* CMD8: response R7 */
 
#define CMD_SEND_IF_COND 0x08
 
/* CMD9: response R1 */
 
#define CMD_SEND_CSD 0x09
 
/* CMD10: response R1 */
 
#define CMD_SEND_CID 0x0a
 
/* CMD12: response R1 */
 
#define CMD_STOP_TRANSMISSION 0x0c
 
/* CMD13: response R2 */
 
#define CMD_SEND_STATUS 0x0d
 
/* CMD16: arg0[31:0]: block length, response R1 */
 
#define CMD_SET_BLOCKLEN 0x10
 
/* CMD17: arg0[31:0]: data address, response R1 */
 
#define CMD_READ_SINGLE_BLOCK 0x11
 
/* CMD18: arg0[31:0]: data address, response R1 */
 
#define CMD_READ_MULTIPLE_BLOCK 0x12
 
/* CMD24: arg0[31:0]: data address, response R1 */
 
#define CMD_WRITE_SINGLE_BLOCK 0x18
 
/* CMD25: arg0[31:0]: data address, response R1 */
 
#define CMD_WRITE_MULTIPLE_BLOCK 0x19
 
/* CMD27: response R1 */
 
#define CMD_PROGRAM_CSD 0x1b
 
/* CMD28: arg0[31:0]: data address, response R1b */
 
#define CMD_SET_WRITE_PROT 0x1c
 
/* CMD29: arg0[31:0]: data address, response R1b */
 
#define CMD_CLR_WRITE_PROT 0x1d
 
/* CMD30: arg0[31:0]: write protect data address, response R1 */
 
#define CMD_SEND_WRITE_PROT 0x1e
 
/* CMD32: arg0[31:0]: data address, response R1 */
 
#define CMD_TAG_SECTOR_START 0x20
 
/* CMD33: arg0[31:0]: data address, response R1 */
 
#define CMD_TAG_SECTOR_END 0x21
 
/* CMD34: arg0[31:0]: data address, response R1 */
 
#define CMD_UNTAG_SECTOR 0x22
 
/* CMD35: arg0[31:0]: data address, response R1 */
 
#define CMD_TAG_ERASE_GROUP_START 0x23
 
/* CMD36: arg0[31:0]: data address, response R1 */
 
#define CMD_TAG_ERASE_GROUP_END 0x24
 
/* CMD37: arg0[31:0]: data address, response R1 */
 
#define CMD_UNTAG_ERASE_GROUP 0x25
 
/* CMD38: arg0[31:0]: stuff bits, response R1b */
 
#define CMD_ERASE 0x26
 
/* ACMD41: arg0[31:0]: OCR contents, response R1 */
 
#define CMD_SD_SEND_OP_COND 0x29
 
/* CMD42: arg0[31:0]: stuff bits, response R1b */
 
#define CMD_LOCK_UNLOCK 0x2a
 
/* CMD55: arg0[31:0]: stuff bits, response R1 */
 
#define CMD_APP 0x37
 
/* CMD58: arg0[31:0]: stuff bits, response R3 */
 
#define CMD_READ_OCR 0x3a
 
/* CMD59: arg0[31:1]: stuff bits, arg0[0:0]: crc option, response R1 */
 
#define CMD_CRC_ON_OFF 0x3b
 

	
 
/* command responses */
 
/* R1: size 1 byte */
 
#define R1_IDLE_STATE 0
 
#define R1_ERASE_RESET 1
 
#define R1_ILL_COMMAND 2
 
#define R1_COM_CRC_ERR 3
 
#define R1_ERASE_SEQ_ERR 4
 
#define R1_ADDR_ERR 5
 
#define R1_PARAM_ERR 6
 
/* R1b: equals R1, additional busy bytes */
 
/* R2: size 2 bytes */
 
#define R2_CARD_LOCKED 0
 
#define R2_WP_ERASE_SKIP 1
 
#define R2_ERR 2
 
#define R2_CARD_ERR 3
 
#define R2_CARD_ECC_FAIL 4
 
#define R2_WP_VIOLATION 5
 
#define R2_INVAL_ERASE 6
 
#define R2_OUT_OF_RANGE 7
 
#define R2_CSD_OVERWRITE 7
 
#define R2_IDLE_STATE (R1_IDLE_STATE + 8)
 
#define R2_ERASE_RESET (R1_ERASE_RESET + 8)
 
#define R2_ILL_COMMAND (R1_ILL_COMMAND + 8)
 
#define R2_COM_CRC_ERR (R1_COM_CRC_ERR + 8)
 
#define R2_ERASE_SEQ_ERR (R1_ERASE_SEQ_ERR + 8)
 
#define R2_ADDR_ERR (R1_ADDR_ERR + 8)
 
#define R2_PARAM_ERR (R1_PARAM_ERR + 8)
 
/* R3: size 5 bytes */
 
#define R3_OCR_MASK (0xffffffffUL)
 
#define R3_IDLE_STATE (R1_IDLE_STATE + 32)
 
#define R3_ERASE_RESET (R1_ERASE_RESET + 32)
 
#define R3_ILL_COMMAND (R1_ILL_COMMAND + 32)
 
#define R3_COM_CRC_ERR (R1_COM_CRC_ERR + 32)
 
#define R3_ERASE_SEQ_ERR (R1_ERASE_SEQ_ERR + 32)
 
#define R3_ADDR_ERR (R1_ADDR_ERR + 32)
 
#define R3_PARAM_ERR (R1_PARAM_ERR + 32)
 
/* Data Response: size 1 byte */
 
#define DR_STATUS_MASK 0x0e
 
#define DR_STATUS_ACCEPTED 0x05
 
#define DR_STATUS_CRC_ERR 0x0a
 
#define DR_STATUS_WRITE_ERR 0x0c
 

	
 
/* status bits for card types */
 
#define SD_RAW_SPEC_1 0
 
#define SD_RAW_SPEC_2 1
 
#define SD_RAW_SPEC_SDHC 2
 

	
 
#if !SD_RAW_SAVE_RAM
 
/* static data buffer for acceleration */
 
static uint8_t raw_block[512];
 
/* offset where the data within raw_block lies on the card */
 
static offset_t raw_block_address;
 
#if SD_RAW_WRITE_BUFFERING
 
/* flag to remember if raw_block was written to the card */
 
static uint8_t raw_block_written;
 
#endif
 
#endif
 

	
 
/* card type state */
 
static uint8_t sd_raw_card_type;
 

	
 
/* private helper functions */
 
static void sd_raw_send_byte(uint8_t b);
 
static uint8_t sd_raw_rec_byte();
 
static uint8_t sd_raw_send_command(uint8_t command, uint32_t arg);
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Initializes memory card communication.
 
 *
 
 * \returns 0 on failure, 1 on success.
 
 */
 
uint8_t sd_raw_init()
 
{
 
    /* enable inputs for reading card status */
 
    configure_pin_available();
 
    configure_pin_locked();
 

	
 
    /* enable outputs for MOSI, SCK, SS, input for MISO */
 
    configure_pin_mosi();
 
    configure_pin_sck();
 
    configure_pin_ss();
 
    configure_pin_miso();
 

	
 
    unselect_card();
 

	
 
    /* initialize SPI with lowest frequency; max. 400kHz during identification mode of card */
 
    SPCR = (0 << SPIE) | /* SPI Interrupt Enable */
 
           (1 << SPE)  | /* SPI Enable */
 
           (0 << DORD) | /* Data Order: MSB first */
 
           (1 << MSTR) | /* Master mode */
 
           (0 << CPOL) | /* Clock Polarity: SCK low when idle */
 
           (0 << CPHA) | /* Clock Phase: sample on rising SCK edge */
 
           (1 << SPR1) | /* Clock Frequency: f_OSC / 128 */
 
           (1 << SPR0);
 
    SPSR &= ~(1 << SPI2X); /* No doubled clock frequency */
 

	
 
    /* initialization procedure */
 
    sd_raw_card_type = 0;
 
    
 
    if(!sd_raw_available())
 
        return 0;
 

	
 
    /* card needs 74 cycles minimum to start up */
 
    for(uint8_t i = 0; i < 10; ++i)
 
    {
 
        /* wait 8 clock cycles */
 
        sd_raw_rec_byte();
 
    }
 

	
 
    /* address card */
 
    select_card();
 

	
 
    /* reset card */
 
    uint8_t response;
 
    for(uint16_t i = 0; ; ++i)
 
    {
 
        response = sd_raw_send_command(CMD_GO_IDLE_STATE, 0);
 
        if(response == (1 << R1_IDLE_STATE))
 
            break;
 

	
 
        if(i == 0x1ff)
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 
    }
 

	
 
#if SD_RAW_SDHC
 
    /* check for version of SD card specification */
 
    response = sd_raw_send_command(CMD_SEND_IF_COND, 0x100 /* 2.7V - 3.6V */ | 0xaa /* test pattern */);
 
    if((response & (1 << R1_ILL_COMMAND)) == 0)
 
    {
 
        sd_raw_rec_byte();
 
        sd_raw_rec_byte();
 
        if((sd_raw_rec_byte() & 0x01) == 0)
 
            return 0; /* card operation voltage range doesn't match */
 
        if(sd_raw_rec_byte() != 0xaa)
 
            return 0; /* wrong test pattern */
 

	
 
        /* card conforms to SD 2 card specification */
 
        sd_raw_card_type |= (1 << SD_RAW_SPEC_2);
 
    }
 
    else
 
#endif
 
    {
 
        /* determine SD/MMC card type */
 
        sd_raw_send_command(CMD_APP, 0);
 
        response = sd_raw_send_command(CMD_SD_SEND_OP_COND, 0);
 
        if((response & (1 << R1_ILL_COMMAND)) == 0)
 
        {
 
            /* card conforms to SD 1 card specification */
 
            sd_raw_card_type |= (1 << SD_RAW_SPEC_1);
 
        }
 
        else
 
        {
 
            /* MMC card */
 
        }
 
    }
 

	
 
    /* wait for card to get ready */
 
    for(uint16_t i = 0; ; ++i)
 
    {
 
        if(sd_raw_card_type & ((1 << SD_RAW_SPEC_1) | (1 << SD_RAW_SPEC_2)))
 
        {
 
            uint32_t arg = 0;
 
#if SD_RAW_SDHC
 
            if(sd_raw_card_type & (1 << SD_RAW_SPEC_2))
 
                arg = 0x40000000;
 
#endif
 
            sd_raw_send_command(CMD_APP, 0);
 
            response = sd_raw_send_command(CMD_SD_SEND_OP_COND, arg);
 
        }
 
        else
 
        {
 
            response = sd_raw_send_command(CMD_SEND_OP_COND, 0);
 
        }
 

	
 
        if((response & (1 << R1_IDLE_STATE)) == 0)
 
            break;
 

	
 
        if(i == 0x7fff)
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 
    }
 

	
 
#if SD_RAW_SDHC
 
    if(sd_raw_card_type & (1 << SD_RAW_SPEC_2))
 
    {
 
        if(sd_raw_send_command(CMD_READ_OCR, 0))
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 

	
 
        if(sd_raw_rec_byte() & 0x40)
 
            sd_raw_card_type |= (1 << SD_RAW_SPEC_SDHC);
 

	
 
        sd_raw_rec_byte();
 
        sd_raw_rec_byte();
 
        sd_raw_rec_byte();
 
    }
 
#endif
 

	
 
    /* set block size to 512 bytes */
 
    if(sd_raw_send_command(CMD_SET_BLOCKLEN, 512))
 
    {
 
        unselect_card();
 
        return 0;
 
    }
 

	
 
    /* deaddress card */
 
    unselect_card();
 

	
 
    /* switch to highest SPI frequency possible */
 
    SPCR &= ~((1 << SPR1) | (1 << SPR0)); /* Clock Frequency: f_OSC / 4 */
 
    SPSR |= (1 << SPI2X); /* Doubled Clock Frequency: f_OSC / 2 */
 

	
 
#if !SD_RAW_SAVE_RAM
 
    /* the first block is likely to be accessed first, so precache it here */
 
    raw_block_address = (offset_t) -1;
 
#if SD_RAW_WRITE_BUFFERING
 
    raw_block_written = 1;
 
#endif
 
    if(!sd_raw_read(0, raw_block, sizeof(raw_block)))
 
        return 0;
 
#endif
 

	
 
    return 1;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Checks wether a memory card is located in the slot.
 
 *
 
 * \returns 1 if the card is available, 0 if it is not.
 
 */
 
uint8_t sd_raw_available()
 
{
 
    return get_pin_available() == 0x00;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Checks wether the memory card is locked for write access.
 
 *
 
 * \returns 1 if the card is locked, 0 if it is not.
 
 */
 
uint8_t sd_raw_locked()
 
{
 
    return get_pin_locked() == 0x00;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Sends a raw byte to the memory card.
 
 *
 
 * \param[in] b The byte to sent.
 
 * \see sd_raw_rec_byte
 
 */
 
void sd_raw_send_byte(uint8_t b)
 
{
 
    SPDR = b;
 
    /* wait for byte to be shifted out */
 
    while(!(SPSR & (1 << SPIF)));
 
    SPSR &= ~(1 << SPIF);
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Receives a raw byte from the memory card.
 
 *
 
 * \returns The byte which should be read.
 
 * \see sd_raw_send_byte
 
 */
 
uint8_t sd_raw_rec_byte()
 
{
 
    /* send dummy data for receiving some */
 
    SPDR = 0xff;
 
    while(!(SPSR & (1 << SPIF)));
 
    SPSR &= ~(1 << SPIF);
 

	
 
    return SPDR;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Send a command to the memory card which responses with a R1 response (and possibly others).
 
 *
 
 * \param[in] command The command to send.
 
 * \param[in] arg The argument for command.
 
 * \returns The command answer.
 
 */
 
uint8_t sd_raw_send_command(uint8_t command, uint32_t arg)
 
{
 
    uint8_t response;
 

	
 
    /* wait some clock cycles */
 
    sd_raw_rec_byte();
 

	
 
    /* send command via SPI */
 
    sd_raw_send_byte(0x40 | command);
 
    sd_raw_send_byte((arg >> 24) & 0xff);
 
    sd_raw_send_byte((arg >> 16) & 0xff);
 
    sd_raw_send_byte((arg >> 8) & 0xff);
 
    sd_raw_send_byte((arg >> 0) & 0xff);
 
    switch(command)
 
    {
 
        case CMD_GO_IDLE_STATE:
 
           sd_raw_send_byte(0x95);
 
           break;
 
        case CMD_SEND_IF_COND:
 
           sd_raw_send_byte(0x87);
 
           break;
 
        default:
 
           sd_raw_send_byte(0xff);
 
           break;
 
    }
 
    
 
    /* receive response */
 
    for(uint8_t i = 0; i < 10; ++i)
 
    {
 
        response = sd_raw_rec_byte();
 
        if(response != 0xff)
 
            break;
 
    }
 

	
 
    return response;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Reads raw data from the card.
 
 *
 
 * \param[in] offset The offset from which to read.
 
 * \param[out] buffer The buffer into which to write the data.
 
 * \param[in] length The number of bytes to read.
 
 * \returns 0 on failure, 1 on success.
 
 * \see sd_raw_read_interval, sd_raw_write, sd_raw_write_interval
 
 */
 
uint8_t sd_raw_read(offset_t offset, uint8_t* buffer, uintptr_t length)
 
{
 
    offset_t block_address;
 
    uint16_t block_offset;
 
    uint16_t read_length;
 
    while(length > 0)
 
    {
 
        /* determine byte count to read at once */
 
        block_offset = offset & 0x01ff;
 
        block_address = offset - block_offset;
 
        read_length = 512 - block_offset; /* read up to block border */
 
        if(read_length > length)
 
            read_length = length;
 
        
 
#if !SD_RAW_SAVE_RAM
 
        /* check if the requested data is cached */
 
        if(block_address != raw_block_address)
 
#endif
 
        {
 
#if SD_RAW_WRITE_BUFFERING
 
            if(!sd_raw_sync())
 
                return 0;
 
#endif
 

	
 
            /* address card */
 
            select_card();
 

	
 
            /* send single block request */
 
#if SD_RAW_SDHC
 
            if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? block_address / 512 : block_address)))
 
#else
 
            if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, block_address))
 
#endif
 
            {
 
                unselect_card();
 
                return 0;
 
            }
 

	
 
            /* wait for data block (start byte 0xfe) */
 
            while(sd_raw_rec_byte() != 0xfe);
 

	
 
#if SD_RAW_SAVE_RAM
 
            /* read byte block */
 
            uint16_t read_to = block_offset + read_length;
 
            for(uint16_t i = 0; i < 512; ++i)
 
            {
 
                uint8_t b = sd_raw_rec_byte();
 
                if(i >= block_offset && i < read_to)
 
                    *buffer++ = b;
 
            }
 
#else
 
            /* read byte block */
 
            uint8_t* cache = raw_block;
 
            for(uint16_t i = 0; i < 512; ++i)
 
                *cache++ = sd_raw_rec_byte();
 
            raw_block_address = block_address;
 

	
 
            memcpy(buffer, raw_block + block_offset, read_length);
 
            buffer += read_length;
 
#endif
 
            
 
            /* read crc16 */
 
            sd_raw_rec_byte();
 
            sd_raw_rec_byte();
 
            
 
            /* deaddress card */
 
            unselect_card();
 

	
 
            /* let card some time to finish */
 
            sd_raw_rec_byte();
 
        }
 
#if !SD_RAW_SAVE_RAM
 
        else
 
        {
 
            /* use cached data */
 
            memcpy(buffer, raw_block + block_offset, read_length);
 
            buffer += read_length;
 
        }
 
#endif
 

	
 
        length -= read_length;
 
        offset += read_length;
 
    }
 

	
 
    return 1;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Continuously reads units of \c interval bytes and calls a callback function.
 
 *
 
 * This function starts reading at the specified offset. Every \c interval bytes,
 
 * it calls the callback function with the associated data buffer.
 
 *
 
 * By returning zero, the callback may stop reading.
 
 *
 
 * \note Within the callback function, you can not start another read or
 
 *       write operation.
 
 * \note This function only works if the following conditions are met:
 
 *       - (offset - (offset % 512)) % interval == 0
 
 *       - length % interval == 0
 
 *
 
 * \param[in] offset Offset from which to start reading.
 
 * \param[in] buffer Pointer to a buffer which is at least interval bytes in size.
 
 * \param[in] interval Number of bytes to read before calling the callback function.
 
 * \param[in] length Number of bytes to read altogether.
 
 * \param[in] callback The function to call every interval bytes.
 
 * \param[in] p An opaque pointer directly passed to the callback function.
 
 * \returns 0 on failure, 1 on success
 
 * \see sd_raw_write_interval, sd_raw_read, sd_raw_write
 
 */
 
uint8_t sd_raw_read_interval(offset_t offset, uint8_t* buffer, uintptr_t interval, uintptr_t length, sd_raw_read_interval_handler_t callback, void* p)
 
{
 
    if(!buffer || interval == 0 || length < interval || !callback)
 
        return 0;
 

	
 
#if !SD_RAW_SAVE_RAM
 
    while(length >= interval)
 
    {
 
        /* as reading is now buffered, we directly
 
         * hand over the request to sd_raw_read()
 
         */
 
        if(!sd_raw_read(offset, buffer, interval))
 
            return 0;
 
        if(!callback(buffer, offset, p))
 
            break;
 
        offset += interval;
 
        length -= interval;
 
    }
 

	
 
    return 1;
 
#else
 
    /* address card */
 
    select_card();
 

	
 
    uint16_t block_offset;
 
    uint16_t read_length;
 
    uint8_t* buffer_cur;
 
    uint8_t finished = 0;
 
    do
 
    {
 
        /* determine byte count to read at once */
 
        block_offset = offset & 0x01ff;
 
        read_length = 512 - block_offset;
 
        
 
        /* send single block request */
 
#if SD_RAW_SDHC
 
        if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? offset / 512 : offset - block_offset)))
 
#else
 
        if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, offset - block_offset))
 
#endif
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 

	
 
        /* wait for data block (start byte 0xfe) */
 
        while(sd_raw_rec_byte() != 0xfe);
 

	
 
        /* read up to the data of interest */
 
        for(uint16_t i = 0; i < block_offset; ++i)
 
            sd_raw_rec_byte();
 

	
 
        /* read interval bytes of data and execute the callback */
 
        do
 
        {
 
            if(read_length < interval || length < interval)
 
                break;
 

	
 
            buffer_cur = buffer;
 
            for(uint16_t i = 0; i < interval; ++i)
 
                *buffer_cur++ = sd_raw_rec_byte();
 

	
 
            if(!callback(buffer, offset + (512 - read_length), p))
 
            {
 
                finished = 1;
 
                break;
 
            }
 

	
 
            read_length -= interval;
 
            length -= interval;
 

	
 
        } while(read_length > 0 && length > 0);
 
        
 
        /* read rest of data block */
 
        while(read_length-- > 0)
 
            sd_raw_rec_byte();
 
        
 
        /* read crc16 */
 
        sd_raw_rec_byte();
 
        sd_raw_rec_byte();
 

	
 
        if(length < interval)
 
            break;
 

	
 
        offset = offset - block_offset + 512;
 

	
 
    } while(!finished);
 
    
 
    /* deaddress card */
 
    unselect_card();
 

	
 
    /* let card some time to finish */
 
    sd_raw_rec_byte();
 

	
 
    return 1;
 
#endif
 
}
 

	
 
#if DOXYGEN || SD_RAW_WRITE_SUPPORT
 
/**
 
 * \ingroup sd_raw
 
 * Writes raw data to the card.
 
 *
 
 * \note If write buffering is enabled, you might have to
 
 *       call sd_raw_sync() before disconnecting the card
 
 *       to ensure all remaining data has been written.
 
 *
 
 * \param[in] offset The offset where to start writing.
 
 * \param[in] buffer The buffer containing the data to be written.
 
 * \param[in] length The number of bytes to write.
 
 * \returns 0 on failure, 1 on success.
 
 * \see sd_raw_write_interval, sd_raw_read, sd_raw_read_interval
 
 */
 
uint8_t sd_raw_write(offset_t offset, const uint8_t* buffer, uintptr_t length)
 
{
 
    if(sd_raw_locked())
 
        return 0;
 

	
 
    offset_t block_address;
 
    uint16_t block_offset;
 
    uint16_t write_length;
 
    while(length > 0)
 
    {
 
        /* determine byte count to write at once */
 
        block_offset = offset & 0x01ff;
 
        block_address = offset - block_offset;
 
        write_length = 512 - block_offset; /* write up to block border */
 
        if(write_length > length)
 
            write_length = length;
 
        
 
        /* Merge the data to write with the content of the block.
 
         * Use the cached block if available.
 
         */
 
        if(block_address != raw_block_address)
 
        {
 
#if SD_RAW_WRITE_BUFFERING
 
            if(!sd_raw_sync())
 
                return 0;
 
#endif
 

	
 
            if(block_offset || write_length < 512)
 
            {
 
                if(!sd_raw_read(block_address, raw_block, sizeof(raw_block)))
 
                    return 0;
 
            }
 
            raw_block_address = block_address;
 
        }
 

	
 
        if(buffer != raw_block)
 
        {
 
            memcpy(raw_block + block_offset, buffer, write_length);
 

	
 
#if SD_RAW_WRITE_BUFFERING
 
            raw_block_written = 0;
 

	
 
            if(length == write_length)
 
                return 1;
 
#endif
 
        }
 

	
 
        /* address card */
 
        select_card();
 

	
 
        /* send single block request */
 
#if SD_RAW_SDHC
 
        if(sd_raw_send_command(CMD_WRITE_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? block_address / 512 : block_address)))
 
#else
 
        if(sd_raw_send_command(CMD_WRITE_SINGLE_BLOCK, block_address))
 
#endif
 
        {
 
            unselect_card();
 
            return 0;
 
        }
 

	
 
        /* send start byte */
 
        sd_raw_send_byte(0xfe);
 

	
 
        /* write byte block */
 
        uint8_t* cache = raw_block;
 
        for(uint16_t i = 0; i < 512; ++i)
 
            sd_raw_send_byte(*cache++);
 

	
 
        /* write dummy crc16 */
 
        sd_raw_send_byte(0xff);
 
        sd_raw_send_byte(0xff);
 

	
 
        /* wait while card is busy */
 
        while(sd_raw_rec_byte() != 0xff);
 
        sd_raw_rec_byte();
 

	
 
        /* deaddress card */
 
        unselect_card();
 

	
 
        buffer += write_length;
 
        offset += write_length;
 
        length -= write_length;
 

	
 
#if SD_RAW_WRITE_BUFFERING
 
        raw_block_written = 1;
 
#endif
 
    }
 

	
 
    return 1;
 
}
 
#endif
 

	
 
#if DOXYGEN || SD_RAW_WRITE_SUPPORT
 
/**
 
 * \ingroup sd_raw
 
 * Writes a continuous data stream obtained from a callback function.
 
 *
 
 * This function starts writing at the specified offset. To obtain the
 
 * next bytes to write, it calls the callback function. The callback fills the
 
 * provided data buffer and returns the number of bytes it has put into the buffer.
 
 *
 
 * By returning zero, the callback may stop writing.
 
 *
 
 * \param[in] offset Offset where to start writing.
 
 * \param[in] buffer Pointer to a buffer which is used for the callback function.
 
 * \param[in] length Number of bytes to write in total. May be zero for endless writes.
 
 * \param[in] callback The function used to obtain the bytes to write.
 
 * \param[in] p An opaque pointer directly passed to the callback function.
 
 * \returns 0 on failure, 1 on success
 
 * \see sd_raw_read_interval, sd_raw_write, sd_raw_read
 
 */
 
uint8_t sd_raw_write_interval(offset_t offset, uint8_t* buffer, uintptr_t length, sd_raw_write_interval_handler_t callback, void* p)
 
{
 
#if SD_RAW_SAVE_RAM
 
    #error "SD_RAW_WRITE_SUPPORT is not supported together with SD_RAW_SAVE_RAM"
 
#endif
 

	
 
    if(!buffer || !callback)
 
        return 0;
 

	
 
    uint8_t endless = (length == 0);
 
    while(endless || length > 0)
 
    {
 
        uint16_t bytes_to_write = callback(buffer, offset, p);
 
        if(!bytes_to_write)
 
            break;
 
        if(!endless && bytes_to_write > length)
 
            return 0;
 

	
 
        /* as writing is always buffered, we directly
 
         * hand over the request to sd_raw_write()
 
         */
 
        if(!sd_raw_write(offset, buffer, bytes_to_write))
 
            return 0;
 

	
 
        offset += bytes_to_write;
 
        length -= bytes_to_write;
 
    }
 

	
 
    return 1;
 
}
 
#endif
 

	
 
#if DOXYGEN || SD_RAW_WRITE_SUPPORT
 
/**
 
 * \ingroup sd_raw
 
 * Writes the write buffer's content to the card.
 
 *
 
 * \note When write buffering is enabled, you should
 
 *       call this function before disconnecting the
 
 *       card to ensure all remaining data has been
 
 *       written.
 
 *
 
 * \returns 0 on failure, 1 on success.
 
 * \see sd_raw_write
 
 */
 
uint8_t sd_raw_sync()
 
{
 
#if SD_RAW_WRITE_BUFFERING
 
    if(raw_block_written)
 
        return 1;
 
    if(!sd_raw_write(raw_block_address, raw_block, sizeof(raw_block)))
 
        return 0;
 
    raw_block_written = 1;
 
#endif
 
    return 1;
 
}
 
#endif
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Reads informational data from the card.
 
 *
 
 * This function reads and returns the card's registers
 
 * containing manufacturing and status information.
 
 *
 
 * \note: The information retrieved by this function is
 
 *        not required in any way to operate on the card,
 
 *        but it might be nice to display some of the data
 
 *        to the user.
 
 *
 
 * \param[in] info A pointer to the structure into which to save the information.
 
 * \returns 0 on failure, 1 on success.
 
 */
 
uint8_t sd_raw_get_info(struct sd_raw_info* info)
 
{
 
    if(!info || !sd_raw_available())
 
        return 0;
 

	
 
    memset(info, 0, sizeof(*info));
 

	
 
    select_card();
 

	
 
    /* read cid register */
 
    if(sd_raw_send_command(CMD_SEND_CID, 0))
 
    {
 
        unselect_card();
 
        return 0;
 
    }
 
    while(sd_raw_rec_byte() != 0xfe);
 
    for(uint8_t i = 0; i < 18; ++i)
 
    {
 
        uint8_t b = sd_raw_rec_byte();
 

	
 
        switch(i)
 
        {
 
            case 0:
 
                info->manufacturer = b;
 
                break;
 
            case 1:
 
            case 2:
 
                info->oem[i - 1] = b;
 
                break;
 
            case 3:
 
            case 4:
 
            case 5:
 
            case 6:
 
            case 7:
 
                info->product[i - 3] = b;
 
                break;
 
            case 8:
 
                info->revision = b;
 
                break;
 
            case 9:
 
            case 10:
 
            case 11:
 
            case 12:
 
                info->serial |= (uint32_t) b << ((12 - i) * 8);
 
                break;
 
            case 13:
 
                info->manufacturing_year = b << 4;
 
                break;
 
            case 14:
 
                info->manufacturing_year |= b >> 4;
 
                info->manufacturing_month = b & 0x0f;
 
                break;
 
        }
 
    }
 

	
 
    /* read csd register */
 
    uint8_t csd_read_bl_len = 0;
 
    uint8_t csd_c_size_mult = 0;
 
#if SD_RAW_SDHC
 
    uint16_t csd_c_size = 0;
 
#else
 
    uint32_t csd_c_size = 0;
 
#endif
 
    uint8_t csd_structure = 0;
 
    if(sd_raw_send_command(CMD_SEND_CSD, 0))
 
    {
 
        unselect_card();
 
        return 0;
 
    }
 
    while(sd_raw_rec_byte() != 0xfe);
 
    for(uint8_t i = 0; i < 18; ++i)
 
    {
 
        uint8_t b = sd_raw_rec_byte();
 

	
 
        if(i == 0)
 
        {
 
            csd_structure = b >> 6;
 
        }
 
        else if(i == 14)
 
        {
 
            if(b & 0x40)
 
                info->flag_copy = 1;
 
            if(b & 0x20)
 
                info->flag_write_protect = 1;
 
            if(b & 0x10)
 
                info->flag_write_protect_temp = 1;
 
            info->format = (b & 0x0c) >> 2;
 
        }
 
        else
 
        {
 
#if SD_RAW_SDHC
 
            if(csd_structure == 0x01)
 
            {
 
                switch(i)
 
                {
 
                    case 7:
 
                        b &= 0x3f;
 
                    case 8:
 
                    case 9:
 
                        csd_c_size <<= 8;
 
                        csd_c_size |= b;
 
                        break;
 
                }
 
                if(i == 9)
 
                {
 
                    ++csd_c_size;
 
                    info->capacity = (offset_t) csd_c_size * 512 * 1024;
 
                }
 
            }
 
            else if(csd_structure == 0x00)
 
#endif
 
            {
 
                switch(i)
 
                {
 
                    case 5:
 
                        csd_read_bl_len = b & 0x0f;
 
                        break;
 
                    case 6:
 
                        csd_c_size = b & 0x03;
 
                        csd_c_size <<= 8;
 
                        break;
 
                    case 7:
 
                        csd_c_size |= b;
 
                        csd_c_size <<= 2;
 
                        break;
 
                    case 8:
 
                        csd_c_size |= b >> 6;
 
                        ++csd_c_size;
 
                        break;
 
                    case 9:
 
                        csd_c_size_mult = b & 0x03;
 
                        csd_c_size_mult <<= 1;
 
                        break;
 
                    case 10:
 
                        csd_c_size_mult |= b >> 7;
 

	
 
                        info->capacity = (uint32_t) csd_c_size << (csd_c_size_mult + csd_read_bl_len + 2);
 
                        break;
 
                }
 
            }
 
        }
 
    }
 

	
 
    unselect_card();
 

	
 
    return 1;
 
}
 

	
master/master/lib/sd/sd_raw.h
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#ifndef SD_RAW_H
 
#define SD_RAW_H
 

	
 
#include <stdint.h>
 
#include "sd_raw_config.h"
 

	
 
#ifdef __cplusplus
 
extern "C"
 
{
 
#endif
 

	
 
/**
 
 * \addtogroup sd_raw
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * MMC/SD/SDHC raw access header (license: GPLv2 or LGPLv2.1)
 
 *
 
 * \author Roland Riegel
 
 */
 

	
 
/**
 
 * The card's layout is harddisk-like, which means it contains
 
 * a master boot record with a partition table.
 
 */
 
#define SD_RAW_FORMAT_HARDDISK 0
 
/**
 
 * The card contains a single filesystem and no partition table.
 
 */
 
#define SD_RAW_FORMAT_SUPERFLOPPY 1
 
/**
 
 * The card's layout follows the Universal File Format.
 
 */
 
#define SD_RAW_FORMAT_UNIVERSAL 2
 
/**
 
 * The card's layout is unknown.
 
 */
 
#define SD_RAW_FORMAT_UNKNOWN 3
 

	
 
/**
 
 * This struct is used by sd_raw_get_info() to return
 
 * manufacturing and status information of the card.
 
 */
 
struct sd_raw_info
 
{
 
    /**
 
     * A manufacturer code globally assigned by the SD card organization.
 
     */
 
    uint8_t manufacturer;
 
    /**
 
     * A string describing the card's OEM or content, globally assigned by the SD card organization.
 
     */
 
    uint8_t oem[3];
 
    /**
 
     * A product name.
 
     */
 
    uint8_t product[6];
 
    /**
 
     * The card's revision, coded in packed BCD.
 
     *
 
     * For example, the revision value \c 0x32 means "3.2".
 
     */
 
    uint8_t revision;
 
    /**
 
     * A serial number assigned by the manufacturer.
 
     */
 
    uint32_t serial;
 
    /**
 
     * The year of manufacturing.
 
     *
 
     * A value of zero means year 2000.
 
     */
 
    uint8_t manufacturing_year;
 
    /**
 
     * The month of manufacturing.
 
     */
 
    uint8_t manufacturing_month;
 
    /**
 
     * The card's total capacity in bytes.
 
     */
 
    offset_t capacity;
 
    /**
 
     * Defines wether the card's content is original or copied.
 
     *
 
     * A value of \c 0 means original, \c 1 means copied.
 
     */
 
    uint8_t flag_copy;
 
    /**
 
     * Defines wether the card's content is write-protected.
 
     *
 
     * \note This is an internal flag and does not represent the
 
     *       state of the card's mechanical write-protect switch.
 
     */
 
    uint8_t flag_write_protect;
 
    /**
 
     * Defines wether the card's content is temporarily write-protected.
 
     *
 
     * \note This is an internal flag and does not represent the
 
     *       state of the card's mechanical write-protect switch.
 
     */
 
    uint8_t flag_write_protect_temp;
 
    /**
 
     * The card's data layout.
 
     *
 
     * See the \c SD_RAW_FORMAT_* constants for details.
 
     *
 
     * \note This value is not guaranteed to match reality.
 
     */
 
    uint8_t format;
 
};
 

	
 
typedef uint8_t (*sd_raw_read_interval_handler_t)(uint8_t* buffer, offset_t offset, void* p);
 
typedef uintptr_t (*sd_raw_write_interval_handler_t)(uint8_t* buffer, offset_t offset, void* p);
 

	
 
uint8_t sd_raw_init();
 
uint8_t sd_raw_available();
 
uint8_t sd_raw_locked();
 

	
 
uint8_t sd_raw_read(offset_t offset, uint8_t* buffer, uintptr_t length);
 
uint8_t sd_raw_read_interval(offset_t offset, uint8_t* buffer, uintptr_t interval, uintptr_t length, sd_raw_read_interval_handler_t callback, void* p);
 
uint8_t sd_raw_write(offset_t offset, const uint8_t* buffer, uintptr_t length);
 
uint8_t sd_raw_write_interval(offset_t offset, uint8_t* buffer, uintptr_t length, sd_raw_write_interval_handler_t callback, void* p);
 
uint8_t sd_raw_sync();
 

	
 
uint8_t sd_raw_get_info(struct sd_raw_info* info);
 

	
 
/**
 
 * @}
 
 */
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
master/master/lib/sd/sd_raw_config.h
Show inline comments
 
new file 100644
 

	
 
/*
 
 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
 
 *
 
 * This file is free software; you can redistribute it and/or modify
 
 * it under the terms of either the GNU General Public License version 2
 
 * or the GNU Lesser General Public License version 2.1, both as
 
 * published by the Free Software Foundation.
 
 */
 

	
 
#ifndef SD_RAW_CONFIG_H
 
#define SD_RAW_CONFIG_H
 

	
 
#include <stdint.h>
 

	
 
#ifdef __cplusplus
 
extern "C"
 
{
 
#endif
 

	
 
/**
 
 * \addtogroup sd_raw
 
 *
 
 * @{
 
 */
 
/**
 
 * \file
 
 * MMC/SD support configuration (license: GPLv2 or LGPLv2.1)
 
 */
 

	
 
/**
 
 * \ingroup sd_raw_config
 
 * Controls MMC/SD write support.
 
 *
 
 * Set to 1 to enable MMC/SD write support, set to 0 to disable it.
 
 */
 
#define SD_RAW_WRITE_SUPPORT 1
 

	
 
/**
 
 * \ingroup sd_raw_config
 
 * Controls MMC/SD write buffering.
 
 *
 
 * Set to 1 to buffer write accesses, set to 0 to disable it.
 
 *
 
 * \note This option has no effect when SD_RAW_WRITE_SUPPORT is 0.
 
 */
 
#define SD_RAW_WRITE_BUFFERING 1
 

	
 
/**
 
 * \ingroup sd_raw_config
 
 * Controls MMC/SD access buffering.
 
 * 
 
 * Set to 1 to save static RAM, but be aware that you will
 
 * lose performance.
 
 *
 
 * \note When SD_RAW_WRITE_SUPPORT is 1, SD_RAW_SAVE_RAM will
 
 *       be reset to 0.
 
 */
 
#define SD_RAW_SAVE_RAM 1
 

	
 
/**
 
 * \ingroup sd_raw_config
 
 * Controls support for SDHC cards.
 
 *
 
 * Set to 1 to support so-called SDHC memory cards, i.e. SD
 
 * cards with more than 2 gigabytes of memory.
 
 */
 
#define SD_RAW_SDHC 0
 

	
 
/**
 
 * @}
 
 */
 

	
 
/* defines for customisation of sd/mmc port access */
 
#if defined(__AVR_ATmega8__) || \
 
    defined(__AVR_ATmega48__) || \
 
    defined(__AVR_ATmega48P__) || \
 
    defined(__AVR_ATmega88__) || \
 
    defined(__AVR_ATmega88P__) || \
 
    defined(__AVR_ATmega168__) || \
 
    defined(__AVR_ATmega168P__) || \
 
    defined(__AVR_ATmega328P__)
 
    #define configure_pin_mosi() DDRB |= (1 << DDB3)
 
    #define configure_pin_sck() DDRB |= (1 << DDB5)
 
    #define configure_pin_ss() DDRB |= (1 << DDB2)
 
    #define configure_pin_miso() DDRB &= ~(1 << DDB4)
 

	
 
    #define select_card() PORTB &= ~(1 << PORTB2)
 
    #define unselect_card() PORTB |= (1 << PORTB2)
 
#elif defined(__AVR_ATmega16__) || \
 
      defined(__AVR_ATmega32__)
 
    #define configure_pin_mosi() DDRB |= (1 << DDB5)
 
    #define configure_pin_sck() DDRB |= (1 << DDB7)
 
    #define configure_pin_ss() DDRB |= (1 << DDB4)
 
    #define configure_pin_miso() DDRB &= ~(1 << DDB6)
 

	
 
    #define select_card() PORTB &= ~(1 << PORTB4)
 
    #define unselect_card() PORTB |= (1 << PORTB4)
 
#elif defined(__AVR_ATmega64__) || \
 
      defined(__AVR_ATmega128__) || \
 
      defined(__AVR_ATmega169__)
 
    #define configure_pin_mosi() DDRB |= (1 << DDB2)
 
    #define configure_pin_sck() DDRB |= (1 << DDB1)
 
    #define configure_pin_ss() DDRB |= (1 << DDB0)
 
    #define configure_pin_miso() DDRB &= ~(1 << DDB3)
 

	
 
    #define select_card() PORTB &= ~(1 << PORTB0)
 
    #define unselect_card() PORTB |= (1 << PORTB0)
 
#else
 
    //#error "no sd/mmc pin mapping available!" //commented out due to build error ??
 
#endif
 

	
 
#define configure_pin_available() DDRC &= ~(1 << DDC4)
 
#define configure_pin_locked() DDRC &= ~(1 << DDC5)
 

	
 
#define get_pin_available() (PINC & (1 << PINC4))
 
#define get_pin_locked() (PINC & (1 << PINC5))
 

	
 
#if SD_RAW_SDHC
 
    typedef uint64_t offset_t;
 
#else
 
    typedef uint32_t offset_t;
 
#endif
 

	
 
/* configuration checks */
 
#if SD_RAW_WRITE_SUPPORT
 
#undef SD_RAW_SAVE_RAM
 
#define SD_RAW_SAVE_RAM 0
 
#else
 
#undef SD_RAW_WRITE_BUFFERING
 
#define SD_RAW_WRITE_BUFFERING 0
 
#endif
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
master/master/lib/serparser.c
Show inline comments
 
@@ -118,7 +118,6 @@ int uart_Parser(void)
 
			}
 
 
		}
 
		
 
	}
 
	else
 
	{
master/master/master.cproj
Show inline comments
 
@@ -126,12 +126,54 @@
 
    <Compile Include="lib\ax25.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\logger.c">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\led.c">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\led.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\logger.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\byteordering.c">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\byteordering.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\fat.c">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\fat.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\fat_config.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\partition.c">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\partition.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\partition_config.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\sd-reader_config.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\sd_raw.c">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\sd_raw.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\sd\sd_raw_config.h">
 
      <SubType>compile</SubType>
 
    </Compile>
 
    <Compile Include="lib\serial.c">
 
      <SubType>compile</SubType>
 
    </Compile>
 
@@ -150,6 +192,7 @@
 
  </ItemGroup>
 
  <ItemGroup>
 
    <Folder Include="lib" />
 
    <Folder Include="lib\sd" />
 
  </ItemGroup>
 
  <Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
 
</Project>
 
\ No newline at end of file
0 comments (0 inline, 0 general)