Changeset - 29209506593c
[Not reviewed]
master/master/config.h
Show inline comments
 
@@ -14,12 +14,14 @@
 
#define CONFIG_H_
 
 
// --------------------------------------------------------------------------
 
// Module config (master.c)
 
// --------------------------------------------------------------------------
 
 
#define DEBUG_OUTPUT
 
 
#define F_CPU 11059200
 
#define MODULE_ID '1'
 
#define BOARDTEMP_ADDR 0x90
 
 
#define HEATER_THRESHOLD 70
 
master/master/lib/SDa/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/SDa/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/SDa/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/SDa/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/SDa/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/SDa/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/SDa/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/SDa/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/SDa/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/SDa/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"
 
#include "sd_raw_config.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 / 64 which gives over 100khz required minimum clock
 
    //SPSR &= ~(1 << SPI2X); // No doubled clock frequency 
 
	SPCR |= (1<< SPI2X); // Double clock frequ3ency so just less than 400khz
 

	
 
    /* 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()
 
{
 
	int i;
 
	return 1; // !!TODO: OH GOSH CHANGE ME
 
    return get_pin_available() == 0x00;
 
}
 

	
 
/**
 
 * \ingroup sd_raw
 
 * Checks whether 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()
 
{
 
	int i;
 
	// !!TODO oh gosh change me
 
	return 0;
 
    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/SDa/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/SDa/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>
 
#include <avr/io.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
 

	
 
/**
 
 * @}
 
 */
 

	
 
#define configure_pin_mosi() DDRB |= (1 << DDB5) //PB5
 
#define configure_pin_sck() DDRB |= (1 << DDB7) //PB7
 
#define configure_pin_ss() DDRB |= (1 << DDB0) | (1 << DDB4); PORTB |= (1<<DDB4) //PB0 - custom pin, but pb4 must be set as output
 
#define configure_pin_miso() DDRB &= ~(1 << DDB6) //PB6
 
	
 
#define select_card() PORTB &= ~(1 << PORTB0)
 
#define unselect_card() PORTB |= (1 << PORTB0)
 

	
 

	
 

	
 
#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/boardtemp.c
Show inline comments
 
new file 100644
 
/*
 
 * sensors.c
 
 *
 
 * Created: 11/19/2012 9:25:01 PM
 
 *  Author: kripperger
 
 */ 
 
 
 
#include <inttypes.h>
 
#include <avr/io.h>
 
#include <avr/interrupt.h>
 
#include "../config.h"
 
#include <util/delay.h>
 
#include "boardtemp.h"
 
#include "i2c.h"
 
 
int8_t	boardTemp;	// Board Temperature (from i2c)
 

	
 
void sensors_readBoardTemp()
 
{
 
	boardTemp = i2c_read(BOARDTEMP_ADDR, 0x00);		// Read only the first byte of data (we don't need the resolution here)
 
	boardTemp = ((boardTemp*18)/10) + (32);			// Converting Celsius to Fahrenheit
 
	boardTemp = boardTemp - 3;						// Linear offset
 
}
 

	
 
int8_t sensors_getBoardTemp(void)
 
{
 
	return boardTemp;
 
}
master/master/lib/boardtemp.h
Show inline comments
 
new file 100644
 
/*
 
 * sensors.h
 
 *
 
 * Created: 11/19/2012 9:24:50 PM
 
 *  Author: kripperger
 
 */ 
 
 
 
#ifndef BOARDTEMP_H_
 
#define BOARDTEMP_H_
 
 
void sensors_readBoardTemp(void);	// Reads board temperature
 
int8_t sensors_getBoardTemp(void);	// Gets board temperature from variable
 
 
#endif /* BOARDTEMP_H_ */
 
\ No newline at end of file
master/master/lib/eeprom.c
Show inline comments
 
new file 100644
 
/*
 
 * eeprom.c
 
 *
 
 * Created: 11/15/2012 2:29:59 PM
 
 *  Author: ethanzonca
 
 */ 
 
 
#include <avr/io.h>
 
#include <avr/eeprom.h>
 
 
void eeprom_write(uint8_t addr, uint8_t data) {
 
	eeprom_update_byte(addr, data);
 
}
 
 
uint8_t eeprom_read(uint8_t addr) {
 
	return eeprom_read_byte(addr);
 
}
 
\ No newline at end of file
master/master/lib/eeprom.h
Show inline comments
 
new file 100644
 
/*
 
 * eeprom.h
 
 *
 
 * Created: 11/15/2012 2:30:06 PM
 
 *  Author: ethanzonca
 
 */ 
 
 
 
#ifndef EEPROM_H_
 
#define EEPROM_H_
 
 
void eeprom_write(uint8_t addr, uint8_t data);
 
uint8_t eeprom_read(uint8_t addr);
 
 
 
#endif /* EEPROM_H_ */
 
\ No newline at end of file
master/master/lib/heater.c
Show inline comments
 
new file 100644
 
/*
 
 * Master Firmware: Board Heater
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
#include "../config.h"
 
#include "led.h"
 
 
 
 void heater_regulateTemp()
 
 {
 
	 // Gets board temperature and enables heater if below threshold
 
	 if (sensors_getBoardTemp() <= HEATER_THRESHOLD)
 
	 {
 
		 led_on(LED_HEAT);
 
		 led_on(LED_STATUS);
 
	 }
 
	 else if (sensors_getBoardTemp() > (HEATER_THRESHOLD + 5))
 
	 {
 
		 led_off(LED_HEAT);
 
		 led_off(LED_STATUS);
 
	 }
 
 }
 
\ No newline at end of file
master/master/lib/heater.h
Show inline comments
 
new file 100644
 
/*
 
 * Master Firmware: Board Heater
 
 *
 
 * Wireless Observational Modular Aerial Network
 
 * 
 
 * Ethan Zonca
 
 * Matthew Kanning
 
 * Kyle Ripperger
 
 * Matthew Kroening
 
 *
 
 */
 
 
#ifndef HEATER_H_
 
#define HEATER_H_
 
 
void heater_regulateTemp();
 
 
#endif /* HEATER_H_ */
 
\ No newline at end of file
master/master/master.c
Show inline comments
 
@@ -50,15 +50,19 @@ int main(void)
 
	i2c_init();
 
	sensordata_setup(); // must happen before sensors/logger/afsk
 
	slavesensors_setup();
 
	logger_setup();
 
	afsk_setup();
 
	//\f
 
	
 
	#ifdef DEBUG_OUTPUT
 
	serial0_sendString("\r\n---------------------------------\r\n");
 
	serial0_sendString("HAB Controller 1.0 - Initialized!\r\n");
 
	serial0_sendString("---------------------------------\r\n");
 
	#endif
 
	
 
	serial0_sendString("\r\nHello.\r\n\r\n");
 
	
 
	slavesensors_network_scan(); // This can take a little while
 
	
 
	// Buffer for string operations
 
	char logbuf[128];
 
@@ -141,14 +145,16 @@ int main(void)
 
			
 
			sensors_readBoardTemp(); // i2c read, 400k
 
			snprintf(logbuf, 128, "%lu,%d,%u,%s,%s,%s,%s,%s\r\n", time_millis(), sensors_getBoardTemp(),get_timestamp(),get_latitude(),get_longitude(),get_speedKnots(),get_hdop(), get_course());
 
			logger_log(logbuf);
 
			
 
			// Print out logger debug
 
			#ifdef DEBUG_OUTPUT
 
			serial0_sendString("LOG> ");
 
			serial0_sendString(logbuf);
 
			#endif
 
			
 
			led_off(LED_CYCLE);
 
			lastLog = time_millis();
 
		}		
 
		
 
		
0 comments (0 inline, 0 general)