# HG changeset patch # User ethanzonca@CL-ENS241-08.cedarville.edu # Date 2013-01-18 15:59:04 # Node ID c715dbef3de50e1ea79b84b5d517d40439cf2511 # Parent 29209506593c185c1ae6d43c5d72afd017b6d5ed Master repository restructuring and cleanup (major path changes) diff --git a/master/master/lib/SDa/byteordering.c b/master/master/lib/SDa/byteordering.c deleted file mode 100644 --- a/master/master/lib/SDa/byteordering.c +++ /dev/null @@ -1,110 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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); -} - -/** - * @} - */ - diff --git a/master/master/lib/SDa/byteordering.h b/master/master/lib/SDa/byteordering.h deleted file mode 100644 --- a/master/master/lib/SDa/byteordering.h +++ /dev/null @@ -1,188 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - -#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 - diff --git a/master/master/lib/SDa/fat.c b/master/master/lib/SDa/fat.c deleted file mode 100644 --- a/master/master/lib/SDa/fat.c +++ /dev/null @@ -1,2551 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - -#if USE_DYNAMIC_MEMORY - #include -#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 - diff --git a/master/master/lib/SDa/fat.h b/master/master/lib/SDa/fat.h deleted file mode 100644 --- a/master/master/lib/SDa/fat.h +++ /dev/null @@ -1,131 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#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 - diff --git a/master/master/lib/SDa/fat_config.h b/master/master/lib/SDa/fat_config.h deleted file mode 100644 --- a/master/master/lib/SDa/fat_config.h +++ /dev/null @@ -1,128 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#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 - diff --git a/master/master/lib/SDa/partition.c b/master/master/lib/SDa/partition.c deleted file mode 100644 --- a/master/master/lib/SDa/partition.c +++ /dev/null @@ -1,155 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - -#if USE_DYNAMIC_MEMORY - #include -#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; -} - -/** - * @} - */ - diff --git a/master/master/lib/SDa/partition.h b/master/master/lib/SDa/partition.h deleted file mode 100644 --- a/master/master/lib/SDa/partition.h +++ /dev/null @@ -1,212 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#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 - diff --git a/master/master/lib/SDa/partition_config.h b/master/master/lib/SDa/partition_config.h deleted file mode 100644 --- a/master/master/lib/SDa/partition_config.h +++ /dev/null @@ -1,44 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - diff --git a/master/master/lib/SDa/sd-reader_config.h b/master/master/lib/SDa/sd-reader_config.h deleted file mode 100644 --- a/master/master/lib/SDa/sd-reader_config.h +++ /dev/null @@ -1,53 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - diff --git a/master/master/lib/SDa/sd_raw.c b/master/master/lib/SDa/sd_raw.c deleted file mode 100644 --- a/master/master/lib/SDa/sd_raw.c +++ /dev/null @@ -1,1007 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#include -#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; -} - diff --git a/master/master/lib/SDa/sd_raw.h b/master/master/lib/SDa/sd_raw.h deleted file mode 100644 --- a/master/master/lib/SDa/sd_raw.h +++ /dev/null @@ -1,148 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#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 - diff --git a/master/master/lib/SDa/sd_raw_config.h b/master/master/lib/SDa/sd_raw_config.h deleted file mode 100644 --- a/master/master/lib/SDa/sd_raw_config.h +++ /dev/null @@ -1,115 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#include - -#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< const char *gps_aprs_lat = "39.74744N"; diff --git a/master/master/lib/aprs_trackuino/config.h b/master/master/lib/aprs_trackuino/config.h deleted file mode 100644 --- a/master/master/lib/aprs_trackuino/config.h +++ /dev/null @@ -1,213 +0,0 @@ -/* trackuino copyright (C) 2010 EA5HAV Javi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __CONFIG_H__ -#define __CONFIG_H__ - - -// -------------------------------------------------------------------------- -// THIS IS THE TRACKUINO FIRMWARE CONFIGURATION FILE. YOUR CALLSIGN AND -// OTHER SETTINGS GO HERE. -// -// NOTE: all pins are Arduino based, not the Atmega chip. Mapping: -// http://www.arduino.cc/en/Hacking/PinMapping -// -------------------------------------------------------------------------- - - -// -------------------------------------------------------------------------- -// APRS config (aprs.c) -// -------------------------------------------------------------------------- - -// Set your callsign and SSID here. Common values for the SSID are -// (from http://zlhams.wikidot.com/aprs-ssidguide): -// -// - Balloons: 11 -// - Cars: 9 -// - Home: 0 -// - IGate: 5 -#define S_CALLSIGN "MYCALL" -#define S_CALLSIGN_ID 11 - -// Destination callsign: APRS (with SSID=0) is usually okay. -#define D_CALLSIGN "APRS" -#define D_CALLSIGN_ID 0 - -// Digipeating paths: -// (read more about digipeating paths here: http://wa8lmf.net/DigiPaths/ ) -// The recommended digi path for a balloon is WIDE2-1 or pathless. The default -// is pathless. Uncomment the following two lines for WIDE2-1 path: -#define DIGI_PATH1 "WIDE2" -#define DIGI_PATH1_TTL 1 - -// APRS comment: this goes in the comment portion of the APRS message. You -// might want to keep this short. The longer the packet, the more vulnerable -// it is to noise. -#define APRS_COMMENT "Trackuino reminder: replace callsign with your own" - - -// -------------------------------------------------------------------------- -// AX.25 config (ax25.cpp) -// -------------------------------------------------------------------------- - -// TX delay in milliseconds -#define TX_DELAY 300 - -// -------------------------------------------------------------------------- -// Tracker config (trackuino.pde) -// -------------------------------------------------------------------------- - -// APRS packets are slotted so that multiple trackers can be used without -// them stepping on one another. The transmission times are governed by -// the formula: -// -// APRS_SLOT (seconds) + n * APRS_PERIOD (seconds) -// -// When launching multiple balloons, use the same APRS_PERIOD in all balloons -// and set APRS_SLOT so that the packets are spaced equally in time. -// Eg. for two balloons and APRS_PERIOD = 60, set APRS_SLOT to 0 and 30, -// respectively. The first balloon will transmit at 00:00:00, 00:01:00, -// 00:02:00, etc. amd the second balloon will transmit at 00:00:30, 00:01:30, -// 00:02:30, etc. -#define APRS_SLOT 0 // seconds. -1 disables slotted transmissions -#define APRS_PERIOD 60 // seconds - -// GPS baud rate (in bits per second). This is also the baud rate at which -// debug data will be printed out the serial port. -#define GPS_BAUDRATE 9600 - - -// -------------------------------------------------------------------------- -// Modem config (afsk.cpp) -// -------------------------------------------------------------------------- - -// AUDIO_PIN is the audio-out pin. The audio is generated by timer 2 using -// PWM, so the only two options are pins 3 and 11. -// Pin 11 doubles as MOSI, so I suggest using pin 3 for PWM and leave 11 free -// in case you ever want to interface with an SPI device. -#define AUDIO_PIN 3 - -// -------------------------------------------------------------------------- -// Radio config (radio_hx1.cpp) -// -------------------------------------------------------------------------- - -// This is the PTT pin -#define PTT_PIN 4 - -// -------------------------------------------------------------------------- -// Sensors config (sensors.cpp) -// -------------------------------------------------------------------------- - -// Most of the sensors.cpp functions use internal reference voltages (either -// AVCC or 1.1V). If you want to use an external reference, you should -// uncomment the following line: -// -// #define USE_AREF -// -// BEWARE! If you hook up an external voltage to the AREF pin and -// accidentally set the ADC to any of the internal references, YOU WILL -// FRY YOUR AVR. -// -// It is always advised to connect the AREF pin through a pull-up resistor, -// whose value is defined here in ohms (set to 0 if no pull-up): -// -#define AREF_PULLUP 4700 -// -// Since there is already a 32K resistor at the ADC pin, the actual -// voltage read will be VREF * 32 / (32 + AREF_PULLUP) -// -// Read more in the Arduino reference docs: -// http://arduino.cc/en/Reference/AnalogReference?from=Reference.AREF - -// Pin mappings for the internal / external temperature sensors. VS refers -// to (arduino) digital pins, whereas VOUT refers to (arduino) analog pins. -#define INTERNAL_LM60_VS_PIN 6 -#define INTERNAL_LM60_VOUT_PIN 0 -#define EXTERNAL_LM60_VS_PIN 7 -#define EXTERNAL_LM60_VOUT_PIN 1 - -// Units for temperature sensors (Added by: Kyle Crockett) -// 1 = Celsius, 2 = Kelvin, 3 = Fahrenheit -#define TEMP_UNIT 1 - -// Calibration value in the units selected. Use integer only. -#define CALIBRATION_VAL 0 - -// Resistors divider for the voltage meter (ohms) -#define VMETER_R1 10000 -#define VMETER_R2 3300 - -// Voltage meter analog pin -#define VMETER_PIN 2 - -// -------------------------------------------------------------------------- -// Buzzer config (buzzer.cpp) -// -------------------------------------------------------------------------- - -// Type of buzzer (0=active, 1=passive). An active buzzer is driven by a -// DC voltage. A passive buzzer needs a PWM signal. -#define BUZZER_TYPE 0 - -// When using a passive buzzer, specify the PWM frequency here. Choose one -// that maximizes the volume according to the buzzer's datasheet. Not all -// the frequencies are valid, check out the buzzer_*.cpp code. On Arduino, -// it must be between L and 65535, where L = F_CPU / 65535 and F_CPU is the -// clock rate in hertzs. For 16 MHz Arduinos, this gives a lower limit of -// 245 Hz. -#define BUZZER_FREQ 895 // Hz - -// These are the number of seconds the buzzer will stay on/off alternately -#define BUZZER_ON_TIME 1 // secs -#define BUZZER_OFF_TIME 2 // secs - -// This option disables the buzzer above BUZZER_ALTITUDE meters. This is a -// float value, so make it really high (eg. 1000000.0 = 1 million meters) -// if you want it to never stop buzzing. -#define BUZZER_ALTITUDE 3000.0 // meters (1 ft = 0.3048 m) - -// The options here are pin 9 or 10 -#define BUZZER_PIN 9 - -// -------------------------------------------------------------------------- -// Debug -// -------------------------------------------------------------------------- - -// This is the LED pin (13 on Arduinos). The LED will be on while the AVR is -// running and off while it's sleeping, so its brightness gives an indication -// of the CPU activity. -#define LED_PIN 13 - -// Debug info includes printouts from different modules to aid in testing and -// debugging. -// -// 1. To properly receive debug information, only connect the Arduino RX pin -// to the GPS TX pin, and leave the Arduino TX pin disconnected. -// -// 2. On the serial monitor, set the baudrate to GPS_BAUDRATE (above), -// usually 9600. -// -// 3. When flashing the firmware, disconnect the GPS from the RX pin or you -// will get errors. - -// #define DEBUG_GPS // GPS sentence dump and checksum validation -// #define DEBUG_AX25 // AX.25 frame dump -// #define DEBUG_MODEM // Modem ISR overrun and profiling -// #define DEBUG_RESET // AVR reset -// #define DEBUG_SENS // Sensors - - -#endif - diff --git a/master/master/lib/gps.c b/master/master/lib/gps.c new file mode 100644 --- /dev/null +++ b/master/master/lib/gps.c @@ -0,0 +1,700 @@ +/* +* gpsMKa.c +* +* Created: 11/15/2012 12:02:38 PM +* Author: mkanning +*/ + +#include +#include +#include +#include "gps.h" +#include "serial.h" +#include "../config.h" +#include "led.h" + + + +// Circular buffer for incoming data +uint8_t nmeaBuffer[NMEABUFFER_SIZE]; + +// Location of parser in the buffer +uint8_t nmeaBufferParsePosition = 0; + +// Location of receive byte interrupt in the buffer +volatile uint16_t nmeaBufferDataPosition = 0; + + +/* +volatile uint8_t charTest; +ISR(USART1_RX_vect) +{ + serial0_sendChar(UDR1); + +}*/ + +// holds the byte ALREADY PARSED. includes starting character +int bytesReceived = 0; + +//data (and checksum) of most recent transmission +char data[16]; + +//used to skip over bytes during parse +int skipBytes = 0; + +//used to index data arrays during data collection +int numBytes = 0; + +//variables to store data from transmission +//least significant digit is stored at location 0 of arrays +char tramsmissionType[7]; + +char timestamp[12]; //hhmmss.ss +char* get_timestamp() { + return timestamp; +} + +char latitude[14]; //lllll.lla +char* get_latitude() { + return latitude; +} + +char longitude[14]; //yyyyy.yyb +char* get_longitude() { + return longitude; +} + + +char quality; //quality for GGA and validity for RMC +char numSatellites[4]; +char* getNumSatelites() { + return numSatellites; +} + +char hdop[6]; //xx.x +char* get_hdop() { + return hdop; +} + +char altitude[10]; //xxxxxx.x +char wgs84Height[8]; //sxxx.x +char lastUpdated[8]; //blank - included for testing +char stationID[8]; //blank - included for testing +char checksum[3]; //xx + +char knots[8]; //xxx.xx +char* get_speedKnots() { + return knots; +} + +char course[8]; //xxx.x +char* get_course() { + return course; +} + +char dayofmonth[9]; //ddmmyy +char* get_dayofmonth() { + return dayofmonth; +} + +char variation[9]; //xxx.xb +int calculatedChecksum; +int receivedChecksum; + +char convertBuf1[15]; +char convertBuf2[15]; + +// transmission state machine +enum decodeState { + //shared fields + INITIALIZE=0, + GET_TYPE, + GPS_CHECKSUM, //XOR of all the bytes between the $ and the * (not including the delimiters themselves), written in hexadecimal + //GGA data fields + GGA_TIME, + GGA_LATITUDE, + GGA_LONGITUDE, + GGA_QUALITY, + GGA_SATELLITES, + GGA_HDOP, + GGA_ALTITUDE, + GGA_WGS84, + GGA_LAST_UPDATE, + GGA_STATION_ID, + //RMC data fields + RMC_TIME, + RMC_VALIDITY, + RMC_LATITUDE, + RMC_LONGITUDE, + RMC_KNOTS, + RMC_COURSE, + RMC_DATE, + RMC_MAG_VARIATION, + +}decodeState; + + +char debugBuff[128]; + +ISR(USART1_RX_vect) +{ + nmeaBuffer[nmeaBufferDataPosition % NMEABUFFER_SIZE] = UDR1; + nmeaBufferDataPosition = (nmeaBufferDataPosition + 1) % NMEABUFFER_SIZE; + //serial0_sendChar(UDR1); + //snprintf(debugBuff, 32, "GPS: bdp: %d, bpp: %d decodestate: %u \r\n", nmeaBufferDataPosition, nmeaBufferParsePosition, decodeState); + //serial0_sendString((debugBuff)); +} + + +// Could inline if program space available +static void setParserState(uint8_t state) +{ + decodeState = state; + + + // If resetting, clear vars + if(state == INITIALIZE) + { + calculatedChecksum = 0; + } + + // Every time we change state, we have parsed a byte + nmeaBufferParsePosition = (nmeaBufferParsePosition + 1) % NMEABUFFER_SIZE; +} + + + +//// MKa GPS transmission parser START +void parse_gps_transmission(void){ + + // Pull byte off of the buffer + char byte; + + + + while(nmeaBufferDataPosition != nmeaBufferParsePosition) { + led_on(LED_ACTIVITY); + + byte = nmeaBuffer[nmeaBufferParsePosition]; + + //snprintf(debugBuff, 64, "GPSParse: byte [%c] decodestate: %u bp: %u pp: %u\r\n", byte, decodeState, nmeaBufferDataPosition, nmeaBufferParsePosition); + //serial0_sendString((debugBuff)); + + if(decodeState == INITIALIZE) //start of transmission sentence + { + if(byte == '$') { + #ifdef DEBUG_NMEA + serial0_sendString("found $\r\n"); + #endif + + setParserState(GET_TYPE); + numBytes = 0; //prep for next phases + skipBytes = 0; + calculatedChecksum = 0; + } + + else { + setParserState(INITIALIZE); + } + } + + //parse transmission type + else if (decodeState == GET_TYPE) + { + tramsmissionType[numBytes] = byte; + numBytes++; + + #ifdef DEBUG_NMEA + serial0_sendString("stored a type byte\r\n"); + #endif + + if(byte == ',') //end of this data type + { + tramsmissionType[5] = 0x00; + + if (tramsmissionType[2] == 'G' && + tramsmissionType[3] == 'G' && + tramsmissionType[4] == 'A') + { + setParserState(GGA_TIME); + numBytes = 0; + } + else if (tramsmissionType[2] == 'R' && + tramsmissionType[3] == 'M' && + tramsmissionType[4] == 'C') + { + setParserState(RMC_TIME); + numBytes = 0; + } + else //this is an invalid transmission type + { + setParserState(INITIALIZE); + } + } + else { + // continue + setParserState(GET_TYPE); + } + + } + + ///parses GGA transmissions START + /// $--GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*xx + //timestamp + else if (decodeState == GGA_TIME) + { + if (byte == ',') //end of this data type + { + timestamp[4] = 0x00; // Cut off at 4 (no seconds) for APRS + setParserState(GGA_LATITUDE); + skipBytes = 0; //prep for next phase of parse + numBytes = 0; + } + else //store data + { + #ifdef DEBUG_NMEA + serial0_sendString("found GGA time byte\r\n"); + #endif + + setParserState(GGA_TIME); + timestamp[numBytes] = byte; //byte; //adjust number of bytes to fit array + numBytes++; + } + } + + //latitude + else if (decodeState == GGA_LATITUDE) + { + if (byte == ',' && skipBytes == 0) //discard this byte + { + #ifdef DEBUG_NMEA + serial0_sendString("found lat skip byte\r\n"); + #endif + + skipBytes = 1; + setParserState(GGA_LATITUDE); + } + else if (byte == ',') //end of this data type + { + + latitude[7] = 0x00; // null terminate + + + // First 2 bytes + //convertBuf1[0] = latitude[0]; + //convertBuf1[1] = latitude[1]; + //convertBuf1[2] = '\0'; + //strncpy(convertBuf2, latitude, 7); + //convertBuf2[7] = '\0'; + + + setParserState(GGA_LONGITUDE); + skipBytes = 0; //prep for next phase of parse + numBytes = 0; + } + else //store data + { + #ifdef DEBUG_NMEA + serial0_sendString("found lat byte\r\n"); + #endif + + latitude[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + setParserState(GGA_LATITUDE); + } + } + + //longitude + else if (decodeState == GGA_LONGITUDE) + { + if (byte == ',' && skipBytes == 0) //discard this byte + { + #ifdef DEBUG_NMEA + serial0_sendString("found long skip byte\r\n"); + #endif + + skipBytes = 1; + setParserState(GGA_LONGITUDE); + } + else if (byte == ',') //end of this data type + { + longitude[8] = 0x00; + setParserState(GGA_QUALITY); + numBytes = 0; //prep for next phase of parse + skipBytes = 0; + } + else //store data + { + #ifdef DEBUG_NMEA + serial0_sendString("found long byte\r\n"); + #endif + + longitude[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + setParserState(GGA_LONGITUDE); + } + } + + //GGA quality + else if (decodeState == GGA_QUALITY) + { + if (byte == ',') //end of this data type + { + setParserState(GGA_SATELLITES); + numBytes = 0; //prep for next phase of parse + } + else //store data + { + #ifdef DEBUG_NMEA + serial0_sendString("found quality byte\r\n"); + #endif + + quality = byte; //maybe reset if invalid data ?? + setParserState(GGA_QUALITY); + } + } + + //number of satellites + else if (decodeState == GGA_SATELLITES) + { + if (byte == ',') //end of this data type + { + numSatellites[numBytes] = 0x00; + setParserState(GGA_HDOP); + numBytes = 0; //prep for next phase of parse + } + else //store data + { + numSatellites[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + setParserState(GGA_SATELLITES); + } + } + + //HDOP + else if (decodeState == GGA_HDOP) + { + if (byte == ',' ) //end of this data type + { + hdop[numBytes] = 0x00; + setParserState(GGA_ALTITUDE); + numBytes = 0; //prep for next phase of parse + skipBytes = 0; + } + else //store data + { + hdop[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + setParserState(GGA_HDOP); + } + } + + //altitude + else if (decodeState == GGA_ALTITUDE) + { + if (byte == ',' && skipBytes == 0) //discard this byte + { + skipBytes = 1; + setParserState(GGA_ALTITUDE); + } + else if(byte == ',') //end of this data type + { + altitude[numBytes] = 0x00; + setParserState(GGA_WGS84); + numBytes = 0; //prep for next phase of parse + } + else //store data + { + altitude[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + setParserState(GGA_ALTITUDE); + } + } + + //WGS84 Height + else if (decodeState == GGA_WGS84) + { + if (byte == ',' && skipBytes == 0) //discard this byte + { + skipBytes = 1; + setParserState(GGA_WGS84); + } + else if(byte == ',') //end of this data type + { + wgs84Height[numBytes] = 0x00; + setParserState(GGA_LAST_UPDATE); + skipBytes = 0; //prep for next phase of parse + numBytes = 0; + } + else //store data + { + wgs84Height[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + setParserState(GGA_WGS84); + } + } + + //last GGA DGPS update + else if (decodeState == GGA_LAST_UPDATE) + { + if (byte == ',') //end of this data type + { + lastUpdated[numBytes] = 0x00; + setParserState(GGA_STATION_ID); + numBytes = 0; //prep for next phase of parse + } + else //store data - this should be blank + { + lastUpdated[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + setParserState(GGA_LAST_UPDATE); + } + } + + //GGA DGPS station ID + else if (decodeState == GGA_STATION_ID) + { + if (byte == ',' || byte == '*') //end of this data type + { + stationID[numBytes] = 0x00; + setParserState(GPS_CHECKSUM); + numBytes = 0; //prep for next phase of parse + } + else //store data - this should be blank + { + stationID[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + setParserState(GGA_STATION_ID); + } + } + ///parses GGA transmissions END + + /// $GPRMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,ddmmyy,x.x,a*hh + ///parses RMC transmissions + //time + // emz: commented setter, GMC time better? + else if(decodeState == RMC_TIME) + { + if (byte == ',') //end of this data type + { + //timestamp[numBytes] = 0x00; + setParserState(RMC_VALIDITY); + numBytes = 0; //prep for next phase of parse + } + else //store data + { + #ifdef DEBUG_NMEA + serial0_sendString("found time byte\r\n"); + #endif + + //timestamp[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + setParserState(RMC_TIME); + } + } + + //validity + // not needed? dupe gga + else if(decodeState == RMC_VALIDITY) + { + if (byte == ',') //end of this data type + { + setParserState(RMC_LATITUDE); + skipBytes = 0; //prep for next phase of parse + numBytes = 0; + } + else //store data + { + #ifdef DEBUG_NMEA + serial0_sendString("found valid byte\r\n"); + #endif + + //quality = byte; + numBytes++; + setParserState(RMC_VALIDITY); + } + } + + //latitude RMC (we don't need this, commented out setter) + else if(decodeState == RMC_LATITUDE) + { + if (byte == ',' && skipBytes == 0) //discard this byte + { + #ifdef DEBUG_NMEA + serial0_sendString("found lat skip byte\r\n"); + #endif + + skipBytes = 1; + setParserState(RMC_LATITUDE); + } + else if (byte == ',') //end of this data type + { + setParserState(RMC_LONGITUDE); + skipBytes = 0; //prep for next phase of parse + numBytes = 0; + } + else //store data + { + #ifdef DEBUG_NMEA + serial0_sendString("found lat byte\r\n"); + #endif + + //latitude[numBytes]= byte; //adjust number of bytes to fit array + numBytes++; + setParserState(RMC_LATITUDE); + } + } + + //longitude RMC (we don't need this, commented out setter) + else if(decodeState == RMC_LONGITUDE) + { + if (byte == ',' && skipBytes == 0) //discard this byte + { + #ifdef DEBUG_NMEA + serial0_sendString("found long byte\r\n"); + #endif + + skipBytes = 1; + setParserState(RMC_LONGITUDE); + } + else if (byte == ',') //end of this data type + { + setParserState(RMC_KNOTS); + skipBytes = 0; + numBytes = 0; + } + else //store data + { + #ifdef DEBUG_NMEA + serial0_sendString("found long byte\r\n"); + #endif + + //longitude[numBytes]= byte; //adjust number of bytes to fit array + numBytes++; + setParserState(RMC_LONGITUDE); + } + } + + //knots + else if(decodeState == RMC_KNOTS) + { + if (byte == ',') //end of this data type + { + knots[numBytes] = 0x00; + setParserState(RMC_COURSE); + numBytes = 0; //prep for next phase of parse + } + else //store data + { + setParserState(RMC_KNOTS); + knots[numBytes]= byte; //adjust number of bytes to fit array + numBytes++; + } + } + + //course + else if(decodeState == RMC_COURSE) + { + if (byte == ',') //end of this data type + { + course[numBytes] = 0x00; + setParserState(RMC_DATE); + numBytes = 0; //prep for next phase of parse + } + else //store data + { + setParserState(RMC_COURSE); + course[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + } + } + + //date + else if(decodeState == RMC_DATE) + { + if (byte == ',') //end of this data type + { + // Cut it off at day of month. Also has month and year if we ever need it. + dayofmonth[2] = 0x00; + setParserState(RMC_MAG_VARIATION); + skipBytes = 0; //prep for next phase of parse + numBytes = 0; + } + else //store data + { + setParserState(RMC_DATE); + dayofmonth[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + } + } + + //magnetic variation + else if(decodeState == RMC_MAG_VARIATION) + { + if (byte == '*') //end of this data type + { + variation[numBytes] = 0x00; + setParserState(GPS_CHECKSUM); + numBytes = 0; //prep for next phase of parse + } + else //store data + { + setParserState(RMC_MAG_VARIATION); + variation[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + } + } + ///parses RMC transmissions END + + + //checksum + else if (decodeState == GPS_CHECKSUM) + { + if (numBytes == 2) //end of data - terminating character ?? + { + //checksum calculator for testing http://www.hhhh.org/wiml/proj/nmeaxor.html + //TODO: must determine what to do with correct and incorrect messages + receivedChecksum = checksum[0] + (checksum[1]*16); //convert bytes to int + if(calculatedChecksum==receivedChecksum) + { + + } + else + { + + } + #ifdef DEBUG_NMEA + serial0_sendString("OMG GOT TO CHECKSUM!\r\n"); + #endif + + serial0_sendString((debugBuff)); + setParserState(INITIALIZE); + numBytes = 0; //prep for next phase of parse + } + else //store data + { + setParserState(GPS_CHECKSUM); + checksum[numBytes] = byte; //adjust number of bytes to fit array + numBytes++; + } + } + else { + setParserState(INITIALIZE); + } + + if (decodeState!=GPS_CHECKSUM && decodeState!=INITIALIZE) //want bytes between '$' and '*' + { + //input byte into running checksum + XORbyteWithChecksum(byte); + } + led_off(LED_ACTIVITY); + } + + +} +/// MKa GPS transmission parser END + +void XORbyteWithChecksum(uint8_t byte){ + calculatedChecksum ^= (int)byte; //this may need to be re-coded +} + diff --git a/master/master/lib/gps.h b/master/master/lib/gps.h new file mode 100644 --- /dev/null +++ b/master/master/lib/gps.h @@ -0,0 +1,24 @@ +/* + * gpsMKa.h + * + * Created: 11/15/2012 12:02:53 PM + * Author: mkanning + */ + + +#ifndef GPSMKA_H_ +#define GPSMKA_H_ +#define GGA_MESSAGE +#define RMC_MESSAGE +#define UKN_MESSAGE + +char* get_longitude(); +char* get_latitude(); +char* get_timestamp(); +char* get_speedKnots(); +char* get_course(); +char* get_hdop(); +char* getNumSatelites(); +char* get_dayofmonth(); + +#endif /* GPSMKA_H_ */ \ No newline at end of file diff --git a/master/master/lib/i2c.c b/master/master/lib/i2c.c new file mode 100644 --- /dev/null +++ b/master/master/lib/i2c.c @@ -0,0 +1,241 @@ +/* + * i2c.c + * + * Created: 11/7/2012 7:18:23 PM + * Author: kripperger + */ + +#include +#include +#include "../config.h" +#include "i2c.h" + + +/* I2C clock in Hz */ +#define SCL_CLOCK 100000L + + +/************************************************************************* + Initialization of the I2C bus interface. Need to be called only once +*************************************************************************/ +void i2c_init(void) +{ + /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */ + + TWSR = 0; /* no prescaler */ + TWBR = ((F_CPU/SCL_CLOCK)-16)/2; /* must be > 10 for stable operation */ + +}/* i2c_init */ + + + +/************************************************************************* + Issues a start condition and sends address and transfer direction. + return 0 = device accessible, 1= failed to access device +*************************************************************************/ +unsigned char i2c_start(unsigned char address) +{ + uint8_t twst; + + // send START condition + TWCR = (1< + + +/** defines the data direction (reading from I2C device) in i2c_start(),i2c_rep_start() */ +#define I2C_READ 1 +/** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */ +#define I2C_WRITE 0 + + +void i2c_init(void); //initialize the I2C master interace. Need to be called only once +void i2c_stop(void); //Terminates the data transfer and releases the I2C bus + +unsigned char i2c_start(unsigned char addr); //Issues a start condition and sends address and transfer direction + //addr is the address and transfer direction of I2C device + //Returns 0 device accessible + //Returns 1 failed to access device + +unsigned char i2c_rep_start(unsigned char addr); //Issues a repeated start condition and sends address and transfer direction + //addr is the address and transfer direction of I2C device + //Returns 0 device accessible + //Returns 1 failed to access device + +void i2c_start_wait(unsigned char addr); //Issues a start condition and sends address and transfer direction. If device is busy, use ack polling to wait until device ready + //addr is the address and transfer direction of I2C device + +unsigned char i2c_writeX(unsigned char data); //Send one byte to I2C device + //data is the byte to be transfered + //Returns 0 write successful + //Returns 1 write failed + +unsigned char i2c_readAck(void); //read one byte from the I2C device, request more data from device + //Returns byte read from I2C device + +unsigned char i2c_readNak(void); //read one byte from the I2C device, read is followed by a stop condition + //Returns byte read from I2C device + +unsigned char i2c_readX(unsigned char ack); //read one byte from the I2C device. Implemented as a macro, which calls either i2c_readAck or i2c_readNak +#define i2c_readX(ack) (ack) ? i2c_readAck() : i2c_readNak(); + //When ack is 1: send ack, request more data from device + //When ack is 0: send nak, read is followed by a stop condition + //Returns byte read from I2C device + +void i2c_write(unsigned char addr, unsigned char reg, unsigned char data); //Does complete write. Can be used as stand alone function. + +unsigned char i2c_read(unsigned char addr, unsigned char reg); + +#endif /* I2C_H_ */ \ No newline at end of file diff --git a/master/master/lib/logger.c b/master/master/lib/logger.c --- a/master/master/lib/logger.c +++ b/master/master/lib/logger.c @@ -18,11 +18,11 @@ #include #include #include -#include "sd/fat.h" -#include "sd/fat_config.h" -#include "sd/partition.h" -#include "sd/sd_raw.h" -#include "sd/sd_raw_config.h" +#include "sdcard/fat.h" +#include "sdcard/fat_config.h" +#include "sdcard/partition.h" +#include "sdcard/sd_raw.h" +#include "sdcard/sd_raw_config.h" #include "logger.h" #include "led.h" diff --git a/master/master/lib/sd/ChangeLog b/master/master/lib/sd/ChangeLog deleted file mode 100644 --- a/master/master/lib/sd/ChangeLog +++ /dev/null @@ -1,124 +0,0 @@ - -2012-06-12 sd-reader - * fix capacity readout from csd register depending on format version - * fix gcc strict-aliasing warnings (also somewhat enlarges code size) - -2011-04-23 sd-reader - * fix FAT access for cluster numbers beyond 2^15 (for FAT16) and 2^30 (for FAT32) (thanks to Darwin Engwer for testing) - * correctly return disk-full condition from fat_write_file() on certain conditions - * use byteorder memory access functions for fat_fs_get_free() - * be more specific on the return value of fat_write_file() - -2011-02-05 sd-reader - * implement renaming a file or directory - * rewrite byteorder handling to fix unaligned memory accesses on 32-bit and probably 16-bit architectures - * make fat_create_file() not return failure if the file already exists - * make the "cat" output respect the count of bytes actually read - * document how to use fat_seek_file() for retrieving the file position - -2010-10-10 sd-reader - * Fix equal file names when reading two successive 8.3 directory entries. - * Fix calculation of cluster positions beyond 4GB (32 bit integer overflow). - * Fix endless looping of directory listing (occured with valid entry at end of last cluster). - -2010-01-10 sd-reader - * Make LFN support configurable. - * Ignore LFN directory entries without 8.3 name. - * Ignore LFN directory entries which do not match the 8.3 name's checksum. - * Implement delayed directory entry updates (disabled by default) (thanks to Adam Mayer). - * Speedup search for free cluster. - * Fix memory leak when using the "init" command (thanks to Tibor Vilhan). - * Fix ATmega328P-specific pin mappings. - * Add some of the picoPower MCU variants. - -2009-03-30 sd-reader - * Make 8.3 basename and/or extension lowercase when told by Windows NT and later. - * Add ATmega328 pin configuration. - * Fix MMC/SD/SDHC distinction. - * Fix raw block read/write buffering (thanks to Kurt Sterckx). - * Fix fat size calculation for FAT16 when configured with FAT32. - * Fix compilation for read-only configurations. - * Fix card lock detection. - * Make it easier to link with a C++ application (thanks to Jérôme Despatis). - -2008-11-21 sd-reader - * Support for SDHC cards (disabled by default). - * Support for FAT32 (disabled by default). - -2008-06-08 sd-reader - * New "init" command to allow reinitialization of memory card. - * Fix searching through multi-cluster directories. - * Fix reading directory entries spanning a cluster border (backport from mega-eth). - * Do not abort the whole lfn entry when the file name is too long, just drop single characters (backport from mega-eth). - * Change fat16_get_dir_entry_of_path() to ignore a slash at the end (backport from mega-eth). - * Make listing a directory's content much faster (backport from mega-eth). - * Shrink code size by centralizing cluster offset calculation (backport from mega-eth). - * Some other small fixes and optimizations. - -2007-12-13 sd-reader - * Dual-license the major implementation modules under GPL and LGPL. - -2007-06-03 sd-reader - * Fix reading beyond cached block (by Benjamin Meier). - * Implement support for reading and writing file modification dates/times. - (Thanks to Torsten Seeboth for testing.) - -2007-03-01 sd-reader - * Avoid LFN directory entries for the "." and ".." directory references. - This prevented Windows from deleting directories. - * Handle special case where the 8.3 filename begins with 0xe5. - * Fix return value of fat16_delete_file() when deleting empty files. - * Fix fat16_clear_cluster() which was zeroing only 16 of every 32 bytes. - -2007-01-20 sd-reader - * Fix directory creation. - - Correctly create "." and ".." directory entries (8.3 <-> lfn versions). - - Correctly clear cluster containing the directory entries for new directory. - -2006-11-01 sd-reader - * Implement creation and deletion of directories. - * Clear the directory entries of new directory clusters. - * Prevent linkage against printf(). - * Make the use of malloc()/free() optional. - -2006-09-01 sd-reader - * Fix shortening files. - * Fix free disk space calculation. - -2006-08-24 sd-reader - * Improve sleep handling. - * Display extended card information on boot and - when executing the "disk" shell command. - * Correctly determine FAT type by cluster count. - * Fix cluster allocation beyond card capacity. - -2006-08-16 sd-reader - * Provide FAT16 capacity and usage information. - * Implement the backspace key in the mini shell. - * Enter idle mode when waiting for uart activity. - * Make the Card Select pin MCU dependent as well. - * Add mini shell commands to documentation. - -2006-08-08 sd-reader - * Thanks go to Torsten Seeboth for his ongoing efforts - to test changes, fix regressions and give suggestions. - Many of the changes below were initiated by him. - * Much more reliable card initialization. - * Highly improved performance - - optional write buffering - - better cluster handling - - remove unneeded SPI access when reading from buffered block - - use highest spi frequency after card initialization - * Add superfloppy support. - * Better checks when opening a FAT16 filesystem. - * Provide SPI pin mappings for commonly used ATmegas. - * Fix resizing files, hangs could occur. - * Fix overflow when creating files with names longer than 31 characters. - * Fix numerous other small things. - -2006-03-19 sd-reader - * Fix speed regressions. - -2006-03-16 sd-reader - * Initial release. - diff --git a/master/master/lib/sd/FAQ b/master/master/lib/sd/FAQ deleted file mode 100644 --- a/master/master/lib/sd/FAQ +++ /dev/null @@ -1,124 +0,0 @@ -Frequently Asked Questions for sd-reader -======================================== - -General -------- - -Q: Which cards are supported? -A: All MMC/SD/SDHC/miniSD/microSD/microSDHC should work, although not all variants have been tested. - Some very old (low capacity) cards might be broken as well. Cards with a capacity of 16 MB or - less are usually formatted with FAT12, so these are supported in raw mode only, if at all. - -Q: What data rates can I expect? -A: See the benchmark page on the homepage. - http://www.roland-riegel.de/sd-reader/benchmarks/ - -Q: Are there boards available for purchase? -A: No. - -Hardware --------- - -Q: Where can I find the schematic? -A: Get it on the homepage. - http://www.roland-riegel.de/sd-reader/sd-reader_circuit_latest.zip - -Q: What if my card socket has no Card-Detect and/or Card-Lock switches? -A: Change sd_raw_config.h such that it looks like - - #define configure_pin_available() /* nothing */ - #define configure_pin_locked() /* nothing */ - - #define get_pin_available() 0 - #define get_pin_locked() 1 - -Q: All attempts to write to the card fail, although reading works. What's the problem? -A: Enable write support within sd_raw_config.h. And probably, your card socket has no Card-lock - detection (see question above). - -Q: The card initialization fails. What can I do? -A: Usually this is some kind of hardware problem. - * Check the physical connections. - * Keep the signal lines to the card as short as possible. - * Do not use diodes to derive the card's supply voltage. Use a 3.3V voltage regulator instead. - * Have a stable, buffered power supply and use capacitors for correct voltage regulator - operation (see the schematics linked above). - * Use extra capacitors of 50uF and 100nF as close to the card as possible. - * When using an integrated level shifter or no level shifting at all (see the next question), - try adding a pullup of 50k from the data-out line of the card to 3.3V. - * Make sure the limiting frequency of the level shifter is not exceeded. Have a look into its - datasheet! - * Check the signals with a scope. - -Q: What alternatives to resistor level shifting exist? -A: If you want to use additional devices with SPI or the resistor solution appears too ugly, there - are two possibilities. Either operate the whole circuit with a single 3.3V supply and no level - shifting at all or use a level shifting IC which interfaces the memory card to the AVR. - Depending on your requirements, adequate devices could include MAX3378E, MAX3392E, MAX3395E, - 74LVC245 and 74HCT4050 (optionally together with 74HCT125). Please check the datasheets for the - required DC/AC characteristics! - -Software --------- - -Q: What's the software license? -A: GPLv2 or (for most parts) LGPLv2.1. Before using a file, read its license terms included at the - beginning of the file. - -Q: What's the programming language used? -A: It's C, in compliance with the ISO C99 standard. - -Q: What are these .patch-files provided? -A: Those record the source code differences between the old and new release. If you edited your - private sd-reader version, it might be easier to apply the patch files using the "patch" utility - common on Unix-like systems, rather than manually inserting the changes by hand. For Windows - users, the GnuWin32 project provides a port of "patch". - -Q: Where can I learn more about the library interface and how to use it? -A: Look into the HTML documentation provided online or within each source distribution. Also take - the provided main.c as an example application and as a starting point. - -Q: How do I adapt it to an ATmegaXYZ and my circuit in particular? -A: Add your MCU-specific pin configuration to sd_raw_config.h. Some commonly used ones are already - included. - -Q: How do I adapt it to a different MCU clock? -A: Change the MCU_FREQ variable within the Makefile. - -Q: How do I adapt it to some different MCU architecture? -A: Change sd_raw_init(), sd_raw_send_byte(), sd_raw_rec_byte() within sd_raw.c and the pin - definitions within sd_raw_config.h appropriately. - -Q: Can the library be used with Arduino? -A: Yes. I do not have any experience with Arduino myself, but people report that it works with some - minor modifications to the library code needed due to some different compiler settings. Please - search the web for details. - -Q: Can I use software SPI? -A: Yes, but the code is not prepared for it. - -Q: My application crashes somewhere in the lib. Is there some bug hidden? -A: There might be a bug. Much more likely, however, is that you experience memory problems, - especially heap/stack collisions. The crashes can appear everywhere, but typically this is not - the place where the real problem is. Try to minimize the size of structures and other memory - blocks your application uses. Sum up the amount of memory your application allocates with global - and local variables and the memory you allocate dynamically with malloc(). The avr-nm utility - also helps a lot here. When called with the "--print-size --size-sort" parameters, it lists all - symbols and their code/memory size within the given file. See the avr-libc FAQ and the nm manual - for further information. - http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_ramoverlap - http://sourceware.org/binutils/docs/binutils/nm.html - -Q: Opening the FAT filesystem fails. What can I do? -A: Make sure there is a FAT16 or FAT32 filesystem on the card. This usually isn't possible for old - cards with 16 MB or less. For larger ones, format the cards using the OS utilities, like the - Windows Explorer, the Unix/Linux mkdosfs command or special SD card format tools. - http://panasonic.jp/support/global/cs/sd/download/sd_formatter.html - http://www.sdcard.org/consumers/formatter/ - -Q: Writing to the card returns no failure, but when checking the file's content the data is not - there. What happens? -A: For performance reasons, writing to the card is always buffered. Before pulling the card out of - its socket (or issuing a reset of the MCU), make sure sd_raw_sync() gets called such that all - buffered data is written out to permanent card storage. - diff --git a/master/master/lib/sd/byteordering.c b/master/master/lib/sd/byteordering.c deleted file mode 100644 --- a/master/master/lib/sd/byteordering.c +++ /dev/null @@ -1,110 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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); -} - -/** - * @} - */ - diff --git a/master/master/lib/sd/byteordering.h b/master/master/lib/sd/byteordering.h deleted file mode 100644 --- a/master/master/lib/sd/byteordering.h +++ /dev/null @@ -1,188 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - -#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 - diff --git a/master/master/lib/sd/doc/Thumbs.db b/master/master/lib/sd/doc/Thumbs.db deleted file mode 100644 index ffd4e57f63c1edb4c2aa8605b979ebe8bd97f351..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ - * - * 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 - -#if USE_DYNAMIC_MEMORY - #include -#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 - diff --git a/master/master/lib/sd/fat.h b/master/master/lib/sd/fat.h deleted file mode 100644 --- a/master/master/lib/sd/fat.h +++ /dev/null @@ -1,131 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#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 - diff --git a/master/master/lib/sd/fat_config.h b/master/master/lib/sd/fat_config.h deleted file mode 100644 --- a/master/master/lib/sd/fat_config.h +++ /dev/null @@ -1,128 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#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 - diff --git a/master/master/lib/sd/partition.c b/master/master/lib/sd/partition.c deleted file mode 100644 --- a/master/master/lib/sd/partition.c +++ /dev/null @@ -1,155 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - -#if USE_DYNAMIC_MEMORY - #include -#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; -} - -/** - * @} - */ - diff --git a/master/master/lib/sd/partition.h b/master/master/lib/sd/partition.h deleted file mode 100644 --- a/master/master/lib/sd/partition.h +++ /dev/null @@ -1,212 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#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 - diff --git a/master/master/lib/sd/partition_config.h b/master/master/lib/sd/partition_config.h deleted file mode 100644 --- a/master/master/lib/sd/partition_config.h +++ /dev/null @@ -1,44 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - diff --git a/master/master/lib/sd/sd-reader_config.h b/master/master/lib/sd/sd-reader_config.h deleted file mode 100644 --- a/master/master/lib/sd/sd-reader_config.h +++ /dev/null @@ -1,53 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - diff --git a/master/master/lib/sd/sd_raw.c b/master/master/lib/sd/sd_raw.c deleted file mode 100644 --- a/master/master/lib/sd/sd_raw.c +++ /dev/null @@ -1,1004 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#include -#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 - SPCR0 = (0 << SPIE0) | // SPI Interrupt Enable - (1 << SPE0) | // SPI Enable - (0 << DORD0) | // Data Order: MSB first - (1 << MSTR0) | // Master mode - (0 << CPOL0) | // Clock Polarity: SCK low when idle - (0 << CPHA0) | // Clock Phase: sample on rising SCK edge - (1 << SPR10); // Clock Frequency: f_OSC / 64 which gives over 100khz required minimum clock - SPSR0 &= ~(1 << SPI2X0); // No doubled clock frequency - - /* initialization procedure */ - sd_raw_card_type = 0; - - if(!sd_raw_available()) - return 0; - - /* card needs 74 cycles minimum to start up */ - for(uint8_t i = 0; i < 10; ++i) - { - /* wait 8 clock cycles */ - sd_raw_rec_byte(); - } - - /* address card */ - select_card(); - - /* reset card */ - uint8_t response; - for(uint16_t i = 0; ; ++i) - { - response = sd_raw_send_command(CMD_GO_IDLE_STATE, 0); - if(response == (1 << R1_IDLE_STATE)) - break; - - if(i == 0x1ff) - { - unselect_card(); - return 0; - } - } - -#if SD_RAW_SDHC - /* check for version of SD card specification */ - response = sd_raw_send_command(CMD_SEND_IF_COND, 0x100 /* 2.7V - 3.6V */ | 0xaa /* test pattern */); - if((response & (1 << R1_ILL_COMMAND)) == 0) - { - sd_raw_rec_byte(); - sd_raw_rec_byte(); - if((sd_raw_rec_byte() & 0x01) == 0) - return 0; /* card operation voltage range doesn't match */ - if(sd_raw_rec_byte() != 0xaa) - return 0; /* wrong test pattern */ - - /* card conforms to SD 2 card specification */ - sd_raw_card_type |= (1 << SD_RAW_SPEC_2); - } - else -#endif - { - /* determine SD/MMC card type */ - sd_raw_send_command(CMD_APP, 0); - response = sd_raw_send_command(CMD_SD_SEND_OP_COND, 0); - if((response & (1 << R1_ILL_COMMAND)) == 0) - { - /* card conforms to SD 1 card specification */ - sd_raw_card_type |= (1 << SD_RAW_SPEC_1); - } - else - { - /* MMC card */ - } - } - - /* wait for card to get ready */ - for(uint16_t i = 0; ; ++i) - { - if(sd_raw_card_type & ((1 << SD_RAW_SPEC_1) | (1 << SD_RAW_SPEC_2))) - { - uint32_t arg = 0; -#if SD_RAW_SDHC - if(sd_raw_card_type & (1 << SD_RAW_SPEC_2)) - arg = 0x40000000; -#endif - sd_raw_send_command(CMD_APP, 0); - response = sd_raw_send_command(CMD_SD_SEND_OP_COND, arg); - } - else - { - response = sd_raw_send_command(CMD_SEND_OP_COND, 0); - } - - if((response & (1 << R1_IDLE_STATE)) == 0) - break; - - if(i == 0x7fff) - { - unselect_card(); - return 0; - } - } - -#if SD_RAW_SDHC - if(sd_raw_card_type & (1 << SD_RAW_SPEC_2)) - { - if(sd_raw_send_command(CMD_READ_OCR, 0)) - { - unselect_card(); - return 0; - } - - if(sd_raw_rec_byte() & 0x40) - sd_raw_card_type |= (1 << SD_RAW_SPEC_SDHC); - - sd_raw_rec_byte(); - sd_raw_rec_byte(); - sd_raw_rec_byte(); - } -#endif - - /* set block size to 512 bytes */ - if(sd_raw_send_command(CMD_SET_BLOCKLEN, 512)) - { - unselect_card(); - return 0; - } - - /* deaddress card */ - unselect_card(); - - /* switch to highest SPI frequency possible */ - SPCR0 &= ~((1 << SPR10) | (1 << SPR00)); /* Clock Frequency: f_OSC / 4 */ - SPSR0 |= (1 << SPI2X0); /* 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) -{ - SPDR0 = b; - /* wait for byte to be shifted out */ - while(!(SPSR0 & (1 << SPIF0))); - SPSR0 &= ~(1 << SPIF0); -} - -/** - * \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 */ - - SPDR0 = 0xff; - while(!(SPSR0 & (1 << SPIF0))); - SPSR0 &= ~(1 << SPIF0); - - return SPDR0; -} - -/** - * \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; -} - diff --git a/master/master/lib/sd/sd_raw.h b/master/master/lib/sd/sd_raw.h deleted file mode 100644 --- a/master/master/lib/sd/sd_raw.h +++ /dev/null @@ -1,148 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 -#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 - diff --git a/master/master/lib/sd/sd_raw_config.h b/master/master/lib/sd/sd_raw_config.h deleted file mode 100644 --- a/master/master/lib/sd/sd_raw_config.h +++ /dev/null @@ -1,114 +0,0 @@ - -/* - * Copyright (c) 2006-2012 by Roland Riegel - * - * 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 - -#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 << DDRB5) //PB5 -#define configure_pin_sck() DDRB |= (1 << DDRB7) //PB7 -#define configure_pin_ss() DDRB |= (1 << DDRB0) | (1 << DDRB4); PORTB |= (1< + * + * 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); +} + +/** + * @} + */ + diff --git a/master/master/lib/sdcard/byteordering.h b/master/master/lib/sdcard/byteordering.h new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/byteordering.h @@ -0,0 +1,188 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 + +#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 + diff --git a/master/master/lib/sdcard/fat.c b/master/master/lib/sdcard/fat.c new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/fat.c @@ -0,0 +1,2551 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 + +#if USE_DYNAMIC_MEMORY + #include +#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 + diff --git a/master/master/lib/sdcard/fat.h b/master/master/lib/sdcard/fat.h new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/fat.h @@ -0,0 +1,131 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 +#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 + diff --git a/master/master/lib/sdcard/fat_config.h b/master/master/lib/sdcard/fat_config.h new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/fat_config.h @@ -0,0 +1,128 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 +#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 + diff --git a/master/master/lib/sdcard/partition.c b/master/master/lib/sdcard/partition.c new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/partition.c @@ -0,0 +1,155 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 + +#if USE_DYNAMIC_MEMORY + #include +#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; +} + +/** + * @} + */ + diff --git a/master/master/lib/sdcard/partition.h b/master/master/lib/sdcard/partition.h new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/partition.h @@ -0,0 +1,212 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 +#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 + diff --git a/master/master/lib/sdcard/partition_config.h b/master/master/lib/sdcard/partition_config.h new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/partition_config.h @@ -0,0 +1,44 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 + diff --git a/master/master/lib/sdcard/sd-reader_config.h b/master/master/lib/sdcard/sd-reader_config.h new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/sd-reader_config.h @@ -0,0 +1,53 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 + diff --git a/master/master/lib/sdcard/sd_raw.c b/master/master/lib/sdcard/sd_raw.c new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/sd_raw.c @@ -0,0 +1,1007 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 +#include +#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; +} + diff --git a/master/master/lib/sdcard/sd_raw.h b/master/master/lib/sdcard/sd_raw.h new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/sd_raw.h @@ -0,0 +1,148 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 +#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 + diff --git a/master/master/lib/sdcard/sd_raw_config.h b/master/master/lib/sdcard/sd_raw_config.h new file mode 100644 --- /dev/null +++ b/master/master/lib/sdcard/sd_raw_config.h @@ -0,0 +1,115 @@ + +/* + * Copyright (c) 2006-2012 by Roland Riegel + * + * 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 +#include + +#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< -#include - -// Module declarations -static void parse_sentence_type(const char * token); -static void parse_time(const char *token); -static void parse_status(const char *token); -static void parse_lat(const char *token); -static void parse_lat_hemi(const char *token); -static void parse_lon(const char *token); -static void parse_lon_hemi(const char *token); -static void parse_speed(const char *token); -static void parse_course(const char *token); -static void parse_altitude(const char *token); - -// Module types -typedef void (*t_nmea_parser)(const char *token); - - - - -// Module constants -static const t_nmea_parser unk_parsers[] = { - parse_sentence_type, // $GPxxx -}; - -static const t_nmea_parser gga_parsers[] = { - NULL, // $GPGGA - parse_time, // Time - NULL, // Latitude - NULL, // N/S - NULL, // Longitude - NULL, // E/W - NULL, // Fix quality - NULL, // Number of satellites - NULL, // Horizontal dilution of position - parse_altitude, // Altitude - NULL, // "M" (mean sea level) - NULL, // Height of GEOID (MSL) above WGS84 ellipsoid - NULL, // "M" (mean sea level) - NULL, // Time in seconds since the last DGPS update - NULL // DGPS station ID number -}; - -static const t_nmea_parser rmc_parsers[] = { - NULL, // $GPRMC - parse_time, // Time - parse_status, // A=active, V=void - parse_lat, // Latitude, - parse_lat_hemi, // N/S - parse_lon, // Longitude - parse_lon_hemi, // E/W - parse_speed, // Speed over ground in knots - parse_course, // Track angle in degrees (true) - NULL, // Date (DDMMYY) - NULL, // Magnetic variation - NULL // E/W -}; - - - -static const int NUM_OF_UNK_PARSERS = (sizeof(unk_parsers) / sizeof(t_nmea_parser)); -static const int NUM_OF_GGA_PARSERS = (sizeof(gga_parsers) / sizeof(t_nmea_parser)); -static const int NUM_OF_RMC_PARSERS = (sizeof(rmc_parsers) / sizeof(t_nmea_parser)); - - - -// Module variables -static t_sentence_type sentence_type = SENTENCE_UNK; -static bool at_checksum = false; -static unsigned char our_checksum = '$'; -static unsigned char their_checksum = 0; -static char token[16]; -static int num_tokens = 0; -static unsigned int offset = 0; -static bool active = false; -static char gga_time[7] = "", rmc_time[7] = ""; -static char new_time[7]; -static uint32_t new_seconds; -static float new_lat; -static float new_lon; -static char new_aprs_lat[9]; -static char new_aprs_lon[10]; -static float new_course; -static float new_speed; -static float new_altitude; - -// Public (extern) variables, readable from other modules -char gps_time[7]; // HHMMSS -uint32_t gps_seconds = 0; // seconds after midnight -float gps_lat = 0; -float gps_lon = 0; -char gps_aprs_lat[9]; -char gps_aprs_lon[10]; -float gps_course = 0; -float gps_speed = 0; -float gps_altitude = 0; - -// Module functions -/// convert hex to binary ?? -unsigned char from_hex(char a) -{ - if (a >= 'A' && a <= 'F'){ - return a - 'A' + 10; - }else if (a >= 'a' && a <= 'f'){ - return a - 'a' + 10; - }else if (a >= '0' && a <= '9'){ - return a - '0'; - }else{ - return 0; - } -} - -/// determine what type of message is being sent ?? -/// we only send one message type so may not be needed -void parse_sentence_type(const char *token) -{ - if (strcmp(token, "$GPGGA") == 0) { - sentence_type = SENTENCE_GGA; - } else if (strcmp(token, "$GPRMC") == 0) { - sentence_type = SENTENCE_RMC; - } else { - sentence_type = SENTENCE_UNK; - } -} - - -void parse_time(const char *token) -{ - // Time can have decimals (fractions of a second), but we only take HHMMSS - ///we prolly want the entire time (unix time) ?? - strncpy(new_time, token, 6); - // Terminate string - new_time[6] = '\0'; - - new_seconds = - ((new_time[0] - '0') * 10 + (new_time[1] - '0')) * 60 * 60UL + - ((new_time[2] - '0') * 10 + (new_time[3] - '0')) * 60 + - ((new_time[4] - '0') * 10 + (new_time[5] - '0')); -} - -/// not sure of exact purpose, besides the name... -void parse_status(const char *token) -{ - // "A" = active, "V" = void. We should disregard void sentences - if (strcmp(token, "A") == 0){ - active = true; - }else{ - active = false; - } -} - -/// this is a vital function -void parse_lat(const char *token) -{ - // Parses latitude in the format "DD" + "MM" (+ ".M{...}M") - /// not sure about the format... - char degs[3]; - if (strlen(token) >= 4) { - degs[0] = token[0]; - degs[1] = token[1]; - degs[2] = '\0'; - new_lat = atof(degs) + atof(token + 2) / 60; - } - // APRS-ready latitude - strncpy(new_aprs_lat, token, 7); - new_aprs_lat[7] = '\0'; -} - -/// if this is hemisphere it is not needed. we are in the north. -void parse_lat_hemi(const char *token) -{ - if (token[0] == 'S'){ - new_lat = -new_lat; - } - new_aprs_lat[7] = token[0]; - new_aprs_lon[8] = '\0'; -} - -/// another vital function -void parse_lon(const char *token) -{ - // Longitude is in the format "DDD" + "MM" (+ ".M{...}M") - /// again not sure of formatting... - char degs[4]; - if (strlen(token) >= 5) { - degs[0] = token[0]; - degs[1] = token[1]; - degs[2] = token[2]; - degs[3] = '\0'; - new_lon = atof(degs) + atof(token + 3) / 60; - } - // APRS-ready longitude - strncpy(new_aprs_lon, token, 8); - new_aprs_lon[8] = '\0'; -} - -/// if this is hemisphere it is not needed -void parse_lon_hemi(const char *token) -{ - if (token[0] == 'W'){ - new_lon = -new_lon; - } - new_aprs_lon[8] = token[0]; - new_aprs_lon[9] = '\0'; -} - -/// we do not need to record speed -void parse_speed(const char *token) -{ - new_speed = atof(token); -} - -/// we do not need to record course -void parse_course(const char *token) -{ - new_course = atof(token); -} - -/// will use this to validate pressure readings -void parse_altitude(const char *token) -{ - new_altitude = atof(token); -} - - -// -// Exported functions -// -/// void zeroing of data. presumably to be called at start -void gps_setup() { - strcpy(gps_time, "000000"); - strcpy(gps_aprs_lat, "0000.00N"); - strcpy(gps_aprs_lon, "00000.00E"); -} - -/// MKa GPS transmission parser START -void parse_gps_transmission(char gpsToken){ - // i think c is the most recent character of transmission and is constantly - // tested if terminal character. if terminal then do parse on previous transmission. - - // $--GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx - if(gpsToken == '$') //start of transmission sentence - { - - } -} - -/// MKa GPS transmission parser END - -/// process gps transmission -bool gps_decode(char c) -{ - int ret = false; - - switch(c) { - case '\r': - case '\n': // End of sentence - if (num_tokens && our_checksum == their_checksum) { ///checksum is valid (good transmission) - #ifdef DEBUG_GPS - Serial.print(" (OK!) "); - Serial.print(millis()); - #endif - // Return a valid position only when we have two rmc and gga - // messages with the same timestamp. - switch (sentence_type) { - case SENTENCE_UNK: - break; // Keeps gcc happy - case SENTENCE_GGA: - strcpy(gga_time, new_time); - break; - case SENTENCE_RMC: - strcpy(rmc_time, new_time); - break; - } - - // Valid position scenario: - // - // 1. The timestamps of the two previous GGA/RMC sentences must match. - // - // 2. We just processed a known (GGA/RMC) sentence. Suppose the - // contrary: after starting up this module, gga_time and rmc_time - // are both equal (they're both initialized to ""), so (1) holds - // and we wrongly report a valid position. - // - // 3. The GPS has a valid fix. For some reason, the Venus 634FLPX - // reports 24 deg N, 121 deg E (the middle of Taiwan) until a valid - // fix is acquired: - // - // $GPGGA,120003.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,0000*69 (OK!) - // $GPGSA,A,1,,,,,,,,,,,,,0.0,0.0,0.0*30 (OK!) - // $GPRMC,120003.000,V,2400.0000,N,12100.0000,E,000.0,000.0,280606,,,N*78 (OK!) - // $GPVTG,000.0,T,,M,000.0,N,000.0,K,N*02 (OK!) - - if (sentence_type != SENTENCE_UNK && // Known sentence? - strcmp(gga_time, rmc_time) == 0 && // RMC/GGA times match? - active) { // Valid fix? - // Atomically merge data from the two sentences - strcpy(gps_time, new_time); - gps_seconds = new_seconds; - gps_lat = new_lat; - gps_lon = new_lon; - strcpy(gps_aprs_lat, new_aprs_lat); - strcpy(gps_aprs_lon, new_aprs_lon); - gps_course = new_course; - gps_speed = new_speed; - gps_altitude = new_altitude; - ret = true; - } - } - #ifdef DEBUG_GPS - if (num_tokens){ - Serial.println(); - } - #endif - at_checksum = false; // CR/LF signals the end of the checksum - our_checksum = '$'; // Reset checksums - their_checksum = 0; - offset = 0; // Prepare for the next incoming sentence - num_tokens = 0; - sentence_type = SENTENCE_UNK; - break; - - case '*': - // Handle as ',', but prepares to receive checksum (ie. do not break) - at_checksum = true; - our_checksum ^= c; - - case ',': - // Process token - token[offset] = '\0'; - our_checksum ^= c; // Checksum the ',', undo the '*' - - // Parse token - switch (sentence_type) { - case SENTENCE_UNK: - if (num_tokens < NUM_OF_UNK_PARSERS && unk_parsers[num_tokens]){ - unk_parsers[num_tokens](token); - } - break; - case SENTENCE_GGA: - if (num_tokens < NUM_OF_GGA_PARSERS && gga_parsers[num_tokens]){ - gga_parsers[num_tokens](token); - } - break; - case SENTENCE_RMC: - if (num_tokens < NUM_OF_RMC_PARSERS && rmc_parsers[num_tokens]){ - rmc_parsers[num_tokens](token); - } - break; - } - - // Prepare for next token - num_tokens++; - offset = 0; - #ifdef DEBUG_GPS - Serial.print(c); - #endif - break; - - default: - // Any other character - if (at_checksum) { - // Checksum value - their_checksum = their_checksum * 16 + from_hex(c); - } else { - // Regular NMEA data - if (offset < 15) { // Avoid buffer overrun (tokens can't be > 15 chars) - token[offset] = c; - offset++; - our_checksum ^= c; - } - } - #ifdef DEBUG_GPS - Serial.print(c); - #endif - } - return ret; -} - diff --git a/master/master/lib/trackuinoGPS/gps.cpp b/master/master/lib/trackuinoGPS/gps.cpp deleted file mode 100644 --- a/master/master/lib/trackuinoGPS/gps.cpp +++ /dev/null @@ -1,367 +0,0 @@ -/* trackuino copyright (C) 2010 EA5HAV Javi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/*<--delete this - -#include "config.h" -#include "gps.h" -#include -#include -#include - -// Module declarations -static void parse_sentence_type(const char * token); -static void parse_time(const char *token); -static void parse_status(const char *token); -static void parse_lat(const char *token); -static void parse_lat_hemi(const char *token); -static void parse_lon(const char *token); -static void parse_lon_hemi(const char *token); -static void parse_speed(const char *token); -static void parse_course(const char *token); -static void parse_altitude(const char *token); - -// Module types -typedef void (*t_nmea_parser)(const char *token); - -enum t_sentence_type { - SENTENCE_UNK, - SENTENCE_GGA, - SENTENCE_RMC -}; - - -// Module constants -static const t_nmea_parser unk_parsers[] = { - parse_sentence_type, // $GPxxx -}; - -static const t_nmea_parser gga_parsers[] = { - NULL, // $GPGGA - parse_time, // Time - NULL, // Latitude - NULL, // N/S - NULL, // Longitude - NULL, // E/W - NULL, // Fix quality - NULL, // Number of satellites - NULL, // Horizontal dilution of position - parse_altitude, // Altitude - NULL, // "M" (mean sea level) - NULL, // Height of GEOID (MSL) above WGS84 ellipsoid - NULL, // "M" (mean sea level) - NULL, // Time in seconds since the last DGPS update - NULL // DGPS station ID number -}; - -static const t_nmea_parser rmc_parsers[] = { - NULL, // $GPRMC - parse_time, // Time - parse_status, // A=active, V=void - parse_lat, // Latitude, - parse_lat_hemi, // N/S - parse_lon, // Longitude - parse_lon_hemi, // E/W - parse_speed, // Speed over ground in knots - parse_course, // Track angle in degrees (true) - NULL, // Date (DDMMYY) - NULL, // Magnetic variation - NULL // E/W -}; - -static const int NUM_OF_UNK_PARSERS = (sizeof(unk_parsers) / sizeof(t_nmea_parser)); -static const int NUM_OF_GGA_PARSERS = (sizeof(gga_parsers) / sizeof(t_nmea_parser)); -static const int NUM_OF_RMC_PARSERS = (sizeof(rmc_parsers) / sizeof(t_nmea_parser)); - -// Module variables -static t_sentence_type sentence_type = SENTENCE_UNK; -static bool at_checksum = false; -static unsigned char our_checksum = '$'; -static unsigned char their_checksum = 0; -static char token[16]; -static int num_tokens = 0; -static unsigned int offset = 0; -static bool active = false; -static char gga_time[7] = "", rmc_time[7] = ""; -static char new_time[7]; -static uint32_t new_seconds; -static float new_lat; -static float new_lon; -static char new_aprs_lat[9]; -static char new_aprs_lon[10]; -static float new_course; -static float new_speed; -static float new_altitude; - -// Public (extern) variables, readable from other modules -char gps_time[7]; // HHMMSS -uint32_t gps_seconds = 0; // seconds after midnight -float gps_lat = 0; -float gps_lon = 0; -char gps_aprs_lat[9]; -char gps_aprs_lon[10]; -float gps_course = 0; -float gps_speed = 0; -float gps_altitude = 0; - -// Module functions -/// converts from hex to binary ?? -unsigned char from_hex(char a) -{ - if (a >= 'A' && a <= 'F') - return a - 'A' + 10; - else if (a >= 'a' && a <= 'f') - return a - 'a' + 10; - else if (a >= '0' && a <= '9') - return a - '0'; - else - return 0; -} - -/// determines what type of message is being sent ?? -void parse_sentence_type(const char *token) -{ - if (strcmp(token, "$GPGGA") == 0) { - sentence_type = SENTENCE_GGA; - } else if (strcmp(token, "$GPRMC") == 0) { - sentence_type = SENTENCE_RMC; - } else { - sentence_type = SENTENCE_UNK; - } -} - -void parse_time(const char *token) -{ - // Time can have decimals (fractions of a second), but we only take HHMMSS - strncpy(new_time, token, 6); - // Terminate string - new_time[6] = '\0'; - - new_seconds = - ((new_time[0] - '0') * 10 + (new_time[1] - '0')) * 60 * 60UL + - ((new_time[2] - '0') * 10 + (new_time[3] - '0')) * 60 + - ((new_time[4] - '0') * 10 + (new_time[5] - '0')); -} - -void parse_status(const char *token) -{ - // "A" = active, "V" = void. We shoud disregard void sentences - if (strcmp(token, "A") == 0) - active = true; - else - active = false; -} - -void parse_lat(const char *token) -{ - // Parses latitude in the format "DD" + "MM" (+ ".M{...}M") - char degs[3]; - if (strlen(token) >= 4) { - degs[0] = token[0]; - degs[1] = token[1]; - degs[2] = '\0'; - new_lat = atof(degs) + atof(token + 2) / 60; - } - // APRS-ready latitude - strncpy(new_aprs_lat, token, 7); - new_aprs_lat[7] = '\0'; -} - -void parse_lat_hemi(const char *token) -{ - if (token[0] == 'S') - new_lat = -new_lat; - new_aprs_lat[7] = token[0]; - new_aprs_lon[8] = '\0'; -} - -void parse_lon(const char *token) -{ - // Longitude is in the format "DDD" + "MM" (+ ".M{...}M") - char degs[4]; - if (strlen(token) >= 5) { - degs[0] = token[0]; - degs[1] = token[1]; - degs[2] = token[2]; - degs[3] = '\0'; - new_lon = atof(degs) + atof(token + 3) / 60; - } - // APRS-ready longitude - strncpy(new_aprs_lon, token, 8); - new_aprs_lon[8] = '\0'; -} - -void parse_lon_hemi(const char *token) -{ - if (token[0] == 'W') - new_lon = -new_lon; - new_aprs_lon[8] = token[0]; - new_aprs_lon[9] = '\0'; -} - -void parse_speed(const char *token) -{ - new_speed = atof(token); -} - -void parse_course(const char *token) -{ - new_course = atof(token); -} - -void parse_altitude(const char *token) -{ - new_altitude = atof(token); -} - - -// -// Exported functions -// -void gps_setup() { - strcpy(gps_time, "000000"); - strcpy(gps_aprs_lat, "0000.00N"); - strcpy(gps_aprs_lon, "00000.00E"); -} - -bool gps_decode(char c) -{ - int ret = false; - - switch(c) { - case '\r': - case '\n': - // End of sentence - - if (num_tokens && our_checksum == their_checksum) { -#ifdef DEBUG_GPS - Serial.print(" (OK!) "); - Serial.print(millis()); -#endif - // Return a valid position only when we've got two rmc and gga - // messages with the same timestamp. - switch (sentence_type) { - case SENTENCE_UNK: - break; // Keeps gcc happy - case SENTENCE_GGA: - strcpy(gga_time, new_time); - break; - case SENTENCE_RMC: - strcpy(rmc_time, new_time); - break; - } - - // Valid position scenario: - // - // 1. The timestamps of the two previous GGA/RMC sentences must match. - // - // 2. We just processed a known (GGA/RMC) sentence. Suppose the - // contrary: after starting up this module, gga_time and rmc_time - // are both equal (they're both initialized to ""), so (1) holds - // and we wrongly report a valid position. - // - // 3. The GPS has a valid fix. For some reason, the Venus 634FLPX - // reports 24 deg N, 121 deg E (the middle of Taiwan) until a valid - // fix is acquired: - // - // $GPGGA,120003.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,0000*69 (OK!) - // $GPGSA,A,1,,,,,,,,,,,,,0.0,0.0,0.0*30 (OK!) - // $GPRMC,120003.000,V,2400.0000,N,12100.0000,E,000.0,000.0,280606,,,N*78 (OK!) - // $GPVTG,000.0,T,,M,000.0,N,000.0,K,N*02 (OK!) - - if (sentence_type != SENTENCE_UNK && // Known sentence? - strcmp(gga_time, rmc_time) == 0 && // RMC/GGA times match? - active) { // Valid fix? - // Atomically merge data from the two sentences - strcpy(gps_time, new_time); - gps_seconds = new_seconds; - gps_lat = new_lat; - gps_lon = new_lon; - strcpy(gps_aprs_lat, new_aprs_lat); - strcpy(gps_aprs_lon, new_aprs_lon); - gps_course = new_course; - gps_speed = new_speed; - gps_altitude = new_altitude; - ret = true; - } - } -#ifdef DEBUG_GPS - if (num_tokens) - Serial.println(); -#endif - at_checksum = false; // CR/LF signals the end of the checksum - our_checksum = '$'; // Reset checksums - their_checksum = 0; - offset = 0; // Prepare for the next incoming sentence - num_tokens = 0; - sentence_type = SENTENCE_UNK; - break; - - case '*': - // Handle as ',', but prepares to receive checksum (ie. do not break) - at_checksum = true; - our_checksum ^= c; - - case ',': - // Process token - token[offset] = '\0'; - our_checksum ^= c; // Checksum the ',', undo the '*' - - // Parse token - switch (sentence_type) { - case SENTENCE_UNK: - if (num_tokens < NUM_OF_UNK_PARSERS && unk_parsers[num_tokens]) - unk_parsers[num_tokens](token); - break; - case SENTENCE_GGA: - if (num_tokens < NUM_OF_GGA_PARSERS && gga_parsers[num_tokens]) - gga_parsers[num_tokens](token); - break; - case SENTENCE_RMC: - if (num_tokens < NUM_OF_RMC_PARSERS && rmc_parsers[num_tokens]) - rmc_parsers[num_tokens](token); - break; - } - - // Prepare for next token - num_tokens++; - offset = 0; -#ifdef DEBUG_GPS - Serial.print(c); -#endif - break; - - default: - // Any other character - if (at_checksum) { - // Checksum value - their_checksum = their_checksum * 16 + from_hex(c); - } else { - // Regular NMEA data - if (offset < 15) { // Avoid buffer overrun (tokens can't be > 15 chars) - token[offset] = c; - offset++; - our_checksum ^= c; - } - } -#ifdef DEBUG_GPS - Serial.print(c); -#endif - } - return ret; -} - diff --git a/master/master/lib/trackuinoGPS/gps.h b/master/master/lib/trackuinoGPS/gps.h deleted file mode 100644 --- a/master/master/lib/trackuinoGPS/gps.h +++ /dev/null @@ -1,46 +0,0 @@ -/* trackuino copyright (C) 2010 EA5HAV Javi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __GPS_H__ -#define __GPS_H__ - -#include -#include -#include -#include - -extern char gps_time[7]; // HHMMSS -extern uint32_t gps_seconds; // seconds after midnight -extern char gps_date[7]; // DDMMYY -extern float gps_lat; -extern float gps_lon; -extern char gps_aprs_lat[9]; -extern char gps_aprs_lon[10]; -extern float gps_course; -extern float gps_speed; -extern float gps_altitude; - - - -void gps_setup(); -bool gps_decode(char c); -typedef enum t_sentence_type { - SENTENCE_UNK, - SENTENCE_GGA, - SENTENCE_RMC -}t_sentence_type; -#endif diff --git a/master/master/lib/trackuinoGPS/gpsMKa.c b/master/master/lib/trackuinoGPS/gpsMKa.c deleted file mode 100644 --- a/master/master/lib/trackuinoGPS/gpsMKa.c +++ /dev/null @@ -1,700 +0,0 @@ -/* -* gpsMKa.c -* -* Created: 11/15/2012 12:02:38 PM -* Author: mkanning -*/ - -#include -#include -#include -#include "gpsMKa.h" -#include "../serial.h" -#include "../../config.h" -#include "../led.h" - - - -// Circular buffer for incoming data -uint8_t nmeaBuffer[NMEABUFFER_SIZE]; - -// Location of parser in the buffer -uint8_t nmeaBufferParsePosition = 0; - -// Location of receive byte interrupt in the buffer -volatile uint16_t nmeaBufferDataPosition = 0; - - -/* -volatile uint8_t charTest; -ISR(USART1_RX_vect) -{ - serial0_sendChar(UDR1); - -}*/ - -// holds the byte ALREADY PARSED. includes starting character -int bytesReceived = 0; - -//data (and checksum) of most recent transmission -char data[16]; - -//used to skip over bytes during parse -int skipBytes = 0; - -//used to index data arrays during data collection -int numBytes = 0; - -//variables to store data from transmission -//least significant digit is stored at location 0 of arrays -char tramsmissionType[7]; - -char timestamp[12]; //hhmmss.ss -char* get_timestamp() { - return timestamp; -} - -char latitude[14]; //lllll.lla -char* get_latitude() { - return latitude; -} - -char longitude[14]; //yyyyy.yyb -char* get_longitude() { - return longitude; -} - - -char quality; //quality for GGA and validity for RMC -char numSatellites[4]; -char* getNumSatelites() { - return numSatellites; -} - -char hdop[6]; //xx.x -char* get_hdop() { - return hdop; -} - -char altitude[10]; //xxxxxx.x -char wgs84Height[8]; //sxxx.x -char lastUpdated[8]; //blank - included for testing -char stationID[8]; //blank - included for testing -char checksum[3]; //xx - -char knots[8]; //xxx.xx -char* get_speedKnots() { - return knots; -} - -char course[8]; //xxx.x -char* get_course() { - return course; -} - -char dayofmonth[9]; //ddmmyy -char* get_dayofmonth() { - return dayofmonth; -} - -char variation[9]; //xxx.xb -int calculatedChecksum; -int receivedChecksum; - -char convertBuf1[15]; -char convertBuf2[15]; - -// transmission state machine -enum decodeState { - //shared fields - INITIALIZE=0, - GET_TYPE, - GPS_CHECKSUM, //XOR of all the bytes between the $ and the * (not including the delimiters themselves), written in hexadecimal - //GGA data fields - GGA_TIME, - GGA_LATITUDE, - GGA_LONGITUDE, - GGA_QUALITY, - GGA_SATELLITES, - GGA_HDOP, - GGA_ALTITUDE, - GGA_WGS84, - GGA_LAST_UPDATE, - GGA_STATION_ID, - //RMC data fields - RMC_TIME, - RMC_VALIDITY, - RMC_LATITUDE, - RMC_LONGITUDE, - RMC_KNOTS, - RMC_COURSE, - RMC_DATE, - RMC_MAG_VARIATION, - -}decodeState; - - -char debugBuff[128]; - -ISR(USART1_RX_vect) -{ - nmeaBuffer[nmeaBufferDataPosition % NMEABUFFER_SIZE] = UDR1; - nmeaBufferDataPosition = (nmeaBufferDataPosition + 1) % NMEABUFFER_SIZE; - //serial0_sendChar(UDR1); - //snprintf(debugBuff, 32, "GPS: bdp: %d, bpp: %d decodestate: %u \r\n", nmeaBufferDataPosition, nmeaBufferParsePosition, decodeState); - //serial0_sendString((debugBuff)); -} - - -// Could inline if program space available -static void setParserState(uint8_t state) -{ - decodeState = state; - - - // If resetting, clear vars - if(state == INITIALIZE) - { - calculatedChecksum = 0; - } - - // Every time we change state, we have parsed a byte - nmeaBufferParsePosition = (nmeaBufferParsePosition + 1) % NMEABUFFER_SIZE; -} - - - -//// MKa GPS transmission parser START -void parse_gps_transmission(void){ - - // Pull byte off of the buffer - char byte; - - - - while(nmeaBufferDataPosition != nmeaBufferParsePosition) { - led_on(LED_ACTIVITY); - - byte = nmeaBuffer[nmeaBufferParsePosition]; - - //snprintf(debugBuff, 64, "GPSParse: byte [%c] decodestate: %u bp: %u pp: %u\r\n", byte, decodeState, nmeaBufferDataPosition, nmeaBufferParsePosition); - //serial0_sendString((debugBuff)); - - if(decodeState == INITIALIZE) //start of transmission sentence - { - if(byte == '$') { - #ifdef DEBUG_NMEA - serial0_sendString("found $\r\n"); - #endif - - setParserState(GET_TYPE); - numBytes = 0; //prep for next phases - skipBytes = 0; - calculatedChecksum = 0; - } - - else { - setParserState(INITIALIZE); - } - } - - //parse transmission type - else if (decodeState == GET_TYPE) - { - tramsmissionType[numBytes] = byte; - numBytes++; - - #ifdef DEBUG_NMEA - serial0_sendString("stored a type byte\r\n"); - #endif - - if(byte == ',') //end of this data type - { - tramsmissionType[5] = 0x00; - - if (tramsmissionType[2] == 'G' && - tramsmissionType[3] == 'G' && - tramsmissionType[4] == 'A') - { - setParserState(GGA_TIME); - numBytes = 0; - } - else if (tramsmissionType[2] == 'R' && - tramsmissionType[3] == 'M' && - tramsmissionType[4] == 'C') - { - setParserState(RMC_TIME); - numBytes = 0; - } - else //this is an invalid transmission type - { - setParserState(INITIALIZE); - } - } - else { - // continue - setParserState(GET_TYPE); - } - - } - - ///parses GGA transmissions START - /// $--GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*xx - //timestamp - else if (decodeState == GGA_TIME) - { - if (byte == ',') //end of this data type - { - timestamp[4] = 0x00; // Cut off at 4 (no seconds) for APRS - setParserState(GGA_LATITUDE); - skipBytes = 0; //prep for next phase of parse - numBytes = 0; - } - else //store data - { - #ifdef DEBUG_NMEA - serial0_sendString("found GGA time byte\r\n"); - #endif - - setParserState(GGA_TIME); - timestamp[numBytes] = byte; //byte; //adjust number of bytes to fit array - numBytes++; - } - } - - //latitude - else if (decodeState == GGA_LATITUDE) - { - if (byte == ',' && skipBytes == 0) //discard this byte - { - #ifdef DEBUG_NMEA - serial0_sendString("found lat skip byte\r\n"); - #endif - - skipBytes = 1; - setParserState(GGA_LATITUDE); - } - else if (byte == ',') //end of this data type - { - - latitude[7] = 0x00; // null terminate - - - // First 2 bytes - //convertBuf1[0] = latitude[0]; - //convertBuf1[1] = latitude[1]; - //convertBuf1[2] = '\0'; - //strncpy(convertBuf2, latitude, 7); - //convertBuf2[7] = '\0'; - - - setParserState(GGA_LONGITUDE); - skipBytes = 0; //prep for next phase of parse - numBytes = 0; - } - else //store data - { - #ifdef DEBUG_NMEA - serial0_sendString("found lat byte\r\n"); - #endif - - latitude[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - setParserState(GGA_LATITUDE); - } - } - - //longitude - else if (decodeState == GGA_LONGITUDE) - { - if (byte == ',' && skipBytes == 0) //discard this byte - { - #ifdef DEBUG_NMEA - serial0_sendString("found long skip byte\r\n"); - #endif - - skipBytes = 1; - setParserState(GGA_LONGITUDE); - } - else if (byte == ',') //end of this data type - { - longitude[8] = 0x00; - setParserState(GGA_QUALITY); - numBytes = 0; //prep for next phase of parse - skipBytes = 0; - } - else //store data - { - #ifdef DEBUG_NMEA - serial0_sendString("found long byte\r\n"); - #endif - - longitude[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - setParserState(GGA_LONGITUDE); - } - } - - //GGA quality - else if (decodeState == GGA_QUALITY) - { - if (byte == ',') //end of this data type - { - setParserState(GGA_SATELLITES); - numBytes = 0; //prep for next phase of parse - } - else //store data - { - #ifdef DEBUG_NMEA - serial0_sendString("found quality byte\r\n"); - #endif - - quality = byte; //maybe reset if invalid data ?? - setParserState(GGA_QUALITY); - } - } - - //number of satellites - else if (decodeState == GGA_SATELLITES) - { - if (byte == ',') //end of this data type - { - numSatellites[numBytes] = 0x00; - setParserState(GGA_HDOP); - numBytes = 0; //prep for next phase of parse - } - else //store data - { - numSatellites[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - setParserState(GGA_SATELLITES); - } - } - - //HDOP - else if (decodeState == GGA_HDOP) - { - if (byte == ',' ) //end of this data type - { - hdop[numBytes] = 0x00; - setParserState(GGA_ALTITUDE); - numBytes = 0; //prep for next phase of parse - skipBytes = 0; - } - else //store data - { - hdop[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - setParserState(GGA_HDOP); - } - } - - //altitude - else if (decodeState == GGA_ALTITUDE) - { - if (byte == ',' && skipBytes == 0) //discard this byte - { - skipBytes = 1; - setParserState(GGA_ALTITUDE); - } - else if(byte == ',') //end of this data type - { - altitude[numBytes] = 0x00; - setParserState(GGA_WGS84); - numBytes = 0; //prep for next phase of parse - } - else //store data - { - altitude[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - setParserState(GGA_ALTITUDE); - } - } - - //WGS84 Height - else if (decodeState == GGA_WGS84) - { - if (byte == ',' && skipBytes == 0) //discard this byte - { - skipBytes = 1; - setParserState(GGA_WGS84); - } - else if(byte == ',') //end of this data type - { - wgs84Height[numBytes] = 0x00; - setParserState(GGA_LAST_UPDATE); - skipBytes = 0; //prep for next phase of parse - numBytes = 0; - } - else //store data - { - wgs84Height[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - setParserState(GGA_WGS84); - } - } - - //last GGA DGPS update - else if (decodeState == GGA_LAST_UPDATE) - { - if (byte == ',') //end of this data type - { - lastUpdated[numBytes] = 0x00; - setParserState(GGA_STATION_ID); - numBytes = 0; //prep for next phase of parse - } - else //store data - this should be blank - { - lastUpdated[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - setParserState(GGA_LAST_UPDATE); - } - } - - //GGA DGPS station ID - else if (decodeState == GGA_STATION_ID) - { - if (byte == ',' || byte == '*') //end of this data type - { - stationID[numBytes] = 0x00; - setParserState(GPS_CHECKSUM); - numBytes = 0; //prep for next phase of parse - } - else //store data - this should be blank - { - stationID[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - setParserState(GGA_STATION_ID); - } - } - ///parses GGA transmissions END - - /// $GPRMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,ddmmyy,x.x,a*hh - ///parses RMC transmissions - //time - // emz: commented setter, GMC time better? - else if(decodeState == RMC_TIME) - { - if (byte == ',') //end of this data type - { - //timestamp[numBytes] = 0x00; - setParserState(RMC_VALIDITY); - numBytes = 0; //prep for next phase of parse - } - else //store data - { - #ifdef DEBUG_NMEA - serial0_sendString("found time byte\r\n"); - #endif - - //timestamp[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - setParserState(RMC_TIME); - } - } - - //validity - // not needed? dupe gga - else if(decodeState == RMC_VALIDITY) - { - if (byte == ',') //end of this data type - { - setParserState(RMC_LATITUDE); - skipBytes = 0; //prep for next phase of parse - numBytes = 0; - } - else //store data - { - #ifdef DEBUG_NMEA - serial0_sendString("found valid byte\r\n"); - #endif - - //quality = byte; - numBytes++; - setParserState(RMC_VALIDITY); - } - } - - //latitude RMC (we don't need this, commented out setter) - else if(decodeState == RMC_LATITUDE) - { - if (byte == ',' && skipBytes == 0) //discard this byte - { - #ifdef DEBUG_NMEA - serial0_sendString("found lat skip byte\r\n"); - #endif - - skipBytes = 1; - setParserState(RMC_LATITUDE); - } - else if (byte == ',') //end of this data type - { - setParserState(RMC_LONGITUDE); - skipBytes = 0; //prep for next phase of parse - numBytes = 0; - } - else //store data - { - #ifdef DEBUG_NMEA - serial0_sendString("found lat byte\r\n"); - #endif - - //latitude[numBytes]= byte; //adjust number of bytes to fit array - numBytes++; - setParserState(RMC_LATITUDE); - } - } - - //longitude RMC (we don't need this, commented out setter) - else if(decodeState == RMC_LONGITUDE) - { - if (byte == ',' && skipBytes == 0) //discard this byte - { - #ifdef DEBUG_NMEA - serial0_sendString("found long byte\r\n"); - #endif - - skipBytes = 1; - setParserState(RMC_LONGITUDE); - } - else if (byte == ',') //end of this data type - { - setParserState(RMC_KNOTS); - skipBytes = 0; - numBytes = 0; - } - else //store data - { - #ifdef DEBUG_NMEA - serial0_sendString("found long byte\r\n"); - #endif - - //longitude[numBytes]= byte; //adjust number of bytes to fit array - numBytes++; - setParserState(RMC_LONGITUDE); - } - } - - //knots - else if(decodeState == RMC_KNOTS) - { - if (byte == ',') //end of this data type - { - knots[numBytes] = 0x00; - setParserState(RMC_COURSE); - numBytes = 0; //prep for next phase of parse - } - else //store data - { - setParserState(RMC_KNOTS); - knots[numBytes]= byte; //adjust number of bytes to fit array - numBytes++; - } - } - - //course - else if(decodeState == RMC_COURSE) - { - if (byte == ',') //end of this data type - { - course[numBytes] = 0x00; - setParserState(RMC_DATE); - numBytes = 0; //prep for next phase of parse - } - else //store data - { - setParserState(RMC_COURSE); - course[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - } - } - - //date - else if(decodeState == RMC_DATE) - { - if (byte == ',') //end of this data type - { - // Cut it off at day of month. Also has month and year if we ever need it. - dayofmonth[2] = 0x00; - setParserState(RMC_MAG_VARIATION); - skipBytes = 0; //prep for next phase of parse - numBytes = 0; - } - else //store data - { - setParserState(RMC_DATE); - dayofmonth[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - } - } - - //magnetic variation - else if(decodeState == RMC_MAG_VARIATION) - { - if (byte == '*') //end of this data type - { - variation[numBytes] = 0x00; - setParserState(GPS_CHECKSUM); - numBytes = 0; //prep for next phase of parse - } - else //store data - { - setParserState(RMC_MAG_VARIATION); - variation[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - } - } - ///parses RMC transmissions END - - - //checksum - else if (decodeState == GPS_CHECKSUM) - { - if (numBytes == 2) //end of data - terminating character ?? - { - //checksum calculator for testing http://www.hhhh.org/wiml/proj/nmeaxor.html - //TODO: must determine what to do with correct and incorrect messages - receivedChecksum = checksum[0] + (checksum[1]*16); //convert bytes to int - if(calculatedChecksum==receivedChecksum) - { - - } - else - { - - } - #ifdef DEBUG_NMEA - serial0_sendString("OMG GOT TO CHECKSUM!\r\n"); - #endif - - serial0_sendString((debugBuff)); - setParserState(INITIALIZE); - numBytes = 0; //prep for next phase of parse - } - else //store data - { - setParserState(GPS_CHECKSUM); - checksum[numBytes] = byte; //adjust number of bytes to fit array - numBytes++; - } - } - else { - setParserState(INITIALIZE); - } - - if (decodeState!=GPS_CHECKSUM && decodeState!=INITIALIZE) //want bytes between '$' and '*' - { - //input byte into running checksum - XORbyteWithChecksum(byte); - } - led_off(LED_ACTIVITY); - } - - -} -/// MKa GPS transmission parser END - -void XORbyteWithChecksum(uint8_t byte){ - calculatedChecksum ^= (int)byte; //this may need to be re-coded -} - diff --git a/master/master/lib/trackuinoGPS/gpsMKa.h b/master/master/lib/trackuinoGPS/gpsMKa.h deleted file mode 100644 --- a/master/master/lib/trackuinoGPS/gpsMKa.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * gpsMKa.h - * - * Created: 11/15/2012 12:02:53 PM - * Author: mkanning - */ - - -#ifndef GPSMKA_H_ -#define GPSMKA_H_ -#define GGA_MESSAGE -#define RMC_MESSAGE -#define UKN_MESSAGE - -char* get_longitude(); -char* get_latitude(); -char* get_timestamp(); -char* get_speedKnots(); -char* get_course(); -char* get_hdop(); -char* getNumSatelites(); -char* get_dayofmonth(); - -#endif /* GPSMKA_H_ */ \ No newline at end of file diff --git a/master/master/lib/trackuinoGPS/trackuino.pde b/master/master/lib/trackuinoGPS/trackuino.pde deleted file mode 100644 --- a/master/master/lib/trackuinoGPS/trackuino.pde +++ /dev/null @@ -1,142 +0,0 @@ -/* trackuino copyright (C) 2010 EA5HAV Javi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -// Mpide 22 fails to compile Arduino code because it stupidly defines ARDUINO -// as an empty macro (hence the +0 hack). UNO32 builds are fine. Just use the -// real Arduino IDE for Arduino builds. Optionally complain to the Mpide -// authors to fix the broken macro. -#if (ARDUINO + 0) == 0 -#error "Oops! We need the real Arduino IDE (version 22 or 23) for Arduino builds." -#error "See trackuino.pde for details on this" - -// Refuse to compile on arduino version 21 or lower. 22 includes an -// optimization of the USART code that is critical for real-time operation -// of the AVR code. -#elif (ARDUINO + 0) < 22 -#error "Oops! We need Arduino 22 or 23" -#error "See trackuino.pde for details on this" - -// Arduino 1.0+ introduced backwards-incompatible changes in the serial lib. -#elif (ARDUINO + 1) >= 100 -#error "Ooops! We don't support Arduino 1.0+ (yet). Please use 22 or 23" -#error "See trackuino.pde for details on this" - -#endif - - -// Trackuino custom libs -#include "config.h" -#include "afsk_avr.h" -#include "afsk_pic32.h" -#include "aprs.h" -#include "buzzer.h" -#include "gps.h" -#include "pin.h" -#include "power.h" -#include "sensors_avr.h" -#include "sensors_pic32.h" - -// Arduino/AVR libs -#include -#include - -// Module constants -static const uint32_t VALID_POS_TIMEOUT = 2000; // ms - -// Module variables -static int32_t next_aprs = 0; - - -void setup() -{ - pinMode(LED_PIN, OUTPUT); - pin_write(LED_PIN, LOW); - - Serial.begin(GPS_BAUDRATE); -#ifdef DEBUG_RESET - Serial.println("RESET"); -#endif - - buzzer_setup(); - afsk_setup(); - gps_setup(); - sensors_setup(); - -#ifdef DEBUG_SENS - Serial.print("Ti="); - Serial.print(sensors_int_lm60()); - Serial.print(", Te="); - Serial.print(sensors_ext_lm60()); - Serial.print(", Vin="); - Serial.println(sensors_vin()); -#endif - - // Do not start until we get a valid time reference - // for slotted transmissions. - if (APRS_SLOT >= 0) { - do { - while (! Serial.available()) - power_save(); - } while (! gps_decode(Serial.read())); - - next_aprs = millis() + 1000 * - (APRS_PERIOD - (gps_seconds + APRS_PERIOD - APRS_SLOT) % APRS_PERIOD); - } - else { - next_aprs = millis(); - } - // TODO: beep while we get a fix, maybe indicating the number of - // visible satellites by a series of short beeps? -} - -void get_pos() -{ - // Get a valid position from the GPS - int valid_pos = 0; - uint32_t timeout = millis(); - do { - if (Serial.available()) - valid_pos = gps_decode(Serial.read()); - } while ( (millis() - timeout < VALID_POS_TIMEOUT) && ! valid_pos) ; - - if (valid_pos) { - if (gps_altitude > BUZZER_ALTITUDE) { - buzzer_off(); // In space, no one can hear you buzz - } else { - buzzer_on(); - } - } -} - -void loop() -{ - // Time for another APRS frame - if ((int32_t) (millis() - next_aprs) >= 0) { - get_pos(); - aprs_send(); - next_aprs += APRS_PERIOD * 1000L; - while (afsk_busy()) ; - power_save(); - -#ifdef DEBUG_MODEM - // Show modem ISR stats from the previous transmission - afsk_debug(); -#endif - } - - power_save(); // Incoming GPS data or interrupts will wake us up -} diff --git a/master/master/master.c b/master/master/master.c --- a/master/master/master.c +++ b/master/master/master.c @@ -27,7 +27,7 @@ #include "lib/logger.h" #include "lib/watchdog.h" -#include "lib/trackuinoGPS/gpsMKa.h" +#include "lib/gps.h" #include "lib/i2c.h" #include "lib/boardtemp.h" diff --git a/master/master/master.cproj b/master/master/master.cproj --- a/master/master/master.cproj +++ b/master/master/master.cproj @@ -138,6 +138,12 @@ compile + + compile + + + compile + compile @@ -168,40 +174,40 @@ compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile @@ -228,12 +234,6 @@ compile - - compile - - - compile - compile @@ -246,8 +246,7 @@ - - + \ No newline at end of file