From: Bernie Innocenti Date: Sun, 20 Mar 2011 22:31:35 +0000 (-0400) Subject: Add FAT16 library X-Git-Url: https://codewiz.org/gitweb?p=rmslog.git;a=commitdiff_plain;h=a93a61d1163833d9bdee2026ba5a89a8863bf802 Add FAT16 library --- diff --git a/FAT16/byteordering.c b/FAT16/byteordering.c new file mode 100644 index 0000000..69330c9 --- /dev/null +++ b/FAT16/byteordering.c @@ -0,0 +1,62 @@ + +/* + * Copyright (c) 2006-2009 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 !(__AVR__) +/** + * Converts a 16-bit integer 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) +{ + return HTOL16(h); +} +#endif + +#if !(__AVR__) +/** + * Converts a 32-bit integer 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) +{ + return HTOL32(h); +} +#endif + +/** + * @} + */ + diff --git a/FAT16/byteordering.h b/FAT16/byteordering.h new file mode 100644 index 0000000..48ddffe --- /dev/null +++ b/FAT16/byteordering.h @@ -0,0 +1,137 @@ + +/* + * Copyright (c) 2006-2009 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 + */ + +/** + * \def HTOL16(val) + * + * Converts a 16-bit integer 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 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. + */ + +#if __AVR__ +#define HTOL16(val) (val) +#define HTOL32(val) (val) +#elif defined(BIG_ENDIAN) +#define HTOL16(val) ((((uint16_t) (val)) << 8) | \ + (((uint16_t) (val)) >> 8) \ + ) +#define HTOL32(val) (((((uint32_t) (val)) & 0x000000ff) << 24) | \ + ((((uint32_t) (val)) & 0x0000ff00) << 8) | \ + ((((uint32_t) (val)) & 0x00ff0000) >> 8) | \ + ((((uint32_t) (val)) & 0xff000000) >> 24) \ + ) +#else +#error "Endianess undefined! Please define LITTLE_ENDIAN=1 or BIG_ENDIAN=1." +#endif + +uint16_t htol16(uint16_t h); +uint32_t htol32(uint32_t h); + +/** + * Converts a 16-bit integer 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. + */ +#define LTOH16(val) HTOL16(val) + +/** + * Converts a 32-bit integer 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. + */ +#define LTOH32(val) HTOL32(val) + +/** + * Converts a 16-bit integer 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. + */ +#define ltoh16(l) htol16(l) + +/** + * Converts a 32-bit integer 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. + */ +#define ltoh32(l) htol32(l) + + +/** + * @} + */ + +#if __AVR__ +#define htol16(h) (h) +#define htol32(h) (h) +#else +uint16_t htol16(uint16_t h); +uint32_t htol32(uint32_t h); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/FAT16/examples/FAT16_ReadExample/FAT16_ReadExample.pde b/FAT16/examples/FAT16_ReadExample/FAT16_ReadExample.pde new file mode 100644 index 0000000..5bae1dc --- /dev/null +++ b/FAT16/examples/FAT16_ReadExample/FAT16_ReadExample.pde @@ -0,0 +1,195 @@ +/* +FAT16 ReadFile Example +SparkFun Electronics +Written by Ryan Owens +3/16/2010 + +Code Description: Uses an Arduino Duemillanove or Arduino Pro to read the file contents of each file on an SD card. + +Circuit Description: Uses the SparkFun microSD shield. (http://www.sparkfun.com/commerce/product_info.php?products_id=9520) + +Attributions: Special thanks to Roland Riegel for providing an open source FAT library +for AVR microcontrollers. See more of his projects here: +http://www.roland-riegel.de/ + +This code is provided under the Creative Commons Attribution License. More information can be found here: +http://creativecommons.org/licenses/by/3.0/ + +(Use our code freely! Please just remember to give us credit where it's due. Thanks!) +*/ + +//Add libraries to support FAT16 on the SD Card. +//(Note: If you already have these libraries installed in the directory, they'll have to remove in order to compile this.) +#include +#include +#include +#include +#include +#include +#include +#include + +//Define the pin numbers +#define CS 8 +#define MOSI 11 +#define MISO 12 +#define SCK 13 + +//This is the amount of data to be fetched from the SD card for each read. +#define BUFFERSIZE 256 + +unsigned char buffer[BUFFERSIZE]; +char file_name[30]; + +struct fat_dir_struct* dd; //FAT16 directory +struct fat_dir_entry_struct dir_entry; //FAT16 directory entry (A.K.A. a file) + +struct fat_fs_struct* fs; //FAT16 File System +struct partition_struct* partition; //FAT16 Partition + +struct fat_file_struct * file_handle; //FAT16 File Handle + +void setup() +{ + //Set up the pins for the Serial communication + pinMode(0, INPUT); + pinMode(1, OUTPUT); + Serial.begin(9600); + + //Set up the pins for the microSD shield + pinMode(CS, OUTPUT); + pinMode(MOSI, OUTPUT); + pinMode(MISO, INPUT); + pinMode(SCK, OUTPUT); + pinMode(10, OUTPUT); +} + +void loop() +{ + int bytes_read=0; //Keeps track of how many bytes are read when accessing a file on the SD card. + + init_filesystem(); //Initialize the FAT16 file system on the SD card. + + //Get the next file in the directory + while(get_next_filename(dd, file_name)){ + //Open the file + file_handle=open_file_in_dir(fs, dd, file_name); + //Read up to 512 bytes from the file + bytes_read = fat_read_file(file_handle, buffer, BUFFERSIZE); + //Print whatever we just got from the file + Serial.println((const char *)buffer); + //Keep reading from the file until we reach the end (nothing more is read from the file) + while(bytes_read > 0){ + //If there's more to be read from the file, go get it. + bytes_read = fat_read_file(file_handle, buffer, BUFFERSIZE); + //Print the contents that have been read + Serial.println((const char *)buffer); + } + //Close the file before moving on to the next one. + fat_close_file(file_handle); + delay(1000); + Serial.println("Next File..."); + } + while(1); +} + +uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry) +{ + fat_reset_dir(dd); //Make sure to start from the beginning of the directory! + while(fat_read_dir(dd, dir_entry)) + { + if(strcmp(dir_entry->long_name, name) == 0) + { + //fat_reset_dir(dd); + return 1; + } + } + + return 0; +} + +struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name) +{ + struct fat_dir_entry_struct file_entry; + if(!find_file_in_dir(fs, dd, name, &file_entry)) + return 0; + + return fat_open_file(fs, &file_entry); +} + +char init_filesystem(void) +{ + //setup sd card slot + if(!sd_raw_init()) + { + return 0; + } + + //open first partition + partition = partition_open(sd_raw_read, + sd_raw_read_interval, +#if SD_RAW_WRITE_SUPPORT + sd_raw_write, + sd_raw_write_interval, +#else + 0, + 0, +#endif + 0 + ); + + if(!partition) + { + //If the partition did not open, assume the storage device + //is a "superfloppy", i.e. has no MBR. + partition = partition_open(sd_raw_read, + sd_raw_read_interval, +#if SD_RAW_WRITE_SUPPORT + sd_raw_write, + sd_raw_write_interval, +#else + 0, + 0, +#endif + -1 + ); + if(!partition) + { + return 0; + } + } + + //Open file system + fs = fat_open(partition); + if(!fs) + { + return 0; + } + + //Open root directory + fat_get_dir_entry_of_path(fs, "/", &dir_entry); + dd=fat_open_dir(fs, &dir_entry); + + if(!dd) + { + return 0; + } + return 1; +} + +char get_next_filename(struct fat_dir_struct* cur_dir, char * new_file) +{ + //'dir_entry' is a global variable of type directory_entry_struct + + //Get the next file from the root directory + if(fat_read_dir(cur_dir, &dir_entry)) + { + sprintf(new_file, "%s", dir_entry.long_name); + Serial.println((const char *)new_file); + return 1; + } + //If another file isn't found, return 0 + return 0; +} + + diff --git a/FAT16/examples/FAT16_ReadExample/applet/FAT16/byteordering.c.o b/FAT16/examples/FAT16_ReadExample/applet/FAT16/byteordering.c.o new file mode 100644 index 0000000..07d283e Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/FAT16/byteordering.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/FAT16/fat.c.o b/FAT16/examples/FAT16_ReadExample/applet/FAT16/fat.c.o new file mode 100644 index 0000000..1125cad Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/FAT16/fat.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/FAT16/partition.c.o b/FAT16/examples/FAT16_ReadExample/applet/FAT16/partition.c.o new file mode 100644 index 0000000..f5741e1 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/FAT16/partition.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/FAT16/sd_raw.c.o b/FAT16/examples/FAT16_ReadExample/applet/FAT16/sd_raw.c.o new file mode 100644 index 0000000..b020ac4 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/FAT16/sd_raw.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp new file mode 100644 index 0000000..e20ecc6 --- /dev/null +++ b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp @@ -0,0 +1,215 @@ +/* +FAT16 ReadFile Example +SparkFun Electronics +Written by Ryan Owens +3/16/2010 + +Code Description: Uses an Arduino Duemillanove or Arduino Pro to read the file contents of each file on an SD card. + +Circuit Description: Uses the SparkFun microSD shield. (http://www.sparkfun.com/commerce/product_info.php?products_id=9520) + +Attributions: Special thanks to Roland Riegel for providing an open source FAT library +for AVR microcontrollers. See more of his projects here: +http://www.roland-riegel.de/ + +This code is provided under the Creative Commons Attribution License. More information can be found here: +http://creativecommons.org/licenses/by/3.0/ + +(Use our code freely! Please just remember to give us credit where it's due. Thanks!) +*/ + +//Add libraries to support FAT16 on the SD Card. +//(Note: If you already have these libraries installed in the directory, they'll have to remove in order to compile this.) +#include +#include +#include +#include +#include +#include +#include +#include + +//Define the pin numbers +#define CS 8 +#define MOSI 11 +#define MISO 12 +#define SCK 13 + +//This is the amount of data to be fetched from the SD card for each read. +#define BUFFERSIZE 256 + +#include "WProgram.h" +void setup(); +void loop(); +uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry); +struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name); +char init_filesystem(void); +char get_next_filename(struct fat_dir_struct* cur_dir, char * new_file); +unsigned char buffer[BUFFERSIZE]; +char file_name[30]; + +struct fat_dir_struct* dd; //FAT16 directory +struct fat_dir_entry_struct dir_entry; //FAT16 directory entry (A.K.A. a file) + +struct fat_fs_struct* fs; //FAT16 File System +struct partition_struct* partition; //FAT16 Partition + +struct fat_file_struct * file_handle; //FAT16 File Handle + +void setup() +{ + //Set up the pins for the Serial communication + pinMode(0, INPUT); + pinMode(1, OUTPUT); + Serial.begin(9600); + + //Set up the pins for the microSD shield + pinMode(CS, OUTPUT); + pinMode(MOSI, OUTPUT); + pinMode(MISO, INPUT); + pinMode(SCK, OUTPUT); + pinMode(10, OUTPUT); +} + +void loop() +{ + int bytes_read=0; //Keeps track of how many bytes are read when accessing a file on the SD card. + + init_filesystem(); //Initialize the FAT16 file system on the SD card. + + //Get the next file in the directory + while(get_next_filename(dd, file_name)){ + //Open the file + file_handle=open_file_in_dir(fs, dd, file_name); + //Read up to 512 bytes from the file + bytes_read = fat_read_file(file_handle, buffer, BUFFERSIZE); + //Print whatever we just got from the file + Serial.println((const char *)buffer); + //Keep reading from the file until we reach the end (nothing more is read from the file) + while(bytes_read > 0){ + //If there's more to be read from the file, go get it. + bytes_read = fat_read_file(file_handle, buffer, BUFFERSIZE); + //Print the contents that have been read + Serial.println((const char *)buffer); + } + //Close the file before moving on to the next one. + fat_close_file(file_handle); + delay(1000); + Serial.println("Next File..."); + } + while(1); +} + +uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry) +{ + fat_reset_dir(dd); //Make sure to start from the beginning of the directory! + while(fat_read_dir(dd, dir_entry)) + { + if(strcmp(dir_entry->long_name, name) == 0) + { + //fat_reset_dir(dd); + return 1; + } + } + + return 0; +} + +struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name) +{ + struct fat_dir_entry_struct file_entry; + if(!find_file_in_dir(fs, dd, name, &file_entry)) + return 0; + + return fat_open_file(fs, &file_entry); +} + +char init_filesystem(void) +{ + //setup sd card slot + if(!sd_raw_init()) + { + return 0; + } + + //open first partition + partition = partition_open(sd_raw_read, + sd_raw_read_interval, +#if SD_RAW_WRITE_SUPPORT + sd_raw_write, + sd_raw_write_interval, +#else + 0, + 0, +#endif + 0 + ); + + if(!partition) + { + //If the partition did not open, assume the storage device + //is a "superfloppy", i.e. has no MBR. + partition = partition_open(sd_raw_read, + sd_raw_read_interval, +#if SD_RAW_WRITE_SUPPORT + sd_raw_write, + sd_raw_write_interval, +#else + 0, + 0, +#endif + -1 + ); + if(!partition) + { + return 0; + } + } + + //Open file system + fs = fat_open(partition); + if(!fs) + { + return 0; + } + + //Open root directory + fat_get_dir_entry_of_path(fs, "/", &dir_entry); + dd=fat_open_dir(fs, &dir_entry); + + if(!dd) + { + return 0; + } + return 1; +} + +char get_next_filename(struct fat_dir_struct* cur_dir, char * new_file) +{ + //'dir_entry' is a global variable of type directory_entry_struct + + //Get the next file from the root directory + if(fat_read_dir(cur_dir, &dir_entry)) + { + sprintf(new_file, "%s", dir_entry.long_name); + Serial.println((const char *)new_file); + return 1; + } + //If another file isn't found, return 0 + return 0; +} + + + +int main(void) +{ + init(); + + setup(); + + for (;;) + loop(); + + return 0; +} + diff --git a/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.eep b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.eep new file mode 100644 index 0000000..1996e8f --- /dev/null +++ b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.eep @@ -0,0 +1 @@ +:00000001FF diff --git a/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.elf b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.elf new file mode 100644 index 0000000..3a694d3 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.elf differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.hex b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.hex new file mode 100644 index 0000000..6d9a1c7 --- /dev/null +++ b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.hex @@ -0,0 +1,509 @@ +:100000000C9462000C948A000C948A000C948A0070 +:100010000C948A000C948A000C948A000C948A0038 +:100020000C948A000C948A000C948A000C948A0028 +:100030000C948A000C948A000C948A000C948A0018 +:100040000C94890C0C948A000C94550D0C948A0025 +:100050000C948A000C948A000C948A000C948A00F8 +:100060000C948A000C948A000000000024002700F1 +:100070002A0000000000250028002B0000000000DE +:1000800023002600290004040404040404040202DA +:100090000202020203030303030301020408102007 +:1000A0004080010204081020010204081020000012 +:1000B0000007000201000003040600000000000029 +:1000C00000009E0E11241FBECFEFD8E0DEBFCDBFD3 +:1000D00011E0A0E0B1E0E8E8FFE102C005900D9278 +:1000E000A632B107D9F715E0A6E2B1E001C01D9232 +:1000F000A13EB107E1F710E0C4ECD0E004C02297C4 +:10010000FE010E94BE0FC23CD107C9F70E94820CBB +:100110000C94C20F0C940000FB01DC0102C00190A2 +:100120000D9241505040D8F70895FC0181918617F7 +:1001300021F08823D9F7992708953197CF010895A1 +:10014000FB01DC018D91019080190110D9F3990B0D +:100150000895FB01DC0101900D920020E1F7089564 +:10016000FB01DC014150504030F08D91019080192D +:1001700019F40020B9F7881B990B0895DF93CF93EA +:1001800000D0CDB7DEB7DC01009781F16230710598 +:1001900068F1ED91FC911197660F771F80E090E078 +:1001A00016962D913D914D915C911997620F731F99 +:1001B000841F951F0190F081E02DAE014F5F5F4FCE +:1001C00022E030E00995882389F029813A812115C0 +:1001D000310571F08FEF273F380741F0C901409694 +:1001E000079720F08FEF283F380710F020E030E02D +:1001F000C9010F900F90CF91DF910895FC010097F6 +:1002000011F01182108208952F923F924F925F92C7 +:100210006F927F928F929F92AF92BF92CF92DF9216 +:10022000EF92FF920F931F93DF93CF93CDB7DEB77B +:100230002A970FB6F894DEBF0FBECDBF2C017A838C +:1002400069835C834B83009709F409C1672B09F428 +:1002500006C1452B09F403C1DC019D96AD90BD900C +:10026000CD90DC90D09795962D913D914D915C91DC +:100270009897EB81FC81CF01A0E0B0E08A0D9B1D37 +:10028000AC1DBD1D281739074A075B0740F4B901AB +:100290006A197B097C836B83672B09F4E3C0D20165 +:1002A000ED91FC91119720883188D196ED91FC91C8 +:1002B000D297FA87E987EF2BF1F593962D913C9130 +:1002C00094973A872987232B39F4A114B104C104E8 +:1002D000D10409F4C7C0C3C0A114B104C104D1043E +:1002E00051F1860175013101882499246627772709 +:1002F000CB0166197709880999096D837E838F83FD +:10030000988714C0D2018D919C9169857A850E944D +:10031000BE009A878987892B09F4A1C06D817E81EF +:100320008F819885E60EF71E081F191FE614F70443 +:100330000805190538F7089421083108A220B320D0 +:100340000894211C311CCB80DC80D201ED90FC9004 +:10035000E114F10409F1E985FA85E230F105E0F0F4 +:100360003297BF0180E090E03296FA87E987D701A3 +:1003700050962D913C91519740E050E00E94390FEA +:100380009B01AC01F70182899389A489B589280F63 +:10039000391F4A1F5B1F04C020E030E040E050E0FE +:1003A00081010A191B09C016D10608F48601D7017C +:1003B000ED91FC91C501A0E0B0E0280F391F4A1F64 +:1003C0005B1F0190F081E02DCA01B90149815A817A +:1003D00098010995882341F1C01AD10AC801A0E00B +:1003E000B0E0F20125A536A547A550A9280F391F71 +:1003F0004A1F5B1FD2019D962D933D934D935C93B5 +:10040000D097C8018A0D9B1D82159305A0F08D9190 +:100410009C9169857A850E94BE009A878987892B7D +:1004200041F4F20112AA11AA4B815C814C195D09B9 +:100430001BC0AA24BB24E985FA85D201D296FC937D +:10044000EE93D197C114D10439F029813A81200F5C +:10045000311F3A83298379CF4B815C8105C04FEFEF +:100460005FEF02C040E050E0CA012A960FB6F89450 +:10047000DEBF0FBECDBFCF91DF911F910F91FF90D7 +:10048000EF90DF90CF90BF90AF909F908F907F9034 +:100490006F905F904F903F902F900895FC010097D0 +:1004A00011F480E0089583A194A196A785A710AACE +:1004B00017A681E00895CF92DF92EF92FF920F93FB +:1004C0001F93CF93DF938C016A017B01A901D901AE +:1004D000CD91DC91119712968D919C9113978096F6 +:1004E00013969C938E931297F8018081853E09F4B0 +:1004F000BAC0882309F4B7C08FA198A5A9A5BAA549 +:100500000097A105B10521F4CFA2D8A6E9A6FAA6C5 +:10051000209709F4AAC00115110509F4A6C0D80155 +:100520009C91992309F4A1C01B968C918F30A1F561 +:10053000892F90E08F7390702DE030E0829FA001B2 +:10054000839F500D929F500D11244D50504010929A +:1005500027011092260111C0AC0FBD1FF901E15F08 +:10056000FE4F8081F801E80FF11D80818C932F5F91 +:100570003F4F30932701209326012091260130918F +:1005800027012D30310508F06EC0DA01A20FB31F2C +:10059000AF31B10508F367C08881882309F04FC0E7 +:1005A000B801DE01780120E0F70190819032A1F0DE +:1005B0009C93F801848583FF08C0F7018081813412 +:1005C00020F08B3510F4905E9C932F5F0894E11C13 +:1005D000F11C1196283041F78881853011F485EEA1 +:1005E0008883D80118968C91803211F4322F23C061 +:1005F000FE01E20FF11D8EE28083322F3F5F2C5F00 +:10060000FB0190859032B9F0FE01E30FF11D90835C +:10061000D8011C968C9184FF09C0DB0118968C913F +:10062000813420F08B3510F4905E90833F5F6F5FD4 +:100630007F4F321729F7FE01E30FF11D1082F801F9 +:10064000838588A3828D938D9AA389A3848D958DAC +:10065000A68DB78D8BA39CA3ADA3BEA381E0DA01C9 +:1006600014968C9302C081E001C080E0DF91CF91AD +:100670001F910F91FF90EF90DF90CF9008952F92F0 +:100680003F924F925F926F927F928F929F92AF9222 +:10069000BF92CF92DF92EF92FF920F931F93DF935F +:1006A000CF93CDB7DEB7A7970FB6F894DEBF0FBED6 +:1006B000CDBF2C017FA36EA3009709F4B1C06115D3 +:1006C000710509F4ADC0DC018D909C901197F40187 +:1006D000208831889D96AD90BC909E979F966D9096 +:1006E0007C90D0978BE2DB011D928A95E9F7FE01A1 +:1006F000319685E0DF011D928A95E9F7A114B104D6 +:1007000009F076C0F4012288338886899789281AEF +:10071000390A6EC0730100E010E0A114B10441F485 +:10072000D40156966D917D918D919C9159972DC0D4 +:100730008114910421F1B2E0AB16B10400F1EEEFA7 +:10074000FFEFAE0EBF1EB50180E090E022E030E08A +:10075000A20EB31ED40150962D913C91519740E0CA +:1007600050E00E94390F9B01AC01F401828993890A +:10077000A489B589280F391F4A1F5B1F04C020E0D8 +:1007800030E040E050E0CA01B9016E0D7F1D801FCE +:10079000911F1C821B82D401ED91FC9181010619ED +:1007A00017090280F381E02DAE014A5F5F4F20E21E +:1007B00030E0ABE5EA2EA2E0FA2E6E010894C11CEF +:1007C000D11C0995882361F18B819C81680E791E6B +:1007D0006214730488F0C401B5010E94BE005C017C +:1007E00066247724009741F4F201A3A0B4A008C0C6 +:1007F0002EA13FA13A8329838D81882309F48ACFD2 +:10080000D2019E96BC92AE929D97D0967C926E92AB +:100810009F97EEA1FFA18081882319F081E001C09C +:1008200080E0A7960FB6F894DEBF0FBECDBFCF9184 +:10083000DF911F910F91FF90EF90DF90CF90BF90CD +:10084000AF909F908F907F906F905F904F903F9070 +:100850002F9008952F923F924F925F926F927F92C6 +:100860008F929F92AF92BF92CF92DF92EF92FF92C0 +:100870000F931F93DF93CF93CDB7DEB7A8970FB633 +:10088000F894DEBF0FBECDBF98A78FA3009709F4E1 +:100890005CC1DC0114968D919C911597892B09F40C +:1008A00054C116968D919C911797892B09F44DC1CF +:1008B00068C1E8E2F1E08AE1DF011D928A95E9F77B +:1008C000EFA1F8A5F0932901E09328012184328457 +:1008D0004384548459E0220C331C441C551C5A9503 +:1008E000D1F7C201B101655F7F4F8F4F9F4F0190DC +:1008F000F081E02DAE014F5F5F4F29E130E00995B7 +:10090000882309F445C1E981FA81FBA3EAA32C817C +:100910003D81FB81FCA34E818F8198859EA38DA391 +:10092000E985FA856C857D856E887F88888C998CB1 +:10093000611471048104910431F4309709F428C1E1 +:100940003F01882499246115710509F421C1C90169 +:10095000A0E0B0E08E8F9F8FA8A3B9A380E090E0C5 +:10096000242F30E040E050E00E94390F6A8F7B8FE7 +:100970008C8F9D8F840173012E8D3F8D48A159A1CD +:10098000E21AF30A040B150BE61AF70A080B190B07 +:100990008AA19BA101972DA13EA145E0220F331F03 +:1009A0004A95E1F7820F931F6AA17BA10E94580F1D +:1009B00080E090E0E61AF70A080B190B8CA1282FAB +:1009C00030E040E050E0C801B7010E947F0F79019C +:1009D0008A0195EFE9169FE0F90690E0090790E09B +:1009E000190708F4D5C0A5EFEA16AFEFFA06A0E0A4 +:1009F0000A07A0E01A0728F486E0EFA1F8A580878F +:100A000005C08BE0AFA1B8A518968C9388E1EAE207 +:100A1000F1E0DF011D928A95E9F7EAA1FBA15F01F0 +:100A2000CC24DD24C401B301A60195010E94390F35 +:100A300060932A0170932B0180932C0190932D01D8 +:100A4000C601B5012E8D3F8D48A159A10E94390FD5 +:100A5000260E371E481E591E20922E0130922F015D +:100A60004092300150923101AFA1B8A518968C91F7 +:100A7000863029F064E070E080E090E004C062E03D +:100A800070E080E090E022E030E040E050E0E20EF4 +:100A9000F31E041F151FA80197010E94390F6093D0 +:100AA00032017093330180933401909335014AA150 +:100AB0005BA150933701409336015CA1852F90E0F4 +:100AC000DC01EAA1FBA1AE9FC001AF9F900DBE9FCC +:100AD000900D112490933901809338016A8D7B8D9C +:100AE0008C8D9D8DA60195010E94390F20912E01BC +:100AF00030912F014091300150913101620F731FED +:100B0000841F951F60933E0170933F0180934001C5 +:100B100090934101ADA1BEA19D0140E050E0B5E040 +:100B2000220F331F441F551FBA95D1F7620F731F51 +:100B3000841F951F60933A0170933B0180933C01A1 +:100B400090933D0128E231E002C020E030E0C9018D +:100B5000A8960FB6F894DEBF0FBECDBFCF91DF9140 +:100B60001F910F91FF90EF90DF90CF90BF90AF90CB +:100B70009F908F907F906F905F904F903F902F90BD +:100B800008958091280190912901892BF1F691CE49 +:100B90001092290110922801D8CFCF93DF93AC0196 +:100BA000DB01009789F16115710571F190968C91C7 +:100BB000909784FF29C08091750190917601892BCF +:100BC00019F4E5E7F1E008C08091A6019091A70132 +:100BD000892BD1F4E6EAF1E09F012E5F3F4FBD0182 +:100BE0008BE2EB010990BE01E90109929E0181505F +:100BF000C1F75183408391968D919C91929796A7CE +:100C000085A710AA17A602C0E0E0F0E0CF01DF91AF +:100C1000CF910895CF93DF939C01FB01009731F0B2 +:100C20006115710519F080A184FF23C020E030E038 +:100C3000C901DF91CF910895C4E4D1E0DF018BE2D7 +:100C40000D9009928150E1F73093430120934201C6 +:100C500010926F0110927001109271011092720146 +:100C600081A192A1909374018093730122E431E0F9 +:100C7000DFCF8091420190914301892BB9F6DCCFFF +:100C80006F927F928F929F92AF92BF92CF92DF929C +:100C9000EF92FF920F931F93CF93DF934C01EB01E1 +:100CA0007A01009709F467C06115710509F463C002 +:100CB0008881882309F45FC04115510509F45BC0A0 +:100CC0008F3209F421968BE2F70111928A95E9F7A8 +:100CD00080E1F70180A3370101C0E5018881882305 +:100CE00009F44BC0C401B7010E94CD056C01009707 +:100CF00009F441C0CE016FE270E00E9495000097B8 +:100D000039F0482F4C1B5C010894A11CB11C2AC06F +:100D1000FE0101900020E9F731974E2F4C1B5E0138 +:100D2000A40EB11C1FC0F30101900020E9F7319718 +:100D3000E619F709E017F107B9F4CE01B301A801EC +:100D40000E94B000009781F4F601118210820C0F0E +:100D50001D1FF8018081882381F0F70180A184FDA7 +:100D6000BCCF09C0042F10E0C601B7010E943F03A9 +:100D70008823C9F612C080E001C081E0DF91CF91E5 +:100D80001F910F91FF90EF90DF90CF90BF90AF90A9 +:100D90009F908F907F906F900895F601118210823E +:100DA0009DCF8F929F92AF92BF92CF92DF92EF92A0 +:100DB000FF920F93DF93CF93CDB7DEB760970FB657 +:100DC000F894DEBF0FBECDBF7C016B015A01490113 +:100DD000009709F493C06115710509F48FC00430C0 +:100DE0000CF08CC007FD1DC0602F772767FD709544 +:100DF000E4E0660F771FEA95E1F762547E4F88279B +:100E000077FD8095982FAE014F5F5F4F20E130E076 +:100E1000F7010995882309F471C08D81882309F4AD +:100E20006DC08091DF01882309F068C07CC08D818E +:100E30008093DF012A8530E040E050E0542F432FBB +:100E4000322F22278B8590E0A0E0B0E0DC019927CB +:100E50008827282B392B4A2B5B2B898590E0A0E033 +:100E6000B0E0282B392B4A2B5B2B8C8590E0A0E03F +:100E7000B0E0B82FAA2799278827282B392B4A2B8F +:100E80005B2B2093E0013093E1014093E20150930A +:100E9000E3012E8530E040E050E0542F432F322F05 +:100EA00022278F8590E0A0E0B0E0DC019927882719 +:100EB000282B392B4A2B5B2B8D8590E0A0E0B0E0EE +:100EC000282B392B4A2B5B2B888990E0A0E0B0E0DF +:100ED000B82FAA2799278827282B392B4A2B5B2B39 +:100EE0002093E4013093E5014093E6015093E7013C +:100EF00003C08FEF8093DF019F0102C020E030E04C +:100F0000C90160960FB6F894DEBF0FBECDBFCF917A +:100F1000DF910F91FF90EF90DF90CF90BF90AF9057 +:100F20009F908F900895E7EDF1E081E1DF011D9240 +:100F30008A95E9F7F092D801E092D701D092DA01D0 +:100F4000C092D901B092DC01A092DB019092DE0147 +:100F50008092DD0107FF6BCFCCCF282F8FEF8EBDA6 +:100F60000DB407FEFDCF8DB58F778DBD8EB5822F69 +:100F700080648EBD0DB407FEFDCF8DB58F778DBD1E +:100F8000872F9927AA27BB278EBD0DB407FEFDCF5B +:100F90008DB58F778DBDCB01AA27BB278EBD0DB434 +:100FA00007FEFDCF8DB58F778DBDBB27A72F962F61 +:100FB000852F8EBD0DB407FEFDCF8DB58F778DBD0E +:100FC0004EBD0DB407FEFDCF8DB58F778DBD2223AD +:100FD00019F0283069F406C085E98EBD0DB407FE0E +:100FE000FDCF0BC087E88EBD0DB407FEFDCF05C059 +:100FF0008FEF8EBD0DB407FEFDCF8DB58F778DBD04 +:101000001092E9011092E80120E030E09FEF13C058 +:101010009EBD0DB407FEFDCF8DB58F778DBD8EB50E +:10102000A9014F5F5F4F8F3F29F03093E901209373 +:10103000E80108959A012A30310550F33093E9010F +:101040002093E8018FEF08952F923F924F925F9285 +:101050006F927F928F929F92AF92BF92CF92DF92C8 +:10106000EF92FF920F931F93CF93DF934B015C019D +:101070002A01390133243A9422242394AFC0E40195 +:10108000D170CE01A0E0B0E085017401E81AF90A40 +:101090000A0B1B0BC12CB2E0DB2ECC1ADD0A6C1440 +:1010A0007D0408F463018091EA039091EB03A09121 +:1010B000EC03B091ED03E816F9060A071B07F9F0F7 +:1010C0000E940809882309F48FC0209729F480E042 +:1010D000C81682E0D80658F4C801B7014AEE51E0BC +:1010E00020E032E00E942209882309F47DC0E092CA +:1010F000EA03F092EB030093EC031093ED038AEE06 +:10110000481681E0580671F09E0126513E4FC901F4 +:10111000B201A6010E948C001092EE036C147D04B3 +:1011200009F460C0289888E1B801A7010E94AD07C2 +:10113000882311F0289A58C08EEF8EBD0DB407FE9B +:10114000FDCF8DB58F778DBD1092E9011092E8012A +:10115000EAEEF1E020E030E00BC080818EBD0DB4FE +:1011600007FEFDCF31968DB58F778DBD2F5F3F4F39 +:1011700082E02030380788F33093E9012093E801BA +:101180003EBC0DB407FEFDCF8DB58F778DBD3EBC47 +:101190000DB407FEFDCF8DB58F778DBD3EBC0DB470 +:1011A00007FEFDCF8DB58F778DBD8EB58F3FB1F723 +:1011B0008EBD0DB407FEFDCF8DB58F778DBD8EB57D +:1011C000289A4C0C5D1CC601A0E0B0E0880E991E68 +:1011D000AA1EBB1E6C187D082092EE0361147104D8 +:1011E00009F04DCF81E001C080E0DF91CF911F91E8 +:1011F0000F91FF90EF90DF90CF90BF90AF909F90B6 +:101200008F907F906F905F904F903F902F900895B8 +:101210008091EE03882311F081E008956091EA0344 +:101220007091EB038091EC039091ED034AEE51E055 +:1012300020E032E00E942408882319F081E08093A6 +:10124000EE0308953F924F925F926F927F928F923A +:101250009F92AF92BF92CF92DF92EF92FF920F9345 +:101260001F93CF93DF933B014C016A012901332483 +:101270003A9496C01FEFA12E11E0B12EA620B72000 +:10128000C501A0E0B0E084017301E81AF90A0A0B75 +:101290001B0BC0E0D2E0CA19DB094C165D0608F44E +:1012A000E2018091EA039091EB03A091EC03B091ED +:1012B000ED03E816F9060A071B0709F45EC00E9451 +:1012C0000809882309F471C0289881E1B801A701B1 +:1012D0000E94AD07882319F0289A80E066C03EBCC2 +:1012E0000DB407FEFDCF8DB58F778DBD8EB58E3FCA +:1012F000B1F7EAEEF1E020E030E00BC03EBC0DB407 +:1013000007FEFDCF8DB58F778DBD8EB581932F5F95 +:101310003F4F82E02030380788F33093E901209373 +:10132000E801E092EA03F092EB030093EC031093E0 +:10133000ED03950126513E4FC601B901AE010E9451 +:101340008C003EBC0DB407FEFDCF8DB58F778DBDF3 +:101350008EB53EBC0DB407FEFDCF8DB58F778DBD2C +:101360008EB5289A3EBC0DB407FEFDCFCC0EDD1E17 +:101370008DB58F778DBD8EB50AC0950126513E4F34 +:10138000C601B901AE010E948C00CC0EDD1E4C1AC4 +:101390005D0ACE01A0E0B0E0680E791E8A1E9B1E99 +:1013A0004114510409F066CF81E0DF91CF911F9184 +:1013B0000F91FF90EF90DF90CF90BF90AF909F90F4 +:1013C0008F907F906F905F904F903F9008952F92F5 +:1013D0003F924F925F926F927F928F929F92AF92C5 +:1013E000BF92CF92DF92EF92FF920F931F93DF9302 +:1013F000CF9300D000D0CDB7DEB74B015C013A01EE +:101400003C832B832701DA82C98241155105A9F15A +:101410002115310591F10217130778F1E114F10458 +:1014200061F119013094219431083394021B130B9C +:101430006901EE24FF24C501B401A3012B813C8185 +:101440000E9422098823C9F0C301B501A4012981A2 +:101450003A81F2010995882391F0020D131D8B81C9 +:101460009C81800F911F2B813C818217930738F05C +:101470008C0C9D1CAE1CBF1CDECF80E001C081E047 +:101480000F900F900F900F90CF91DF911F910F91C0 +:10149000FF90EF90DF90CF90BF90AF909F908F9094 +:1014A0007F906F905F904F903F902F9008951F9383 +:1014B000CF93DF93239A259A209A24982C9A289ADE +:1014C00083E58CBD8DB58E7F8DBD1092EF0320E03E +:1014D00030E09FEF0AC09EBD0DB407FEFDCF8DB575 +:1014E0008F778DBD8EB52F5F3F4F2A30310598F332 +:1014F0003093E9012093E80128981092E9011092B5 +:10150000E80180E040E050E060E070E00E94AD075C +:10151000182F813079F08091E8019091E90121E064 +:101520008F3F920709F44DC001969093E901809393 +:10153000E801E7CF87E340E050E060E070E00E9420 +:10154000AD0789E240E050E060E070E00E94AD0746 +:1015500082FD02C01093EF031092E9011092E8019E +:101560008091EF03C82FD0E0C370D070209749F06E +:1015700087E340E050E060E070E00E94AD0789E260 +:1015800001C081E040E050E060E070E00E94AD0703 +:1015900080FF0EC08091E8019091E9012FE78F3F15 +:1015A000920779F001969093E9018093E801DECFEC +:1015B00080E140E052E060E070E00E94AD078823E7 +:1015C00019F0289A80E022C0289A8CB58C7F8CBDB7 +:1015D0008DB581608DBD8FEF9FEFAFEFBFEF809333 +:1015E000EA039093EB03A093EC03B093ED0381E047 +:1015F0008093EE0360E070E080E090E04AEE51E01E +:1016000020E032E00E942209811181E0DF91CF9138 +:101610001F9108954F925F926F927F928F929F9247 +:10162000AF92BF92CF92DF92EF92FF920F931F93F0 +:10163000CF93DF935B016C014A01E9013801270177 +:101640004115510571F10115110559F1FF242115BD +:10165000310511F5FF24F3941FC0C401B601A501A3 +:101660009201F30109958C010097E9F0FF2019F42C +:10167000C817D907B0F0C601B501A40198010E94AE +:101680002408882371F0C801A0E0B0E0A80EB91EBC +:10169000CA1EDB1EC01BD10BFF20F9F62097E9F60E +:1016A00002C080E001C081E0DF91CF911F910F91D6 +:1016B000FF90EF90DF90CF90BF90AF909F908F9072 +:1016C0007F906F905F904F900895CF93DF93EB01E1 +:1016D00060E175E00E943F03882359F0CE0160E18C +:1016E00075E00E94A9008EEC95E0BE010E942E0FCD +:1016F00081E0DF91CF9108950F930E94570A8823CC +:1017000009F445C082E299E067EE79E044E258E0EE +:101710002AE03BE000E00E94D10690933E058093D2 +:101720003D05892B89F482E299E067EE79E044E295 +:1017300058E02AE03BE00FEF0E94D10690933E056F +:1017400080933D05892B19F180913D0590913E05CF +:101750000E942A0490933C0580933B050097B9F0C2 +:1017600060E071E040E155E00E94400680913B0559 +:1017700090913C0560E175E00E94CD0590930F05C6 +:1017800080930E0520E0892B19F021E001C020E0B4 +:10179000822F0F910895EF92FF920F931F93CF9393 +:1017A000DF93EB018A017901CB010E944E0208C050 +:1017B000C701B8010E94A000009711F481E006C0A3 +:1017C000CE01B7010E943F03882391F7DF91CF91AB +:1017D0001F910F91FF90EF900895EF92FF920F935A +:1017E0001F93DF93CF93CDB7DEB7AB970FB6F894C7 +:1017F000DEBF0FBECDBF8C017E010894E11CF11C41 +:1018000097010E94CB0B882319F420E030E005C03B +:10181000C801B7010E940A069C01C901AB960FB628 +:10182000F894DEBF0FBECDBFCF91DF911F910F9116 +:10183000FF90EF900895CF93DF930E947C0B32C00E +:1018400060910E0570910F0580913B0590913C05CC +:1018500040EF54E00E94ED0B9093400580933F05CC +:1018600060EF73E040E051E00E940401EC018EEC77 +:1018700095E060EF73E00E942E0F80913F059091FC +:1018800040051C161D0664F30E94FE0068EE73E01E +:1018900080E090E00E94D10C8EEC95E062E071E077 +:1018A0000E942E0F80910E0590910F0560EF74E05D +:1018B0000E94650B882321F6FFCF80E060E00E9444 +:1018C000350D81E061E00E94350D8EEC95E040E839 +:1018D00055E260E070E00E94920D88E061E00E94B5 +:1018E000350D8BE061E00E94350D8CE060E00E94D8 +:1018F000350D8DE061E00E94350D8AE061E00E94C7 +:10190000350D08950E94FB0C0E945D0C0E941B0C7B +:10191000FDCF1F920F920FB60F9211242F933F937A +:101920008F939F93AF93BF93809145059091460508 +:10193000A0914705B0914805309149050196A11D38 +:10194000B11D232F2D5F2D3720F02D570196A11D9E +:10195000B11D209349058093450590934605A093BA +:101960004705B09348058091410590914205A091AB +:101970004305B09144050196A11DB11D8093410519 +:1019800090934205A0934305B0934405BF91AF9156 +:101990009F918F913F912F910F900FBE0F901F90AD +:1019A0001895EF92FF920F931F937B018C018FB7D5 +:1019B000F894409145055091460560914705709116 +:1019C00048058FBF2FB7F894809145059091460543 +:1019D000A0914705B09148052FBF841B950BA60B1E +:1019E000B70BE816F9060A071B0760F71F910F915E +:1019F000FF90EF900895789484B5826084BD84B59B +:101A0000816084BD85B5826085BD85B5816085BDF9 +:101A1000EEE6F0E0808181608083E1E8F0E08081A3 +:101A200082608083808181608083E0E8F0E0808153 +:101A300081608083E1EBF0E0808184608083E0EB73 +:101A4000F0E0808181608083EAE7F0E0808184605B +:101A500080838081826080838081816080838081B7 +:101A6000806880831092C1000895282F30E0C9015A +:101A700086569F4FFC0194912A573F4FF90184915C +:101A8000882391F0E82FF0E0EE0FFF1FE859FF4F99 +:101A9000A591B491662329F48C91909589238C9318 +:101AA00008958C91892B8C9308951F920F920FB6F5 +:101AB0000F9211242F933F934F935F936F937F93D4 +:101AC0008F939F93AF93BF93EF93FF934091C60083 +:101AD000E091CA05F091CB05CF01019660E870E076 +:101AE0000E946C0F9C018091CC059091CD05281728 +:101AF000390739F0E65BFA4F40833093CB052093EA +:101B0000CA05FF91EF91BF91AF919F918F917F9106 +:101B10006F915F914F913F912F910F900FBE0F905A +:101B20001F9018955F926F927F928F929F92AF92C3 +:101B3000BF92CF92DF92EF92FF920F931F93CF93BA +:101B4000DF93EC013A014B01413482E458078FE006 +:101B5000680780E078070CF07FC060E874E88EE1E9 +:101B600090E0A40193010E94A10F21503040404019 +:101B70005040CA01B90122E030E040E050E00E944C +:101B8000A10F59016A01A601950120953095409554 +:101B9000509594E0220F331F441F551F9A95D1F79B +:101BA00060E074E284EF90E00E94A10FCA01B901E5 +:101BB0002FEF30E040E050E00E94390FA401930184 +:101BC0000E94A10FC90181509F4F181619061CF4DD +:101BD000522E5A9403C055245394521A60E079E06F +:101BE0008DE390E0A40193010E94A10F21503040A9 +:101BF00040405040CA01B90122E030E040E050E0EE +:101C00000E94A10F209530954095509583E0220FBA +:101C1000331F441F551F8A95D1F760E074E284EFAB +:101C200090E00E94A10FCA01B9012FEF30E040E01F +:101C300050E00E94390FA40193010E94A10FC90135 +:101C400081509F4F181619061CF4822F815002C034 +:101C500081E0821B851500F5E885F98581E090E03B +:101C60000A8802C0880F991F0A94E2F7808360E017 +:101C700079E08DE390E0A40193010E94A10F21502F +:101C8000304040405040CA01B90122E030E040E01D +:101C900050E00E94A10F04C0E885F98510829501EB +:101CA000EC81FD813083EE81FF812083EA85FB8515 +:101CB000208141E050E0CA010E8402C0880F991FC4 +:101CC0000A94E2F7282B2083EA85FB852081CA014C +:101CD0000F8402C0880F991F0A94E2F7282B2083F3 +:101CE000EA85FB858081088802C0440F551F0A944D +:101CF000E2F7842B8083DF91CF911F910F91FF90AA +:101D0000EF90DF90CF90BF90AF909F908F907F909B +:101D10006F905F900895FC01A085B18521898C9119 +:101D200090E0022E02C0959587950A94E2F780FF15 +:101D3000F6CF0484F585E02D6083089580E291E07C +:101D40009093CF058093CE058AE495E09093D105DA +:101D50008093D00585EC90E09093D3058093D205D5 +:101D600084EC90E09093D5058093D40580EC90E0CE +:101D70009093D7058093D60581EC90E09093D90598 +:101D80008093D80586EC90E09093DB058093DA058C +:101D900084E08093DC0583E08093DD0587E0809319 +:101DA000DE0585E08093DF0581E08093E0050895FE +:101DB0000F931F93CF93DF938C01EB0109C0219602 +:101DC000D801ED91FC910190F081E02DC8010995B9 +:101DD00068816623A1F7DF91CF911F910F9108953C +:101DE000EF92FF920F931F93CF93DF938C017B01B0 +:101DF000EA010CC0D7016D917D01D801ED91FC91F4 +:101E00000190F081E02DC80109952197209791F765 +:101E1000DF91CF911F910F91FF90EF900895DC011A +:101E2000ED91FC910280F381E02D099508950F93C7 +:101E30001F938C01DC01ED91FC910190F081E02D6C +:101E40006DE00995D801ED91FC910190F081E02DB4 +:101E5000C8016AE009951F910F9108950F931F9390 +:101E60008C010E940F0FC8010E94170F1F910F9144 +:101E70000895629FD001739FF001829FE00DF11DD4 +:101E8000649FE00DF11D929FF00D839FF00D749FF4 +:101E9000F00D659FF00D9927729FB00DE11DF91FA0 +:101EA000639FB00DE11DF91FBD01CF0111240895FD +:101EB000AA1BBB1B51E107C0AA1FBB1FA617B70770 +:101EC00010F0A61BB70B881F991F5A95A9F780958C +:101ED0009095BC01CD01089597FB092E07260AD0E5 +:101EE00077FD04D0E5DF06D000201AF470956195E7 +:101EF0007F4F0895F6F7909581959F4F0895A1E241 +:101F00001A2EAA1BBB1BFD010DC0AA1FBB1FEE1F73 +:101F1000FF1FA217B307E407F50720F0A21BB30BBE +:101F2000E40BF50B661F771F881F991F1A9469F73A +:101F300060957095809590959B01AC01BD01CF0196 +:101F4000089597FB092E05260ED057FD04D0D7DF44 +:101F50000AD0001C38F450954095309521953F4F9C +:101F60004F4F5F4F0895F6F7909580957095619566 +:101F70007F4F8F4F9F4F0895EE0FFF1F0590F491F5 +:081F8000E02D0994F894FFCF55 +:101F88002F004E6578742046696C652E2E2E000150 +:101F9800030507090E10121416181C1E0000000075 +:061FA8008B0ED80EF00EB6 +:00000001FF diff --git a/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.o b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.o new file mode 100644 index 0000000..5d241a8 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/FAT16_ReadExample.cpp.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/HardwareSerial.cpp.o b/FAT16/examples/FAT16_ReadExample/applet/HardwareSerial.cpp.o new file mode 100644 index 0000000..d5af083 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/HardwareSerial.cpp.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/Print.cpp.o b/FAT16/examples/FAT16_ReadExample/applet/Print.cpp.o new file mode 100644 index 0000000..c63f9bf Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/Print.cpp.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/WInterrupts.c.o b/FAT16/examples/FAT16_ReadExample/applet/WInterrupts.c.o new file mode 100644 index 0000000..52801e4 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/WInterrupts.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/WMath.cpp.o b/FAT16/examples/FAT16_ReadExample/applet/WMath.cpp.o new file mode 100644 index 0000000..d8792d0 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/WMath.cpp.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/core.a b/FAT16/examples/FAT16_ReadExample/applet/core.a new file mode 100644 index 0000000..c16866b Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/core.a differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/pins_arduino.c.o b/FAT16/examples/FAT16_ReadExample/applet/pins_arduino.c.o new file mode 100644 index 0000000..c1fa9f5 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/pins_arduino.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/wiring.c.o b/FAT16/examples/FAT16_ReadExample/applet/wiring.c.o new file mode 100644 index 0000000..394cbe8 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/wiring.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/wiring_analog.c.o b/FAT16/examples/FAT16_ReadExample/applet/wiring_analog.c.o new file mode 100644 index 0000000..619dd14 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/wiring_analog.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/wiring_digital.c.o b/FAT16/examples/FAT16_ReadExample/applet/wiring_digital.c.o new file mode 100644 index 0000000..02809ce Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/wiring_digital.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/wiring_pulse.c.o b/FAT16/examples/FAT16_ReadExample/applet/wiring_pulse.c.o new file mode 100644 index 0000000..354b096 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/wiring_pulse.c.o differ diff --git a/FAT16/examples/FAT16_ReadExample/applet/wiring_shift.c.o b/FAT16/examples/FAT16_ReadExample/applet/wiring_shift.c.o new file mode 100644 index 0000000..2f318d0 Binary files /dev/null and b/FAT16/examples/FAT16_ReadExample/applet/wiring_shift.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/FAT16_WriteExample.pde b/FAT16/examples/FAT16_WriteExample/FAT16_WriteExample.pde new file mode 100644 index 0000000..9322df3 --- /dev/null +++ b/FAT16/examples/FAT16_WriteExample/FAT16_WriteExample.pde @@ -0,0 +1,205 @@ +/* +FAT16 WriteFile Example Sketch +SparkFun Electronics +Written by Ryan Owens +3/16/2010 + +Code Description: Uses an Arduino Duemillanove or Arduino Pro to write a string to a file. + +Circuit Description: Uses the SparkFun microSD shield. (http://www.sparkfun.com/commerce/product_info.php?products_id=9520) + +Attributions: Special thanks to Roland Riegel for providing an open source FAT library +for AVR microcontrollers. See more of his projects here: +http://www.roland-riegel.de/ + +This code is provided under the Creative Commons Attribution License. More information can be found here: +http://creativecommons.org/licenses/by/3.0/ + +(Use our code freely! Please just remember to give us credit where it's due. Thanks!) +*/ + +//Add libraries to support FAT16 on the SD Card. +//(Note: If you already have these libraries installed in the directory, they'll have to remove in order to compile this.) +#include +#include +#include +#include +#include +#include +#include +#include + +//Define the pin numbers +#define CS 8 +#define MOSI 11 +#define MISO 12 +#define SCK 13 + +//This is the amount of data to be fetched from the SD card for each read. +#define BUFFERSIZE 256 + +char buffer[BUFFERSIZE]="Testing\n"; +char file_name[30]; + +struct fat_dir_struct* dd; //FAT16 directory +struct fat_dir_entry_struct dir_entry; //FAT16 directory entry (A.K.A. a file) + +struct fat_fs_struct* fs; //FAT16 File System +struct partition_struct* partition; //FAT16 Partition + +struct fat_file_struct * file_handle; //FAT16 File Handle + +void setup() +{ + //Set up the pins for the Serial communication + pinMode(0, INPUT); + pinMode(1, OUTPUT); + Serial.begin(9600); + + //Set up the pins for the microSD shield + pinMode(CS, OUTPUT); + pinMode(MOSI, OUTPUT); + pinMode(MISO, INPUT); + pinMode(SCK, OUTPUT); + pinMode(10, OUTPUT); +} + +void loop() +{ + int bytes_read=0; //Keeps track of how many bytes are read when accessing a file on the SD card. + int count=0; + + init_filesystem(); //Initialize the FAT16 file system on the SD card. + + //Create a file named Test.txt. If the file already exists, delete it and create a new one. + if(!fat_create_file(dd, "Test.txt", &dir_entry)){ + fat_delete_file(fs, &dir_entry); + fat_create_file(dd, "Test.txt", &dir_entry); + } + //Open the file that's just been created + file_handle=open_file_in_dir(fs, dd, "Test.txt"); + //Write some initial data to the file + fat_write_file(file_handle, (const uint8_t*)buffer, strlen(buffer)); + sd_raw_sync(); //An SD sync must be performed after each write operation + fat_close_file(file_handle); //Close the file. + while(1){ + //Open the file (now we're at the beginning of the file again. + open_file_in_dir(fs, dd, "Test.txt"); + //Read the contents of the file (up to 512 bytes) + bytes_read = fat_read_file(file_handle, (uint8_t*)buffer, BUFFERSIZE); + //Print the contents of the file + Serial.println((const char*)buffer); + //Now go to the end of the file to write some data. + fat_seek_file(file_handle, 0, FAT_SEEK_END); + sprintf(buffer, "%d", count++); + //Write the new 'buffer' string to the end of the file + fat_write_file(file_handle, (const uint8_t*)buffer, strlen(buffer)); + sd_raw_sync(); + //Close the file, we're finished! + fat_close_file(file_handle); + delay(1000); + } + + while(1); +} + +uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry) +{ + fat_reset_dir(dd); //Make sure to start from the beginning of the directory! + while(fat_read_dir(dd, dir_entry)) + { + if(strcmp(dir_entry->long_name, name) == 0) + { + //fat_reset_dir(dd); + return 1; + } + } + + return 0; +} + +struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name) +{ + struct fat_dir_entry_struct file_entry; + if(!find_file_in_dir(fs, dd, name, &file_entry)) + return 0; + + return fat_open_file(fs, &file_entry); +} + +char init_filesystem(void) +{ + //setup sd card slot + if(!sd_raw_init()) + { + return 0; + } + + //open first partition + partition = partition_open(sd_raw_read, + sd_raw_read_interval, +#if SD_RAW_WRITE_SUPPORT + sd_raw_write, + sd_raw_write_interval, +#else + 0, + 0, +#endif + 0 + ); + + if(!partition) + { + //If the partition did not open, assume the storage device + //is a "superfloppy", i.e. has no MBR. + partition = partition_open(sd_raw_read, + sd_raw_read_interval, +#if SD_RAW_WRITE_SUPPORT + sd_raw_write, + sd_raw_write_interval, +#else + 0, + 0, +#endif + -1 + ); + if(!partition) + { + return 0; + } + } + + //Open file system + fs = fat_open(partition); + if(!fs) + { + return 0; + } + + //Open root directory + fat_get_dir_entry_of_path(fs, "/", &dir_entry); + dd=fat_open_dir(fs, &dir_entry); + + if(!dd) + { + return 0; + } + return 1; +} + +char get_next_filename(struct fat_dir_struct* cur_dir, char * new_file) +{ + //'dir_entry' is a global variable of type directory_entry_struct + + //Get the next file from the root directory + if(fat_read_dir(cur_dir, &dir_entry)) + { + sprintf(new_file, "%s", dir_entry.long_name); + Serial.println((const char *)new_file); + return 1; + } + //If another file isn't found, return 0 + return 0; +} + + diff --git a/FAT16/examples/FAT16_WriteExample/applet/FAT16/byteordering.c.o b/FAT16/examples/FAT16_WriteExample/applet/FAT16/byteordering.c.o new file mode 100644 index 0000000..07d283e Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/FAT16/byteordering.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/FAT16/fat.c.o b/FAT16/examples/FAT16_WriteExample/applet/FAT16/fat.c.o new file mode 100644 index 0000000..1125cad Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/FAT16/fat.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/FAT16/partition.c.o b/FAT16/examples/FAT16_WriteExample/applet/FAT16/partition.c.o new file mode 100644 index 0000000..f5741e1 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/FAT16/partition.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/FAT16/sd_raw.c.o b/FAT16/examples/FAT16_WriteExample/applet/FAT16/sd_raw.c.o new file mode 100644 index 0000000..b020ac4 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/FAT16/sd_raw.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp new file mode 100644 index 0000000..7fb0ea7 --- /dev/null +++ b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp @@ -0,0 +1,225 @@ +/* +FAT16 WriteFile Example Sketch +SparkFun Electronics +Written by Ryan Owens +3/16/2010 + +Code Description: Uses an Arduino Duemillanove or Arduino Pro to write a string to a file. + +Circuit Description: Uses the SparkFun microSD shield. (http://www.sparkfun.com/commerce/product_info.php?products_id=9520) + +Attributions: Special thanks to Roland Riegel for providing an open source FAT library +for AVR microcontrollers. See more of his projects here: +http://www.roland-riegel.de/ + +This code is provided under the Creative Commons Attribution License. More information can be found here: +http://creativecommons.org/licenses/by/3.0/ + +(Use our code freely! Please just remember to give us credit where it's due. Thanks!) +*/ + +//Add libraries to support FAT16 on the SD Card. +//(Note: If you already have these libraries installed in the directory, they'll have to remove in order to compile this.) +#include +#include +#include +#include +#include +#include +#include +#include + +//Define the pin numbers +#define CS 8 +#define MOSI 11 +#define MISO 12 +#define SCK 13 + +//This is the amount of data to be fetched from the SD card for each read. +#define BUFFERSIZE 256 + +#include "WProgram.h" +void setup(); +void loop(); +uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry); +struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name); +char init_filesystem(void); +char get_next_filename(struct fat_dir_struct* cur_dir, char * new_file); +char buffer[BUFFERSIZE]="Testing\n"; +char file_name[30]; + +struct fat_dir_struct* dd; //FAT16 directory +struct fat_dir_entry_struct dir_entry; //FAT16 directory entry (A.K.A. a file) + +struct fat_fs_struct* fs; //FAT16 File System +struct partition_struct* partition; //FAT16 Partition + +struct fat_file_struct * file_handle; //FAT16 File Handle + +void setup() +{ + //Set up the pins for the Serial communication + pinMode(0, INPUT); + pinMode(1, OUTPUT); + Serial.begin(9600); + + //Set up the pins for the microSD shield + pinMode(CS, OUTPUT); + pinMode(MOSI, OUTPUT); + pinMode(MISO, INPUT); + pinMode(SCK, OUTPUT); + pinMode(10, OUTPUT); +} + +void loop() +{ + int bytes_read=0; //Keeps track of how many bytes are read when accessing a file on the SD card. + int count=0; + + init_filesystem(); //Initialize the FAT16 file system on the SD card. + + //Create a file named Test.txt. If the file already exists, delete it and create a new one. + if(!fat_create_file(dd, "Test.txt", &dir_entry)){ + fat_delete_file(fs, &dir_entry); + fat_create_file(dd, "Test.txt", &dir_entry); + } + //Open the file that's just been created + file_handle=open_file_in_dir(fs, dd, "Test.txt"); + //Write some initial data to the file + fat_write_file(file_handle, (const uint8_t*)buffer, strlen(buffer)); + sd_raw_sync(); //An SD sync must be performed after each write operation + fat_close_file(file_handle); //Close the file. + while(1){ + //Open the file (now we're at the beginning of the file again. + open_file_in_dir(fs, dd, "Test.txt"); + //Read the contents of the file (up to 512 bytes) + bytes_read = fat_read_file(file_handle, (uint8_t*)buffer, BUFFERSIZE); + //Print the contents of the file + Serial.println((const char*)buffer); + //Now go to the end of the file to write some data. + fat_seek_file(file_handle, 0, FAT_SEEK_END); + sprintf(buffer, "%d", count++); + //Write the new 'buffer' string to the end of the file + fat_write_file(file_handle, (const uint8_t*)buffer, strlen(buffer)); + sd_raw_sync(); + //Close the file, we're finished! + fat_close_file(file_handle); + delay(1000); + } + + while(1); +} + +uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry) +{ + fat_reset_dir(dd); //Make sure to start from the beginning of the directory! + while(fat_read_dir(dd, dir_entry)) + { + if(strcmp(dir_entry->long_name, name) == 0) + { + //fat_reset_dir(dd); + return 1; + } + } + + return 0; +} + +struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name) +{ + struct fat_dir_entry_struct file_entry; + if(!find_file_in_dir(fs, dd, name, &file_entry)) + return 0; + + return fat_open_file(fs, &file_entry); +} + +char init_filesystem(void) +{ + //setup sd card slot + if(!sd_raw_init()) + { + return 0; + } + + //open first partition + partition = partition_open(sd_raw_read, + sd_raw_read_interval, +#if SD_RAW_WRITE_SUPPORT + sd_raw_write, + sd_raw_write_interval, +#else + 0, + 0, +#endif + 0 + ); + + if(!partition) + { + //If the partition did not open, assume the storage device + //is a "superfloppy", i.e. has no MBR. + partition = partition_open(sd_raw_read, + sd_raw_read_interval, +#if SD_RAW_WRITE_SUPPORT + sd_raw_write, + sd_raw_write_interval, +#else + 0, + 0, +#endif + -1 + ); + if(!partition) + { + return 0; + } + } + + //Open file system + fs = fat_open(partition); + if(!fs) + { + return 0; + } + + //Open root directory + fat_get_dir_entry_of_path(fs, "/", &dir_entry); + dd=fat_open_dir(fs, &dir_entry); + + if(!dd) + { + return 0; + } + return 1; +} + +char get_next_filename(struct fat_dir_struct* cur_dir, char * new_file) +{ + //'dir_entry' is a global variable of type directory_entry_struct + + //Get the next file from the root directory + if(fat_read_dir(cur_dir, &dir_entry)) + { + sprintf(new_file, "%s", dir_entry.long_name); + Serial.println((const char *)new_file); + return 1; + } + //If another file isn't found, return 0 + return 0; +} + + + +int main(void) +{ + init(); + + setup(); + + for (;;) + loop(); + + return 0; +} + diff --git a/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.eep b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.eep new file mode 100644 index 0000000..1996e8f --- /dev/null +++ b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.eep @@ -0,0 +1 @@ +:00000001FF diff --git a/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.elf b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.elf new file mode 100644 index 0000000..a82c1fa Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.elf differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.hex b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.hex new file mode 100644 index 0000000..8e576ae --- /dev/null +++ b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.hex @@ -0,0 +1,883 @@ +:100000000C9462000C948A000C948A000C948A0070 +:100010000C948A000C948A000C948A000C948A0038 +:100020000C948A000C948A000C948A000C948A0028 +:100030000C948A000C948A000C948A000C948A0018 +:100040000C9480170C948A000C944C180C948A0021 +:100050000C948A000C948A000C948A000C948A00F8 +:100060000C948A000C948A000000000024002700F1 +:100070002A0000000000250028002B0000000000DE +:1000800023002600290004040404040404040202DA +:100090000202020203030303030301020408102007 +:1000A0004080010204081020010204081020000012 +:1000B0000007000201000003040600000000000029 +:1000C0000000951911241FBECFEFD8E0DEBFCDBFD1 +:1000D00012E0A0E0B1E0E4EEF5E302C005900D927D +:1000E000A632B107D9F715E0A6E2B2E001C01D9231 +:1000F000A33CB107E1F710E0C4ECD0E004C02297C4 +:10010000FE010E94B51AC23CD107C9F70E947917B7 +:100110000C94F01A0C940000FB01DC0102C0019069 +:100120000D9241505040D8F70895DC0101C06D9305 +:1001300041505040E0F70895FC018191861721F06D +:100140008823D9F7992708953197CF010895FB01A6 +:10015000DC018D91019080190110D9F3990B08955C +:10016000FB01DC014150504030F08D91019080192D +:1001700019F40020B9F7881B990B0895FB01DC01E5 +:100180004150504048F001900D920020C9F701C045 +:100190001D9241505040E0F70895FC0181E090E04D +:1001A0000190061609F4CF010020D1F701970895B8 +:1001B000AEE0B0E0EEEDF0E00C94C71A0D891E89B8 +:1001C00086E08C831A8309838FEF9FE79E838D835C +:1001D0009E01275E3F4FCE0101966F89788DA90160 +:1001E0000E94FC002F813885020F131FF801108236 +:1001F0002E96E4E00C94E31AABE0B0E0E2E0F1E02C +:100200000C94B91A3C012B015A01FC011782168289 +:10021000838181FD03C06FEF7FEFC6C19AE0892E15 +:100220001E010894211C311CF3012381F20123FDDE +:10023000859123FF81912F01882309F4B2C1853272 +:1002400039F423FD859123FF81912F01853229F413 +:1002500090E0B3010E94EF02E7CF982FFF24EE2435 +:100260009924FFE1FF15D0F09B3269F09C3228F40D +:10027000903259F0933291F40EC09D3249F0903390 +:1002800069F441E024C052E0F52A84E0F82A28C04D +:1002900098E0F92A25C0E0E1FE2A22C0F7FC29C037 +:1002A000892F80538A3070F4F6FE05C0989C902CFC +:1002B0001124980E15C0E89CE02C1124E80EF0E201 +:1002C000FF2A0EC09E3229F4F6FC6BC140E4F42AEA +:1002D00007C09C3619F450E8F52A02C0983649F454 +:1002E000F20123FD959123FF91912F01992309F0AC +:1002F000B8CF892F8554833018F08052833038F47A +:1003000044E050E0A40EB51E5FE359830FC093365E +:1003100031F0933779F0933509F056C020C0F501DC +:100320008081898342E050E0A40EB51E610101E0A6 +:1003300010E012C0F501C080D180F6FC03C06FEF61 +:100340007FEF02C0692D70E042E050E0A40EB51EC0 +:10035000C6010E94E4028C015FE7F52214C0F5019A +:10036000C080D180F6FC03C06FEF7FEF02C0692D23 +:1003700070E042E050E0A40EB51EC6010E94D90212 +:100380008C0150E8F52AF3FE07C01AC080E290E025 +:10039000B3010E94EF02EA948E2D90E0081719072E +:1003A000A8F30EC0F601F7FC8591F7FE81916F016D +:1003B00090E0B3010E94EF02E110EA940150104076 +:1003C0000115110579F7EAC0943611F0993669F5EF +:1003D000F7FE08C0F501208131814281538184E01C +:1003E00090E00AC0F501808191819C01442737FD8E +:1003F0004095542F82E090E0A80EB91E9FE6F922A6 +:1004000057FF09C050954095309521953F4F4F4F6C +:100410005F4FE0E8FE2ACA01B901A1012AE030E0FD +:100420000E941B03D82ED21840C0953729F41F2DE7 +:100430001F7E2AE030E01DC01F2D197F9F3661F01E +:10044000903720F4983509F0ACC00FC0903739F0E0 +:10045000983709F0A6C004C028E030E00AC0106157 +:1004600014FD146020E130E004C014FD166020E1AA +:1004700032E017FF08C0F5016081718182819381AC +:1004800044E050E008C0F50180819181BC0180E02A +:1004900090E042E050E0A40EB51EA1010E941B03B3 +:1004A000D82ED2188FE7F82EF122F6FE0BC05EEFA1 +:1004B000F522D91438F4F4FE07C0F2FC05C08FEE23 +:1004C000F82202C01D2D01C0192DF4FE0DC0FE0141 +:1004D000ED0DF11D8081803319F499EEF92208C0E9 +:1004E0001F5FF2FE05C003C08F2D867809F01F5FE5 +:1004F0000F2DF3FC14C0F0FE0FC01E1510F09D2C44 +:100500000BC09D2C9E0C911A1E2D06C080E290E01F +:10051000B3010E94EF021F5F1E15C0F304C01E1539 +:1005200010F4E11A01C0EE2404FF0FC080E390E054 +:10053000B3010E94EF0202FF1DC001FD03C088E766 +:1005400090E00EC088E590E00BC0802F867891F097 +:1005500001FF02C08BE201C080E2F7FC8DE290E077 +:10056000B3010E94EF0206C080E390E0B3010E9455 +:10057000EF029A94D914C0F3DA94F101ED0DF11D54 +:10058000808190E0B3010E94EF02DD20A9F706C050 +:1005900080E290E0B3010E94EF02EA94EE20C1F7FE +:1005A00043CEF30166817781CB012B96E2E10C9477 +:1005B000D51AFC010590615070400110D8F7809564 +:1005C00090958E0F9F1F0895FC016150704001901F +:1005D0000110D8F7809590958E0F9F1F08950F9367 +:1005E0001F93CF93DF938C01EB018B8181FF1BC0A5 +:1005F00082FF0DC02E813F818C819D812817390794 +:1006000064F4E881F9810193F983E88306C0E88501 +:10061000F985802F0995892B31F48E819F81019670 +:100620009F838E8302C00FEF1FEFC801DF91CF9130 +:100630001F910F910895FA01AA27283051F1203116 +:1006400081F1E8946F936E7F6E5F7F4F8F4F9F4F66 +:10065000AF4FB1E03ED0B4E03CD0670F781F891FA8 +:100660009A1FA11D680F791F8A1F911DA11D6A0F76 +:10067000711D811D911DA11D20D009F468943F9129 +:100680002AE0269F11243019305D3193DEF6CF0128 +:100690000895462F4770405D4193B3E00FD0C9F7EE +:1006A000F6CF462F4F70405D4A3318F0495D31FD5B +:1006B0004052419302D0A9F7EACFB4E0A6959795AE +:1006C000879577956795BA95C9F700976105710584 +:1006D00008959B01AC010A2E0694579547953795CE +:1006E0002795BA95C9F7620F731F841F951FA01D28 +:1006F0000895DF93CF9300D0CDB7DEB7DC0100972C +:1007000081F16230710568F1ED91FC911197660FEE +:10071000771F80E090E016962D913D914D915C9170 +:100720001997620F731F841F951F0190F081E02DB0 +:10073000AE014F5F5F4F22E030E00995882389F0DA +:1007400029813A812115310571F08FEF273F380754 +:1007500041F0C9014096079720F08FEF283F3807F6 +:1007600010F020E030E0C9010F900F90CF91DF91A1 +:1007700008954F925F926F927F928F929F92AF9265 +:10078000BF92CF92DF92EF92FF920F931F93DF936E +:10079000CF9300D0CDB7DEB75C01009709F449C014 +:1007A0006230710508F445C0DC0116966D907D90AD +:1007B0008D909C9019972E010894411C511C660F36 +:1007C000771F7B0100E010E0E60CF71C081D191DE7 +:1007D000D501ED91FC910190F081E02DC801B701A8 +:1007E000A20122E030E00995882319F1C980DA805E +:1007F000C114D10401F1C60140960897D0F0B8EFBA +:10080000CB16BFEFDB0610F0CC24DD241A82198250 +:10081000D501ED91FC910480F581E02DC801B7016F +:10082000A20122E030E00995B601C114D10439F6E5 +:1008300002C080E001C081E00F900F90CF91DF9166 +:100840001F910F91FF90EF90DF90CF90BF90AF90EE +:100850009F908F907F906F905F904F9008952F9210 +:100860003F924F925F926F927F928F929F92AF9240 +:10087000BF92CF92DF92EF92FF920F931F93DF937D +:10088000CF93CDB7DEB72A970FB6F894DEBF0FBE71 +:10089000CDBF3C012B01009709F48FC0DC01ED9125 +:1008A000FC911197208131813A87298724803580F6 +:1008B00016968D919D910D90BC91A02D8D839E8358 +:1008C000AF83B887F30182859385A485B585B695F6 +:1008D000A795979587959C838B834A01CC24DD242B +:1008E00042E0A42EB12C41C0C501880F991F7C01A4 +:1008F00000E010E02D813E814F815885E20EF31E0D +:10090000041F151FC801B701AE014F5F5F4F22E002 +:1009100030E0A985BA85FD010995882309F44DC009 +:1009200089819A81892BF1F4C114D10429F4EFEF64 +:10093000FFEFFA83E98302C0DA82C982C801B701F6 +:10094000AE014F5F5F4F22E030E0F101099588234F +:1009500091F00894810891088114910411F46501C3 +:100960000CC065010894A11CB11C2B813C81A2160E +:10097000B30608F4B9CF8928E1F432E043165104F4 +:10098000F0F0DA82C982440C551CB20180E090E09C +:100990002D813E814F815885620F731F841F951FE3 +:1009A000AE014F5F5F4F22E030E0F10109958823EF +:1009B00031F4C301B6010E94B903CC24DD24C60181 +:1009C0002A960FB6F894DEBF0FBECDBFCF91DF9150 +:1009D0001F910F91FF90EF90DF90CF90BF90AF905D +:1009E0009F908F907F906F905F904F903F902F904F +:1009F000089580E190E00895FC01009711F01182C4 +:100A0000108208952F923F924F925F926F927F9241 +:100A10008F929F92AF92BF92CF92DF92EF92FF920E +:100A20000F931F93DF93CF93CDB7DEB72A970FB6FF +:100A3000F894DEBF0FBECDBF2C017A8369835C833F +:100A40004B83009709F409C1672B09F406C1452BB4 +:100A500009F403C1DC019D96AD90BD90CD90DC9072 +:100A6000D09795962D913D914D915C919897EB8102 +:100A7000FC81CF01A0E0B0E08A0D9B1DAC1DBD1D27 +:100A8000281739074A075B0740F4B9016A197B093F +:100A90007C836B83672B09F4E3C0D201ED91FC9159 +:100AA000119720883188D196ED91FC91D297FA87E1 +:100AB000E987EF2BF1F593962D913C9194973A8726 +:100AC0002987232B39F4A114B104C104D10409F4FA +:100AD000C7C0C3C0A114B104C104D10451F186013F +:100AE000750131018824992466277727CB0166197F +:100AF0007709880999096D837E838F83988714C04D +:100B0000D2018D919C9169857A850E9479039A879B +:100B10008987892B09F4A1C06D817E818F81988599 +:100B2000E60EF71E081F191FE614F704080519053D +:100B300038F7089421083108A220B3200894211C1A +:100B4000311CCB80DC80D201ED90FC90E114F104EB +:100B500009F1E985FA85E230F105E0F03297BF014D +:100B600080E090E03296FA87E987D70150962D9180 +:100B70003C91519740E050E00E94301A9B01AC013B +:100B8000F70182899389A489B589280F391F4A1FE3 +:100B90005B1F04C020E030E040E050E081010A1912 +:100BA0001B09C016D10608F48601D701ED91FC910E +:100BB000C501A0E0B0E0280F391F4A1F5B1F01905C +:100BC000F081E02DCA01B90149815A819801099546 +:100BD000882341F1C01AD10AC801A0E0B0E0F201B7 +:100BE00025A536A547A550A9280F391F4A1F5B1F09 +:100BF000D2019D962D933D934D935C93D097C80160 +:100C00008A0D9B1D82159305A0F08D919C9169859D +:100C10007A850E9479039A878987892B41F4F201AA +:100C200012AA11AA4B815C814C195D091BC0AA2430 +:100C3000BB24E985FA85D201D296FC93EE93D19735 +:100C4000C114D10439F029813A81200F311F3A8330 +:100C5000298379CF4B815C8105C04FEF5FEF02C0E4 +:100C600040E050E0CA012A960FB6F894DEBF0FBEEE +:100C7000CDBFCF91DF911F910F91FF90EF90DF904B +:100C8000CF90BF90AF909F908F907F906F905F902C +:100C90004F903F902F900895FC01009711F480E051 +:100CA000089583A194A196A785A710AA17A681E00D +:100CB0000895CF92DF92EF92FF920F931F93CF93FD +:100CC000DF938C016A017B01A901D901CD91DC91EF +:100CD000119712968D919C911397809613969C93E1 +:100CE0008E931297F8018081853E09F4BAC088235B +:100CF00009F4B7C08FA198A5A9A5BAA50097A10529 +:100D0000B10521F4CFA2D8A6E9A6FAA6209709F446 +:100D1000AAC00115110509F4A6C0D8019C91992318 +:100D200009F4A1C01B968C918F30A1F5892F90E01A +:100D30008F7390702DE030E0829FA001839F500D53 +:100D4000929F500D11244D50504010922702109246 +:100D5000260211C0AC0FBD1FF901E25FFD4F80817B +:100D6000F801E80FF11D80818C932F5F3F4F309386 +:100D700027022093260220912602309127022D304F +:100D8000310508F06EC0DA01A20FB31FAF31B10513 +:100D900008F367C08881882309F04FC0B801DE01DD +:100DA000780120E0F70190819032A1F09C93F80146 +:100DB000848583FF08C0F7018081813420F08B3562 +:100DC00010F4905E9C932F5F0894E11CF11C119627 +:100DD000283041F78881853011F485EE8883D80169 +:100DE00018968C91803211F4322F23C0FE01E20F4D +:100DF000F11D8EE28083322F3F5F2C5FFB019085D7 +:100E00009032B9F0FE01E30FF11D9083D8011C96DA +:100E10008C9184FF09C0DB0118968C91813420F0FD +:100E20008B3510F4905E90833F5F6F5F7F4F32177A +:100E300029F7FE01E30FF11D1082F801838588A3D5 +:100E4000828D938D9AA389A3848D958DA68DB78D60 +:100E50008BA39CA3ADA3BEA381E0DA0114968C936F +:100E600002C081E001C080E0DF91CF911F910F911E +:100E7000FF90EF90DF90CF9008957F928F929F9296 +:100E8000AF92BF92CF92DF92EF92FF920F931F9398 +:100E9000DF93CF93CDB7DEB72C970FB6F894DEBFB4 +:100EA0000FBECDBF6C015B01009709F453C0611503 +:100EB000710509F44FC0DB019796ED90FD900D91FF +:100EC0001C919A97E114F1040105110509F442C03F +:100ED0004E010894811C911C75EE772ED601ED9180 +:100EE000FC910190F081E02DC801B701A4012CE034 +:100EF00030E00995882371F17982D601ED91FC915A +:100F00000480F581E02DC801B701A4012CE030E098 +:100F100009958823F9F08C858F3049F480E290E0C0 +:100F2000A0E0B0E0E80EF91E0A1F1B1FD7CFD501C5 +:100F300091966D917C9192976115710539F0C6017A +:100F40000E94B90320E030E0882311F021E030E076 +:100F5000822F01C080E02C960FB6F894DEBF0FBE42 +:100F6000CDBFCF91DF911F910F91FF90EF90DF9058 +:100F7000CF90BF90AF909F908F907F900895CF9229 +:100F8000DF92EF92FF920F931F93DF93CF93CDB732 +:100F9000DEB760970FB6F894DEBF0FBECDBF8C01F1 +:100FA0006230710510F480E035C0009729F440E00C +:100FB00050E060E070E016C06250704080E090E069 +:100FC000F8012089318940E050E00E94301AAB01DD +:100FD000BC01F80182899389A489B589480F591FFA +:100FE0006A1F7B1FDE01119680E1FD0111928A9537 +:100FF000E9F7F8018081918120893189FC01C6805F +:10100000D780CB01BA01AD0109EF14E0EE24FF2433 +:10101000F601099560960FB6F894DEBF0FBECDBFFE +:10102000CF91DF911F910F91FF90EF90DF90CF90C4 +:1010300008952F923F924F925F926F927F928F927C +:101040009F92AF92BF92CF92DF92EF92FF920F9357 +:101050001F93DF93CF93CDB7DEB7A7970FB6F89462 +:10106000DEBF0FBECDBF2C017FA36EA3009709F496 +:10107000B1C06115710509F4ADC0DC018D909C9083 +:101080001197F401208831889D96AD90BC909E9771 +:101090009F966D907C90D0978BE2DB011D928A9594 +:1010A000E9F7FE01319685E0DF011D928A95E9F7A7 +:1010B000A114B10409F076C0F4012288338886892E +:1010C0009789281A390A6EC0730100E010E0A11454 +:1010D000B10441F4D40156966D917D918D919C910E +:1010E00059972DC08114910421F1B2E0AB16B104DF +:1010F00000F1EEEFFFEFAE0EBF1EB50180E090E015 +:1011000022E030E0A20EB31ED40150962D913C9106 +:10111000519740E050E00E94301A9B01AC01F4016D +:1011200082899389A489B589280F391F4A1F5B1FBB +:1011300004C020E030E040E050E0CA01B9016E0D8B +:101140007F1D801F911F1C821B82D401ED91FC9199 +:101150008101061917090280F381E02DAE014A5F73 +:101160005F4F20E230E0A9E5EA2EA6E0FA2E6E01FC +:101170000894C11CD11C0995882361F18B819C8145 +:10118000680E791E6214730488F0C401B5010E94D0 +:1011900079035C0166247724009741F4F201A3A04F +:1011A000B4A008C02EA13FA13A8329838D81882352 +:1011B00009F48ACFD2019E96BC92AE929D97D096AA +:1011C0007C926E929F97EEA1FFA18081882319F0F7 +:1011D00081E001C080E0A7960FB6F894DEBF0FBE95 +:1011E000CDBFCF91DF911F910F91FF90EF90DF90D6 +:1011F000CF90BF90AF909F908F907F906F905F90B7 +:101200004F903F902F9008952F923F924F925F9270 +:101210006F927F928F929F92AF92BF92CF92DF9206 +:10122000EF92FF920F931F93DF93CF93CDB7DEB76B +:10123000A8970FB6F894DEBF0FBECDBF98A78FA3B7 +:10124000009709F45CC1DC0114968D919C9115976F +:10125000892B09F454C116968D919C911797892B6F +:1012600009F44DC168C1E8E2F2E08AE1DF011D92B4 +:101270008A95E9F7EFA1F8A5F0932902E0932802F7 +:10128000218432844384548459E0220C331C441C4E +:10129000551C5A95D1F7C201B101655F7F4F8F4F41 +:1012A0009F4F0190F081E02DAE014F5F5F4F29E12C +:1012B00030E00995882309F445C1E981FA81FBA34F +:1012C000EAA32C813D81FB81FCA34E818F8198850F +:1012D0009EA38DA3E985FA856C857D856E887F88C0 +:1012E000888C998C611471048104910431F43097D5 +:1012F00009F428C13F01882499246115710509F476 +:1013000021C1C901A0E0B0E08E8F9F8FA8A3B9A32F +:1013100080E090E0242F30E040E050E00E94301A5E +:101320006A8F7B8F8C8F9D8F840173012E8D3F8DF3 +:1013300048A159A1E21AF30A040B150BE61AF70AA1 +:10134000080B190B8AA19BA101972DA13EA145E095 +:10135000220F331F4A95E1F7820F931F6AA17BA1E9 +:101360000E944F1A80E090E0E61AF70A080B190B6A +:101370008CA1282F30E040E050E0C801B7010E9466 +:10138000761A79018A0195EFE9169FE0F90690E057 +:10139000090790E0190708F4D5C0A5EFEA16AFEFEA +:1013A000FA06A0E00A07A0E01A0728F486E0EFA1F9 +:1013B000F8A5808705C08BE0AFA1B8A518968C93DF +:1013C00088E1EAE2F2E0DF011D928A95E9F7EAA1FD +:1013D000FBA15F01CC24DD24C401B301A60195016A +:1013E0000E94301A60932A0270932B0280932C0281 +:1013F00090932D02C601B5012E8D3F8D48A159A1B4 +:101400000E94301A260E371E481E591E20922E02A8 +:1014100030922F024092300250923102AFA1B8A513 +:1014200018968C91863029F064E070E080E090E0BE +:1014300004C062E070E080E090E022E030E040E054 +:1014400050E0E20EF31E041F151FA80197010E9431 +:10145000301A609332027093330280933402909377 +:1014600035024AA15BA150933702409336025CA13A +:10147000852F90E0DC01EAA1FBA1AE9FC001AF9FE8 +:10148000900DBE9F900D11249093390280933802E5 +:101490006A8D7B8D8C8D9D8DA60195010E94301AE1 +:1014A00020912E0230912F02409130025091310252 +:1014B000620F731F841F951F60933E0270933F025B +:1014C0008093400290934102ADA1BEA19D0140E0F6 +:1014D00050E0B5E0220F331F441F551FBA95D1F7D6 +:1014E000620F731F841F951F60933A0270933B0233 +:1014F00080933C0290933D0228E232E002C020E05B +:1015000030E0C901A8960FB6F894DEBF0FBECDBF7C +:10151000CF91DF911F910F91FF90EF90DF90CF90CF +:10152000BF90AF909F908F907F906F905F904F9003 +:101530003F902F9008958091280290912902892B45 +:10154000F1F691CE1092290210922802D8CF2F9254 +:101550003F924F925F926F927F928F929F92AF9243 +:10156000BF92CF92DF92EF92FF920F931F93DF9380 +:10157000CF9300D00F92CDB7DEB71C019B0100972F +:1015800009F4D2C04115510509F4CEC0FA01019009 +:101590000020E9F7E41BF50BCF010B966DE070E03E +:1015A0000E944F1A862F8F5F8B83D90193964D909F +:1015B0005C9094974114510451F0AA24BB24650116 +:1015C000EE24FF2487016624772443010BC0F10138 +:1015D000E688F788008D118D6288738884889588E5 +:1015E000570168011A82E614F7040805190509F085 +:1015F0006EC04114510409F497C0E114F1040105CF +:10160000110589F1C101B2010E947903009751F5DA +:10161000C101B20141E050E00E942F048C0100970B +:1016200009F482C002501040B80180E090E00E5FE3 +:101630001F4FD10150962D913C91519740E050E0C1 +:101640000E94301A5B016C01F10182899389A4899F +:10165000B589A80EB91ECA1EDB1EC101B8010E94C1 +:10166000BF0765C02C01F2E04F16510420F4EE24B0 +:10167000FF2487011DC02EEF3FEF420E531EB20123 +:1016800080E090E0A2E0B0E04A0E5B1EF10120890C +:10169000318940E050E00E94301A7B018C01F10159 +:1016A00082899389A489B589E80EF91E0A1F1B1F38 +:1016B000D10150968D919C9151973C018824992499 +:1016C0006E0C7F1C801E911E570168011A82D10189 +:1016D000ED91FC910190F081E02DC801B701AE01C0 +:1016E0004F5F5F4F21E030E009958823E9F0898161 +:1016F000853E11F0882379F4BA81BF5FBA83EB810C +:10170000BE17A8F480E290E0A0E0B0E0E80EF91E79 +:101710000A1F1B1F68CF80E290E0A0E0B0E0E80E57 +:10172000F91E0A1F1B1F5CCFAA24BB246501B5014B +:10173000C6010F900F900F90CF91DF911F910F91E5 +:10174000FF90EF90DF90CF90BF90AF909F908F90E1 +:101750007F906F905F904F903F902F9008953F92B1 +:101760004F925F926F927F928F929F92AF92BF92B1 +:10177000CF92DF92EF92FF920F931F93DF93CF935D +:10178000CDB7DEB7A0970FB6F894DEBF0FBECDBFC2 +:10179000FC018B01009709F424C16115710509F45E +:1017A00020C10190F081E02D44805580DB019796A7 +:1017B000AD90BD90CD90DC909A97E62E912EFB01D6 +:1017C00001900020E9F73197FE2EF61A8F2D90E058 +:1017D0000C966DE070E00E94631A762ECE010196A1 +:1017E00060E270E04BE050E00E949500C8016EE2BC +:1017F00070E00E94CD00FC010097E1F09C012F5F9A +:101800003F4F81818823B1F0D9010D900020E9F785 +:101810001197A21BB30B8A2F8095F80EBE01675F4C +:101820007F4F4A2FA43008F043E0CB01B90150E0CC +:101830000E948C00CE010196B8E0BF1588F06E2D95 +:10184000792D4F2D50E00E948C00F80180818E325E +:1018500031F581818E3209F482818823F9F01FC02D +:10186000DC018E2D992D9C01F90188E001900D92EB +:101870008150E1F7D80191968C91982F92959F70A5 +:10188000803A10F4905D01C09F598F708A3010F437 +:10189000805D01C08F599F83888701C0772489812B +:1018A000853E11F485E0898385E1FE013C96DF01E8 +:1018B0001D928A95E9F7F80180A18C8781A192A1F8 +:1018C0009C8F8B8F83A194A1A5A1B6A18D8F9E8F94 +:1018D000AF8FB8A3672D70E0E5E0660F771FEA953C +:1018E000E1F780E090E06A0D7B1D8C1D9D1DAE012F +:1018F0004F5F5F4F20E230E0F2010995882309F441 +:1019000070C0E98081E090E09093270280932602E6 +:10191000FE01329621E030E00AC08E2D8695E794D4 +:10192000EE24E794E82A8191E80E2F5F3F4F2B3099 +:10193000310598F33093270220932602F72C4E01AD +:101940000894811C911C7DE0372E6FE0662E44C008 +:10195000C4016FEF70E040E250E00E949500F39CFC +:10196000D00111241D97A00FB11F91E0F401E90FE0 +:10197000F11D8C918083892F8F5FF401E80FF11D99 +:1019800010829E5F9B3019F09A3121F402C09EE0D4 +:1019900001C09CE18C91882321F09F3110F41196B5 +:1019A000E5CFF982F71419F48F2D806489836C8652 +:1019B000EE861D861B8E1C8EC601B501A40120E299 +:1019C00030E0F201099580E290E0A0E0B0E0A80EDE +:1019D000B91ECA1EDB1EFA94FF2009F0B9CF81E0C0 +:1019E00001C080E0A0960FB6F894DEBF0FBECDBF59 +:1019F000CF91DF911F910F91FF90EF90DF90CF90EB +:101A0000BF90AF909F908F907F906F905F904F901E +:101A10003F900895AF92BF92CF92DF92EF92FF92E4 +:101A20000F931F93CF93DF938C017B01EA01009703 +:101A300009F448C06115710509F444C0FB018081B7 +:101A4000882309F43FC041155105E1F1B42EAD2EB4 +:101A5000C801BE010E941908882379F0C7016B2DC7 +:101A60007A2D0E94A700009799F7F80183A194A10D +:101A700096A785A710AA17A625C0F801C080D18017 +:101A80008BE2FE0111928A95E9F78B2D9A2DB70111 +:101A90004FE150E00E94BE00C601B801AE010E94B5 +:101AA000A70A6FA378A789A79AA761157105810571 +:101AB000910541F0C601BE010E94AF0B882319F0C9 +:101AC00081E001C080E0DF91CF911F910F91FF90E5 +:101AD000EF90DF90CF90BF90AF9008954F925F92BC +:101AE0006F927F928F929F92AF92BF92CF92DF922E +:101AF000EF92FF920F931F93DF93CF93CDB7DEB793 +:101B00002A970FB6F894DEBF0FBECDBF2C015A0145 +:101B10006B01009709F4E5C0DC0193960D911C91CF +:101B20009497ED91FC91208931890115110599F067 +:101B3000AB82BC82CD82DE8239018824992422279F +:101B40003327A90126193709480959092F833887EE +:101B500049875A8720C0411551056105710509F46F +:101B6000A9C0E6CFD2018D919C91B8010E94790362 +:101B70000097D9F0EB80FC800D811E812F81388584 +:101B800049855A85E20EF31E041F151FEB82FC8265 +:101B90000D831E838C018B819C81AD81BE81681673 +:101BA00079068A069B06F0F2B4C078016B817C81CD +:101BB0008D819E81660D771D881D991D6150704035 +:101BC00080409040A40193010E94761AD2018D9129 +:101BD0009C91B801A9010E942F04009709F481C0CB +:101BE000012B21F4F20194A383A37C01D2019596E9 +:101BF000AD92BD92CD92DC929897A114B104C1042C +:101C0000D10421F494961C921E929397F201819133 +:101C10009191BF010E94AF0B882309F462C0A11407 +:101C2000B104C104D10429F4F20180819181B7018A +:101C30003FC00B811C812D813E816016710682069A +:101C40009306C0F1D2010D911C910115110591F17E +:101C5000B2E0EB16F10470F1C801B7010E947903FC +:101C60004C018FEF9FEF9A838983D801ED91FC910E +:101C70001197EE0CFF1CB70180E090E016962D91B5 +:101C80003D914D915C911997620F731F841F951FB1 +:101C90000480F581E02DAE014F5F5F4F22E030E020 +:101CA0000995882339F08114910421F0C801B40109 +:101CB0000E94B903F20185A596A5A7A5B0A9A8160B +:101CC000B906CA06DB0658F4D2019D96AD92BD92C4 +:101CD000CD92DC92D097D2961C921E92D19781E041 +:101CE00001C080E02A960FB6F894DEBF0FBECDBFCC +:101CF000CF91DF911F910F91FF90EF90DF90CF90E8 +:101D0000BF90AF909F908F907F906F905F904F901B +:101D1000089578010115110509F448CF67CFCF92D6 +:101D2000DF92EF92FF920F931F93CF93DF93EC011B +:101D30006B01009709F445C06115710509F441C0B4 +:101D40008DA59EA5AFA5B8A9413059F0413018F036 +:101D50004230B9F50CC0FB01E080F18002811381B3 +:101D600013C0FB01E080F1800281138109C0FB01F7 +:101D7000E080F180028113818DA19EA1AFA1B8A561 +:101D8000E80EF91E0A1F1B1F8DA19EA1AFA1B8A5C9 +:101D90008E159F05A007B10738F4CE01B801A70141 +:101DA0000E946E0D882369F0EDA6FEA60FA718AB62 +:101DB0001AAA19AAF601E082F1820283138381E054 +:101DC00001C080E0DF91CF911F910F91FF90EF90C4 +:101DD000DF90CF9008952F923F924F925F926F9233 +:101DE0007F928F929F92AF92BF92CF92DF92EF92AB +:101DF000FF920F931F93DF93CF9300D000D000D0BA +:101E0000CDB7DEB79E838D837A8369835C834B83F2 +:101E1000009709F441C1672B09F43EC1452B09F431 +:101E20003BC1DC019D968D909D90AD90BC90D0976C +:101E3000FC0185A196A1A7A1B0A588159905AA05C1 +:101E4000BB0508F429C10190F081E02D208831887C +:101E5000AD81BE81D1960D911C91D29701151105CE +:101E600009F055C093960D911C9194970115110599 +:101E7000A9F481149104A104B10409F00DC1CF01AA +:101E800060E070E041E050E00E942F048C01ED81A1 +:101E9000FE8194A383A3009709F4FEC0AD81BE81A7 +:101EA0009D96CD90DD90ED90FC90D097C114D1041B +:101EB000E104F10421F52BC0C418D508E608F708A1 +:101EC000ED81FE8180819181B8010E9479030097A4 +:101ED00011F08C0117C0C114D104E104F10409F020 +:101EE000DBC0AD81BE818D919C91B80141E050E095 +:101EF0000E942F04009709F4CFC08C0103C0210178 +:101F000066247724C414D504E604F704A8F60894DC +:101F100021083108822093200894211C311CAB80B9 +:101F2000BC80ED81FE81C080D180C114D104F9F064 +:101F300002301105E0F002501040B80180E090E05E +:101F40000E5F1F4FD60150962D913C91519740E066 +:101F500050E00E94301A9B01AC01F60182899389FE +:101F6000A489B589280F391F4A1F5B1F04C020E0D0 +:101F700030E040E050E07101E818F908AE14BF0409 +:101F800008F47501D601ED91FC91C401A0E0B0E028 +:101F9000280F391F4A1F5B1F0480F581E02DCA01FD +:101FA000B90149815A8197010995882309F448C0EC +:101FB000AE18BF08C701A0E0B0E0ED81FE8125A505 +:101FC00036A547A550A9280F391F4A1F5B1F25A713 +:101FD00036A747A750ABC701880D991D82159305F9 +:101FE00008F180819181B8010E947903009711F076 +:101FF0008C0116C0A114B10459F0ED81FE818081DD +:102000009181B80141E050E00E942F04009739F41B +:10201000AD81BE81D2961C921E92D19711C08C01C7 +:1020200088249924ED81FE8112AB01ABA114B10487 +:1020300039F089819A818E0D9F1D9A83898371CF92 +:10204000ED81FE8185A596A5A7A5B0A9E5A0F6A07E +:1020500007A110A5E816F9060A071B07C0F485A317 +:1020600096A3A7A3B0A781919191BF010E94AF0B46 +:10207000882369F4AD81BE819D96AD90BC909E97FA +:10208000AE18BF08FD01E5A6F6A607A710AB4B8169 +:102090005C814A195B0902C04FEF5FEFCA012696C7 +:1020A0000FB6F894DEBF0FBECDBFCF91DF911F9169 +:1020B0000F91FF90EF90DF90CF90BF90AF909F90E7 +:1020C0008F907F906F905F904F903F902F900895EA +:1020D000CF93DF93AC01DB01009789F161157105A6 +:1020E00071F190968C91909784FF29C08091750230 +:1020F00090917602892B19F4E5E7F2E008C080910F +:10210000A6029091A702892BD1F4E6EAF2E09F01A2 +:102110002E5F3F4FBD018BE2EB010990BE01E9014B +:1021200009929E018150C1F75183408391968D9110 +:102130009C91929796A785A710AA17A602C0E0E0E7 +:10214000F0E0CF01DF91CF910895CF93DF939C0111 +:10215000FB01009731F06115710519F080A184FF32 +:1021600023C020E030E0C901DF91CF910895C4E49D +:10217000D2E0DF018BE20D9009928150E1F73093BC +:1021800043022093420210926F021092700210924A +:1021900071021092720281A192A1909374028093B5 +:1021A000730222E432E0DFCF809142029091430239 +:1021B000892BB9F6DCCF6F927F928F929F92AF926C +:1021C000BF92CF92DF92EF92FF920F931F93CF9324 +:1021D000DF934C01EB017A01009709F467C06115A8 +:1021E000710509F463C08881882309F45FC0411533 +:1021F000510509F45BC08F3209F421968BE2F70197 +:1022000011928A95E9F780E1F70180A3370101C0B7 +:10221000E5018881882309F44BC0C401B7010E94FD +:1022200068106C01009709F441C0CE016FE270E0C4 +:102230000E949C00009739F0482F4C1B5C010894C9 +:10224000A11CB11C2AC0FE0101900020E9F73197C2 +:102250004E2F4C1B5E01A40EB11C1FC0F301019058 +:102260000020E9F73197E619F709E017F107B9F40B +:10227000CE01B301A8010E94B000009781F4F601DD +:10228000118210820C0F1D1FF8018081882381F0BC +:10229000F70180A184FDBCCF09C0042F10E0C60166 +:1022A000B7010E9419088823C9F612C080E001C056 +:1022B00081E0DF91CF911F910F91FF90EF90DF9020 +:1022C000CF90BF90AF909F908F907F906F90089528 +:1022D000F601118210829DCF8F929F92AF92BF9292 +:1022E000CF92DF92EF92FF920F93DF93CF93CDB710 +:1022F000DEB760970FB6F894DEBF0FBECDBF7C018E +:102300006B015A014901009709F493C061157105E9 +:1023100009F48FC004300CF08CC007FD1DC0602F85 +:10232000772767FD7095E4E0660F771FEA95E1F780 +:1023300062547E4F882777FD8095982FAE014F5FBE +:102340005F4F20E130E0F7010995882309F471C05F +:102350008D81882309F46DC08091DF02882309F004 +:1023600068C07CC08D818093DF022A8530E040E028 +:1023700050E0542F432F322F22278B8590E0A0E08E +:10238000B0E0DC0199278827282B392B4A2B5B2BBF +:10239000898590E0A0E0B0E0282B392B4A2B5B2BFD +:1023A0008C8590E0A0E0B0E0B82FAA279927882775 +:1023B000282B392B4A2B5B2B2093E0023093E10230 +:1023C0004093E2025093E3022E8530E040E050E07B +:1023D000542F432F322F22278F8590E0A0E0B0E0CA +:1023E000DC0199278827282B392B4A2B5B2B8D85DD +:1023F00090E0A0E0B0E0282B392B4A2B5B2B88899A +:1024000090E0A0E0B0E0B82FAA2799278827282BD2 +:10241000392B4A2B5B2B2093E4023093E502409347 +:10242000E6025093E70203C08FEF8093DF029F0123 +:1024300002C020E030E0C90160960FB6F894DEBF1C +:102440000FBECDBFCF91DF910F91FF90EF90DF9046 +:10245000CF90BF90AF909F908F900895E7EDF2E0FE +:1024600081E1DF011D928A95E9F7F092D802E092AE +:10247000D702D092DA02C092D902B092DC02A092C6 +:10248000DB029092DE028092DD0207FF6BCFCCCFA1 +:10249000282F8FEF8EBD0DB407FEFDCF8DB58F7742 +:1024A0008DBD8EB5822F80648EBD0DB407FEFDCF2D +:1024B0008DB58F778DBD872F9927AA27BB278EBD16 +:1024C0000DB407FEFDCF8DB58F778DBDCB01AA274B +:1024D000BB278EBD0DB407FEFDCF8DB58F778DBDAB +:1024E000BB27A72F962F852F8EBD0DB407FEFDCFDE +:1024F0008DB58F778DBD4EBD0DB407FEFDCF8DB56B +:102500008F778DBD222319F0283069F406C085E944 +:102510008EBD0DB407FEFDCF0BC087E88EBD0DB498 +:1025200007FEFDCF05C08FEF8EBD0DB407FEFDCFBA +:102530008DB58F778DBD1092E9021092E80220E0F0 +:1025400030E09FEF13C09EBD0DB407FEFDCF8DB5EB +:102550008F778DBD8EB5A9014F5F5F4F8F3F29F0FB +:102560003093E9022093E80208959A012A30310558 +:1025700050F33093E9022093E8028FEF08952F92F1 +:102580003F924F925F926F927F928F929F92AF9203 +:10259000BF92CF92DF92EF92FF920F931F93CF9350 +:1025A000DF934B015C012A01390133243A94222440 +:1025B0002394AFC0E401D170CE01A0E0B0E085016A +:1025C0007401E81AF90A0A0B1B0BC12CB2E0DB2ECE +:1025D000CC1ADD0A6C147D0408F463018091EA04CE +:1025E0009091EB04A091EC04B091ED04E816F9068B +:1025F0000A071B07F9F00E94A313882309F48FC070 +:10260000209729F480E0C81682E0D80658F4C80163 +:10261000B7014AEE52E020E032E00E94BD13882369 +:1026200009F47DC0E092EA04F092EB040093EC041C +:102630001093ED048AEE481682E0580671F09E0170 +:1026400026513D4FC901B201A6010E948C00109293 +:10265000EE046C147D0409F460C0289888E1B80188 +:10266000A7010E944812882311F0289A58C08EEFC3 +:102670008EBD0DB407FEFDCF8DB58F778DBD109249 +:10268000E9021092E802EAEEF2E020E030E00BC04E +:1026900080818EBD0DB407FEFDCF31968DB58F774D +:1026A0008DBD2F5F3F4F82E02030380788F3309395 +:1026B000E9022093E8023EBC0DB407FEFDCF8DB5C4 +:1026C0008F778DBD3EBC0DB407FEFDCF8DB58F77E6 +:1026D0008DBD3EBC0DB407FEFDCF8DB58F778DBD92 +:1026E0008EB58F3FB1F78EBD0DB407FEFDCF8DB512 +:1026F0008F778DBD8EB5289A4C0C5D1CC601A0E06D +:10270000B0E0880E991EAA1EBB1E6C187D08209290 +:10271000EE046114710409F04DCF81E001C080E046 +:10272000DF91CF911F910F91FF90EF90DF90CF90AD +:10273000BF90AF909F908F907F906F905F904F90E1 +:102740003F902F9008958091EE04882311F081E04E +:1027500008956091EA047091EB048091EC049091EB +:10276000ED044AEE52E020E032E00E94BF128823DE +:1027700019F081E08093EE0408953F924F925F92AA +:102780006F927F928F929F92AF92BF92CF92DF9281 +:10279000EF92FF920F931F93CF93DF933B014C0176 +:1027A0006A01290133243A9496C01FEFA12E11E04B +:1027B000B12EA620B720C501A0E0B0E084017301CE +:1027C000E81AF90A0A0B1B0BC0E0D2E0CA19DB09B0 +:1027D0004C165D0608F4E2018091EA049091EB0446 +:1027E000A091EC04B091ED04E816F9060A071B0766 +:1027F00009F45EC00E94A313882309F471C02898CD +:1028000081E1B801A7010E944812882319F0289A93 +:1028100080E066C03EBC0DB407FEFDCF8DB58F775E +:102820008DBD8EB58E3FB1F7EAEEF2E020E030E0EC +:102830000BC03EBC0DB407FEFDCF8DB58F778DBDAF +:102840008EB581932F5F3F4F82E02030380788F3A9 +:102850003093E9022093E802E092EA04F092EB045C +:102860000093EC041093ED04950126513D4FC601F1 +:10287000B901AE010E948C003EBC0DB407FEFDCF35 +:102880008DB58F778DBD8EB53EBC0DB407FEFDCFE7 +:102890008DB58F778DBD8EB5289A3EBC0DB407FEE1 +:1028A000FDCFCC0EDD1E8DB58F778DBD8EB50AC0E8 +:1028B000950126513D4FC601B901AE010E948C0021 +:1028C000CC0EDD1E4C1A5D0ACE01A0E0B0E0680E11 +:1028D000791E8A1E9B1E4114510409F066CF81E0C7 +:1028E000DF91CF911F910F91FF90EF90DF90CF90EC +:1028F000BF90AF909F908F907F906F905F904F9020 +:102900003F9008952F923F924F925F926F927F92E5 +:102910008F929F92AF92BF92CF92DF92EF92FF92EF +:102920000F931F93DF93CF9300D000D0CDB7DEB7C6 +:102930004B015C013A013C832B832701DA82C98277 +:1029400041155105A9F12115310591F10217130720 +:1029500078F1E114F10461F1190130942194310806 +:102960003394021B130B6901EE24FF24C501B4014B +:10297000A3012B813C810E94BD138823C9F0C301B0 +:10298000B501A40129813A81F2010995882391F0CA +:10299000020D131D8B819C81800F911F2B813C8127 +:1029A0008217930738F08C0C9D1CAE1CBF1CDECF29 +:1029B00080E001C081E00F900F900F900F90CF91B9 +:1029C000DF911F910F91FF90EF90DF90CF90BF901C +:1029D000AF909F908F907F906F905F904F903F90BF +:1029E0002F9008951F93CF93DF93239A259A209ACF +:1029F00024982C9A289A83E58CBD8DB58E7F8DBD49 +:102A00001092EF0420E030E09FEF0AC09EBD0DB4AD +:102A100007FEFDCF8DB58F778DBD8EB52F5F3F4FF4 +:102A20002A30310598F33093E9022093E802289880 +:102A30001092E9021092E80280E040E050E060E08D +:102A400070E00E944812182F813079F08091E802DE +:102A50009091E90221E08F3F920709F44DC0019661 +:102A60009093E9028093E802E7CF87E340E050E0EB +:102A700060E070E00E94481289E240E050E060E0CF +:102A800070E00E94481282FD02C01093EF04109281 +:102A9000E9021092E8028091EF04C82FD0E0C370E1 +:102AA000D070209749F087E340E050E060E070E0AC +:102AB0000E94481289E201C081E040E050E060E0FD +:102AC00070E00E94481280FF0EC08091E802909151 +:102AD000E9022FE78F3F920779F001969093E90280 +:102AE0008093E802DECF80E140E052E060E070E0F9 +:102AF0000E944812882319F0289A80E022C0289A60 +:102B00008CB58C7F8CBD8DB581608DBD8FEF9FEFB7 +:102B1000AFEFBFEF8093EA049093EB04A093EC0433 +:102B2000B093ED0481E08093EE0460E070E080E01B +:102B300090E04AEE52E020E032E00E94BD138111A5 +:102B400081E0DF91CF911F9108954F925F926F9234 +:102B50007F928F929F92AF92BF92CF92DF92EF922D +:102B6000FF920F931F93CF93DF935B016C014A0198 +:102B7000E901380127014115510571F101151105D0 +:102B800059F1FF242115310511F5FF24F3941FC0DD +:102B9000C401B601A5019201F30109958C010097CA +:102BA000E9F0FF2019F4C817D907B0F0C601B50144 +:102BB000A40198010E94BF12882371F0C801A0E00F +:102BC000B0E0A80EB91ECA1EDB1EC01BD10BFF2031 +:102BD000F9F62097E9F602C080E001C081E0DF91BC +:102BE000CF911F910F91FF90EF90DF90CF90BF900A +:102BF000AF909F908F907F906F905F904F900895CF +:102C00000F930E94F214882309F445C08DEB93E1E1 +:102C100062E874E14FEB52E125EA35E100E00E9401 +:102C20006C119093200580931F05892B89F48DEBFF +:102C300093E162E874E14FEB52E125EA35E10FEFF1 +:102C40000E946C119093200580931F05892B19F128 +:102C500080911F05909120050E94040990931E0504 +:102C600080931D050097B9F060E071E042EF54E0F9 +:102C70000E94DB1080911D0590911E0562EF74E0AB +:102C80000E9468109093F1048093F00420E0892B57 +:102C900019F021E001C020E0822F0F910895EF92FA +:102CA000FF920F931F93CF93DF93EB018A0179017A +:102CB000CB010E944C0608C0C701B8010E94A700C2 +:102CC000009711F481E006C0CE01B7010E941908F7 +:102CD000882391F7DF91CF911F910F91FF90EF9093 +:102CE0000895EF92FF920F931F93DF93CF93CDB789 +:102CF000DEB7AB970FB6F894DEBF0FBECDBF8C0129 +:102D00007E010894E11CF11C97010E944F16882354 +:102D100019F420E030E005C0C801B7010E94A510F9 +:102D20009C01C901AB960FB6F894DEBF0FBECDBFB4 +:102D3000CF91DF911F910F91FF90EF900895CF9267 +:102D4000DF92EF92FF920F931F93CF93DF930E9436 +:102D500000168091F0049091F10462E071E042EF7E +:102D600054E00E940A0D882391F480911D059091F2 +:102D70001E0562EF74E00E943D078091F00490917F +:102D8000F10462E071E042EF54E00E940A0D6091AC +:102D9000F0047091F10480911D0590911E0542E0B0 +:102DA00051E00E94711690932205809321050EE058 +:102DB00011E0F80101900020E9F73197E01BF10BD9 +:102DC000B801AF010E94EB0E0E94A3138091210570 +:102DD000909122050E94FC04C0E0D0E078018BE0D5 +:102DE000C82E81E0D82E6091F0047091F10480919A +:102DF0001D0590911E0542E051E00E9471168091E0 +:102E00002105909122056EE071E040E051E00E94C2 +:102E1000020580EB95E06EE071E00E94251A80913A +:102E200021059091220560E070E042E00E948F0E43 +:102E30008E010F5F1F4F00D000D000D0EDB7FEB75E +:102E40003196ADB7BEB71296FC92EE921197D3822F +:102E5000C282D583C4830E94D800F701019000206C +:102E6000E9F73197EE50F1408DB79EB706960FB651 +:102E7000F8949EBF0FBE8DBF8091210590912205D1 +:102E8000B701AF010E94EB0E0E94A31380912105B0 +:102E9000909122050E94FC0468EE73E080E090E0CF +:102EA0000E94C817E8019FCF80E060E00E942C18C4 +:102EB00081E061E00E942C1880EB95E040E855E24B +:102EC00060E070E00E94891888E061E00E942C18A0 +:102ED0008BE061E00E942C188CE060E00E942C18CE +:102EE0008DE061E00E942C188AE061E00E942C18BD +:102EF00008950E94F2170E9454170E949F16FDCF5A +:102F00001F920F920FB60F9211242F933F938F931E +:102F10009F93AF93BF938091270590912805A0912F +:102F20002905B0912A0530912B050196A11DB11DEF +:102F3000232F2D5F2D3720F02D570196A11DB11D98 +:102F400020932B058093270590932805A0932905AE +:102F5000B0932A058091230590912405A091250521 +:102F6000B09126050196A11DB11D80932305909374 +:102F70002405A0932505B0932605BF91AF919F919D +:102F80008F913F912F910F900FBE0F901F9018952A +:102F9000EF92FF920F931F937B018C018FB7F894F0 +:102FA00040912705509128056091290570912A05C7 +:102FB0008FBF2FB7F8948091270590912805A09195 +:102FC0002905B0912A052FBF841B950BA60BB70BC3 +:102FD000E816F9060A071B0760F71F910F91FF908B +:102FE000EF900895789484B5826084BD84B5816043 +:102FF00084BD85B5826085BD85B5816085BDEEE601 +:10300000F0E0808181608083E1E8F0E0808182608F +:103010008083808181608083E0E8F0E0808181604E +:103020008083E1EBF0E0808184608083E0EBF0E07E +:10303000808181608083EAE7F0E080818460808322 +:1030400080818260808380818160808380818068CC +:1030500080831092C1000895282F30E0C901865660 +:103060009F4FFC0194912A573F4FF9018491882387 +:1030700091F0E82FF0E0EE0FFF1FE859FF4FA59108 +:10308000B491662329F48C91909589238C930895AB +:103090008C91892B8C9308951F920F920FB60F92EB +:1030A00011242F933F934F935F936F937F938F934D +:1030B0009F93AF93BF93EF93FF934091C600E0912E +:1030C000AC05F091AD05CF01019660E870E00E947B +:1030D000631A9C018091AE059091AF0528173907BE +:1030E00039F0E45DFA4F40833093AD052093AC0591 +:1030F000FF91EF91BF91AF919F918F917F916F91D0 +:103100005F914F913F912F910F900FBE0F901F90A5 +:1031100018955F926F927F928F929F92AF92BF921B +:10312000CF92DF92EF92FF920F931F93CF93DF9393 +:10313000EC013A014B01413482E458078FE0680703 +:1031400080E078070CF07FC060E874E88EE190E0E2 +:10315000A40193010E94981A2150304040405040F1 +:10316000CA01B90122E030E040E050E00E94981A24 +:1031700059016A01A6019501209530954095509519 +:1031800094E0220F331F441F551F9A95D1F760E03A +:1031900074E284EF90E00E94981ACA01B9012FEFFF +:1031A00030E040E050E00E94301AA40193010E94F8 +:1031B000981AC90181509F4F181619061CF4522EF7 +:1031C0005A9403C055245394521A60E079E08DE379 +:1031D00090E0A40193010E94981A21503040404091 +:1031E0005040CA01B90122E030E040E050E00E94C6 +:1031F000981A209530954095509583E0220F331F03 +:10320000441F551F8A95D1F760E074E284EF90E087 +:103210000E94981ACA01B9012FEF30E040E050E057 +:103220000E94301AA40193010E94981AC90181508A +:103230009F4F181619061CF4822F815002C081E09E +:10324000821B851500F5E885F98581E090E00A8804 +:1032500002C0880F991F0A94E2F7808360E079E04A +:103260008DE390E0A40193010E94981A2150304010 +:1032700040405040CA01B90122E030E040E050E057 +:103280000E94981A04C0E885F98510829501EC81A6 +:10329000FD813083EE81FF812083EA85FB852081DB +:1032A00041E050E0CA010E8402C0880F991F0A94C1 +:1032B000E2F7282B2083EA85FB852081CA010F8451 +:1032C00002C0880F991F0A94E2F7282B2083EA8511 +:1032D000FB858081088802C0440F551F0A94E2F7DD +:1032E000842B8083DF91CF911F910F91FF90EF90FE +:1032F000DF90CF90BF90AF909F908F907F906F9016 +:103300005F900895FC01A085B18521898C9190E0A2 +:10331000022E02C0959587950A94E2F780FFF6CFBA +:103320000484F585E02D608308958FE192E0909309 +:10333000B1058093B0058CE295E09093B30580933E +:10334000B20585EC90E09093B5058093B40584ECCC +:1033500090E09093B7058093B60580EC90E0909351 +:10336000B9058093B80581EC90E09093BB058093FC +:10337000BA0586EC90E09093BD058093BC0584E08F +:103380008093BE0583E08093BF0587E08093C005EE +:1033900085E08093C10581E08093C20508950F9375 +:1033A0001F93CF93DF938C01EB0109C02196D801C5 +:1033B000ED91FC910190F081E02DC80109956881A3 +:1033C0006623A1F7DF91CF911F910F910895EF929E +:1033D000FF920F931F93CF93DF938C017B01EA0140 +:1033E0000CC0D7016D917D01D801ED91FC91019048 +:1033F000F081E02DC80109952197209791F7DF9181 +:10340000CF911F910F91FF90EF900895DC01ED9106 +:10341000FC910280F381E02D099508950F931F938D +:103420008C01DC01ED91FC910190F081E02D6DE0CB +:103430000995D801ED91FC910190F081E02DC80132 +:103440006AE009951F910F9108950F931F938C01C6 +:103450000E94061AC8010E940E1A1F910F9108952A +:10346000629FD001739FF001829FE00DF11D649F68 +:10347000E00DF11D929FF00D839FF00D749FF00DF4 +:10348000659FF00D9927729FB00DE11DF91F639F95 +:10349000B00DE11DF91FBD01CF0111240895AA1B34 +:1034A000BB1B51E107C0AA1FBB1FA617B70710F02F +:1034B000A61BB70B881F991F5A95A9F78095909561 +:1034C000BC01CD01089597FB092E07260AD077FD90 +:1034D00004D0E5DF06D000201AF4709561957F4F87 +:1034E0000895F6F7909581959F4F0895A1E21A2EC1 +:1034F000AA1BBB1BFD010DC0AA1FBB1FEE1FFF1F98 +:10350000A217B307E407F50720F0A21BB30BE40BE7 +:10351000F50B661F771F881F991F1A9469F760952E +:103520007095809590959B01AC01BD01CF010895E8 +:1035300097FB092E05260ED057FD04D0D7DF0AD001 +:10354000001C38F450954095309521953F4F4F4FD2 +:103550005F4F0895F6F790958095709561957F4F30 +:103560008F4F9F4F0895EE0FFF1F0590F491E02DB0 +:1035700009942F923F924F925F926F927F928F9217 +:103580009F92AF92BF92CF92DF92EF92FF920F93F2 +:103590001F93CF93DF93CDB7DEB7CA1BDB0B0FB6FC +:1035A000F894DEBF0FBECDBF09942A8839884888B9 +:1035B0005F846E847D848C849B84AA84B984C8844F +:1035C000DF80EE80FD800C811B81AA81B981CE0F46 +:1035D000D11D0FB6F894DEBF0FBECDBFED0108952B +:0435E000F894FFCF8D +:1035E4002F00546573742E74787400256400546538 +:1035F4007374696E670A0000000000000000000098 +:1036040000000000000000000000000000000000B6 +:1036140000000000000000000000000000000000A6 +:103624000000000000000000000000000000000096 +:103634000000000000000000000000000000000086 +:103644000000000000000000000000000000000076 +:103654000000000000000000000000000000000066 +:103664000000000000000000000000000000000056 +:103674000000000000000000000000000000000046 +:103684000000000000000000000000000000000036 +:103694000000000000000000000000000000000026 +:1036A4000000000000000000000000000000000016 +:1036B4000000000000000000000000000000000006 +:1036C40000000000000000000000000000000000F6 +:1036D40000000000000000000000000000000000E6 +:1036E40000000000000000000000000000000103D2 +:1036F4000507090E10121416181C1E000000008283 +:0637040019CF19E71900BE +:00000001FF diff --git a/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.o b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.o new file mode 100644 index 0000000..70062d7 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/FAT16_WriteExample.cpp.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/HardwareSerial.cpp.o b/FAT16/examples/FAT16_WriteExample/applet/HardwareSerial.cpp.o new file mode 100644 index 0000000..d5af083 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/HardwareSerial.cpp.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/Print.cpp.o b/FAT16/examples/FAT16_WriteExample/applet/Print.cpp.o new file mode 100644 index 0000000..c63f9bf Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/Print.cpp.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/WInterrupts.c.o b/FAT16/examples/FAT16_WriteExample/applet/WInterrupts.c.o new file mode 100644 index 0000000..52801e4 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/WInterrupts.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/WMath.cpp.o b/FAT16/examples/FAT16_WriteExample/applet/WMath.cpp.o new file mode 100644 index 0000000..d8792d0 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/WMath.cpp.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/core.a b/FAT16/examples/FAT16_WriteExample/applet/core.a new file mode 100644 index 0000000..41cf0b4 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/core.a differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/pins_arduino.c.o b/FAT16/examples/FAT16_WriteExample/applet/pins_arduino.c.o new file mode 100644 index 0000000..c1fa9f5 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/pins_arduino.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/wiring.c.o b/FAT16/examples/FAT16_WriteExample/applet/wiring.c.o new file mode 100644 index 0000000..394cbe8 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/wiring.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/wiring_analog.c.o b/FAT16/examples/FAT16_WriteExample/applet/wiring_analog.c.o new file mode 100644 index 0000000..619dd14 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/wiring_analog.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/wiring_digital.c.o b/FAT16/examples/FAT16_WriteExample/applet/wiring_digital.c.o new file mode 100644 index 0000000..02809ce Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/wiring_digital.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/wiring_pulse.c.o b/FAT16/examples/FAT16_WriteExample/applet/wiring_pulse.c.o new file mode 100644 index 0000000..354b096 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/wiring_pulse.c.o differ diff --git a/FAT16/examples/FAT16_WriteExample/applet/wiring_shift.c.o b/FAT16/examples/FAT16_WriteExample/applet/wiring_shift.c.o new file mode 100644 index 0000000..2f318d0 Binary files /dev/null and b/FAT16/examples/FAT16_WriteExample/applet/wiring_shift.c.o differ diff --git a/FAT16/fat.c b/FAT16/fat.c new file mode 100644 index 0000000..c3e2d39 --- /dev/null +++ b/FAT16/fat.c @@ -0,0 +1,2326 @@ + +/* + * Copyright (c) 2006-2009 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 + +#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; +}; + +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; + uint8_t finished; +}; + +struct fat_usage_count_callback_arg +{ + cluster_t cluster_count; + uintptr_t buffer_size; +}; + +uint16_t i=0; + +#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); +static uint8_t fat_interpret_dir_entry(struct fat_dir_entry_struct* dir_entry, const uint8_t* raw_entry); + +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(const struct fat_fs_struct* fs, cluster_t cluster_num, cluster_t count); +static uint8_t fat_free_clusters(const struct fat_fs_struct* fs, cluster_t cluster_num); +static uint8_t fat_terminate_clusters(const 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(const 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[inout] 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 = ltoh16(*((uint16_t*) &buffer[0x00])); + uint16_t reserved_sectors = ltoh16(*((uint16_t*) &buffer[0x03])); + uint8_t sectors_per_cluster = buffer[0x02]; + uint8_t fat_copies = buffer[0x05]; + uint16_t max_root_entries = ltoh16(*((uint16_t*) &buffer[0x06])); + uint16_t sector_count_16 = ltoh16(*((uint16_t*) &buffer[0x08])); + uint16_t sectors_per_fat = ltoh16(*((uint16_t*) &buffer[0x0b])); + uint32_t sector_count = ltoh32(*((uint32_t*) &buffer[0x15])); +#if FAT_FAT32_SUPPORT + uint32_t sectors_per_fat32 = ltoh32(*((uint32_t*) &buffer[0x19])); + uint32_t cluster_root_dir = ltoh32(*((uint32_t*) &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 + 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 + 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 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(const 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_next = 0; + cluster_t cluster_max; + 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_max = fs->header.fat_size / sizeof(fat_entry32); + else +#endif + cluster_max = fs->header.fat_size / sizeof(fat_entry16); + + cluster_t cluster_new=0; + for(cluster_new = 2; cluster_new < cluster_max; ++cluster_new) + { +#if FAT_FAT32_SUPPORT + if(is_fat32) + { + if(!device_read(fat_offset + cluster_new * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32))) + return 0; + } + else +#endif + { + if(!device_read(fat_offset + cluster_new * 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; + + /* allocate cluster */ + if(cluster_next == 0) + fat_entry32 = HTOL32(FAT32_CLUSTER_LAST_MAX); + else + fat_entry32 = htol32(cluster_next); + + if(!device_write(fat_offset + cluster_new * 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; + + /* 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 + cluster_new * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16))) + break; + } + + cluster_next = cluster_new; + if(--count_left == 0) + break; + } + + 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 + 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 + 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 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(const 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 + 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; + + /* free cluster */ + fat_entry = HTOL32(FAT32_CLUSTER_FREE); + fs->partition->device_write(fat_offset + 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 + 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 + 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 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(const 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 + 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 + 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 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 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 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; +} + +/** + * \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 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 -1; + } + 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 && pos == 0) + /* 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 -1; + + 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) + { + uint32_t size_old = fd->dir_entry.file_size; + + /* update file size */ + fd->dir_entry.file_size = fd->pos; + /* 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; + } + } + + return buffer_len - buffer_left; +} + +/** + * \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. + * + * \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 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; + + /* reset directory entry */ + memset(dir_entry, 0, sizeof(*dir_entry)); + + /* reset callback arguments */ + memset(&arg, 0, sizeof(arg)); + 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; + uint32_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 */ + cluster_offset = 0; + + /* get number of next cluster */ + if(!(cluster_num = fat_get_next_cluster(fs, cluster_num))) + { + /* directory entry not found, reset directory handle */ + cluster_num = dd->dir_entry.cluster; + break; + } + } + } + + dd->entry_cluster = cluster_num; + dd->entry_offset = cluster_offset; + + return dir_entry->long_name[0] != '\0' ? 1 : 0; +} + +/** + * \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. + */ +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]) + return 1; + + if(!dir_entry->entry_offset) + dir_entry->entry_offset = offset; + + switch(fat_interpret_dir_entry(dir_entry, buffer)) + { + case 0: /* failure */ + { + return 0; + } + case 1: /* buffer successfully parsed, continue */ + { + return 1; + } + case 2: /* directory entry complete, finish */ + { + arg->finished = 1; + return 0; + } + } + + return 0; +} + +/** + * \ingroup fat_fs + * Interprets a raw directory entry and puts the contained + * information into the directory entry. + * + * 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,out] dir_entry The directory entry to fill. + * \param[in] raw_entry A pointer to 32 bytes of raw data. + * \returns 0 on failure, 1 on success and 2 if the + * directory entry is complete. + */ +uint8_t fat_interpret_dir_entry(struct fat_dir_entry_struct* dir_entry, const uint8_t* raw_entry) +{ + if(!dir_entry || !raw_entry || !raw_entry[0]) + return 0; + + char* long_name = dir_entry->long_name; + if(raw_entry[11] == 0x0f) + { + /* Lfn supports unicode, but we do not, for now. + * So we assume pure ascii and read only every + * second byte. + */ + uint16_t char_offset = ((raw_entry[0] & 0x3f) - 1) * 13; + const uint8_t char_mapping[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; + for(i = 0; i <= 12 && char_offset + i < sizeof(dir_entry->long_name) - 1; ++i) + long_name[char_offset + i] = raw_entry[char_mapping[i]]; + + return 1; + } + else + { + /* if we do not have a long name, take the short one */ + if(long_name[0] == '\0') + { + uint8_t i; + for(i = 0; i < 8; ++i) + { + if(raw_entry[i] == ' ') + break; + long_name[i] = raw_entry[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((raw_entry[12] & 0x08) && raw_entry[i] >= 'A' && raw_entry[i] <= 'Z') + long_name[i] += 'a' - 'A'; + } + if(long_name[0] == 0x05) + long_name[0] = (char) FAT_DIRENTRY_DELETED; + + if(raw_entry[8] != ' ') + { + long_name[i++] = '.'; + + uint8_t j = 8; + for(; j < 11; ++j) + { + if(raw_entry[j] == ' ') + break; + long_name[i] = raw_entry[j]; + + /* See above for the lowercase 8.3 name handling of + * Windows NT and later. + */ + if((raw_entry[12] & 0x10) && raw_entry[j] >= 'A' && raw_entry[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 = raw_entry[11]; + dir_entry->cluster = ltoh16(*((uint16_t*) &raw_entry[26])); +#if FAT_FAT32_SUPPORT + dir_entry->cluster |= ((cluster_t) ltoh16(*((uint16_t*) &raw_entry[20]))) << 16; +#endif + dir_entry->file_size = ltoh32(*((uint32_t*) &raw_entry[28])); + +#if FAT_DATETIME_SUPPORT + dir_entry->modification_time = ltoh16(*((uint16_t*) &raw_entry[22])); + dir_entry->modification_date = ltoh16(*((uint16_t*) &raw_entry[24])); +#endif + + return 2; + } +} + +#if 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(const 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 */ + uint8_t free_dir_entries_needed = (strlen(dir_entry->long_name) + 12) / 13 + 1; + uint8_t free_dir_entries_found = 0; + 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; + free_dir_entries_found = 0; + } + + /* 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 */ + ++free_dir_entries_found; + if(free_dir_entries_found >= free_dir_entries_needed) + break; + + offset += 32; + } + else + { + offset += 32; + dir_entry_offset = offset; + free_dir_entries_found = 0; + } + } + + return dir_entry_offset; +} +#endif + +#if 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); + uint8_t lfn_entry_count = (name_len + 12) / 13; + 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) + name_ext_len = 3; + + memcpy(&buffer[8], name_ext, name_ext_len); + } + + if(name_len <= 8) + { + memcpy(buffer, name, name_len); + + /* 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; + } + else + { + 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); + } + 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 + *((uint16_t*) &buffer[0x16]) = htol16(dir_entry->modification_time); + *((uint16_t*) &buffer[0x18]) = htol16(dir_entry->modification_date); +#endif +#if FAT_FAT32_SUPPORT + *((uint16_t*) &buffer[0x14]) = htol16((uint16_t) (dir_entry->cluster >> 16)); +#endif + *((uint16_t*) &buffer[0x1a]) = htol16(dir_entry->cluster); + *((uint32_t*) &buffer[0x1c]) = htol32(dir_entry->file_size); + + /* write to disk */ + if(!device_write(offset + (uint16_t) lfn_entry_count * 32, buffer, sizeof(buffer))) + return 0; + + /* calculate checksum of 8.3 name */ + uint8_t checksum = buffer[0]; + for(i = 1; i < 11; ++i) + checksum = ((checksum >> 1) | (checksum << 7)) + buffer[i]; + + /* write lfn entries */ + uint8_t lfn_entry=0; + for(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); + } + + return 1; +} +#endif + +/** + * \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 file. + * \returns 0 on failure, 1 on success. + * \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 0; + } + } + + 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; +} + +/** + * \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; + + 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; + } + + /* 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)); +} + +/** + * \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; +} + +/** + * \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 + +#if 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 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 (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 (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; + + uintptr_t j=0; + for(j = 0; i < buffer_size; j += 2, buffer += 2) + { + uint16_t cluster = *((uint16_t*) &buffer[0]); + if(cluster == HTOL16(FAT16_CLUSTER_FREE)) + ++(count_arg->cluster_count); + } + + return 1; +} + +#if 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(i = 0; i < buffer_size; i += 4, buffer += 4) + { + uint32_t cluster = *((uint32_t*) &buffer[0]); + if(cluster == HTOL32(FAT32_CLUSTER_FREE)) + ++(count_arg->cluster_count); + } + + return 1; +} +#endif + diff --git a/FAT16/fat.h b/FAT16/fat.h new file mode 100644 index 0000000..846dccc --- /dev/null +++ b/FAT16/fat.h @@ -0,0 +1,129 @@ + +/* + * Copyright (c) 2006-2009 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_create_dir(struct fat_dir_struct* parent, const char* dir, struct fat_dir_entry_struct* dir_entry); +#define fat_delete_dir fat_delete_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/FAT16/fat_config.h b/FAT16/fat_config.h new file mode 100644 index 0000000..cb053a5 --- /dev/null +++ b/FAT16/fat_config.h @@ -0,0 +1,111 @@ + +/* + * Copyright (c) 2006-2009 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 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 +//#define FAT_FAT32_SUPPORT 1 + +/** + * \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/FAT16/partition.c b/FAT16/partition.c new file mode 100644 index 0000000..9f1d5b6 --- /dev/null +++ b/FAT16/partition.c @@ -0,0 +1,160 @@ + +/* + * Copyright (c) 2006-2009 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 "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 = ((uint32_t) buffer[8]) | + ((uint32_t) buffer[9] << 8) | + ((uint32_t) buffer[10] << 16) | + ((uint32_t) buffer[11] << 24); + new_partition->length = ((uint32_t) buffer[12]) | + ((uint32_t) buffer[13] << 8) | + ((uint32_t) buffer[14] << 16) | + ((uint32_t) buffer[15] << 24); + } + 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/FAT16/partition.h b/FAT16/partition.h new file mode 100644 index 0000000..433f1fe --- /dev/null +++ b/FAT16/partition.h @@ -0,0 +1,212 @@ + +/* + * Copyright (c) 2006-2009 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/FAT16/partition_config.h b/FAT16/partition_config.h new file mode 100644 index 0000000..0040378 --- /dev/null +++ b/FAT16/partition_config.h @@ -0,0 +1,44 @@ + +/* + * Copyright (c) 2006-2009 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/FAT16/sd-reader_config.h b/FAT16/sd-reader_config.h new file mode 100644 index 0000000..0819f05 --- /dev/null +++ b/FAT16/sd-reader_config.h @@ -0,0 +1,53 @@ + +/* + * Copyright (c) 2006-2009 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/FAT16/sd_raw.c b/FAT16/sd_raw.c new file mode 100644 index 0000000..10cddbb --- /dev/null +++ b/FAT16/sd_raw.c @@ -0,0 +1,1011 @@ + +/* + * Copyright (c) 2006-2009 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" +//ADDED! +#include +#include +#include +#include + +#include + +/** + * \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(void); +static uint8_t sd_raw_send_command(uint8_t command, uint32_t arg); + +uint16_t i2 = 0; + +/** + * \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(); + PORTB |= (1<> 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(i2 = 0; i2 < 10; ++i2) + { + 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(i2 = 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(i2 = 0; i2 < 512; ++i2) + *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(i2 = 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(i2 = 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 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(i2 = 0; i2 < 512; ++i2) + 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 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 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(i2 = 0; i2 < 18; ++i2) + { + uint8_t b = sd_raw_rec_byte(); + + switch(i2) + { + case 0: + info->manufacturer = b; + break; + case 1: + case 2: + info->oem[i2 - 1] = b; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + info->product[i2 - 3] = b; + break; + case 8: + info->revision = b; + break; + case 9: + case 10: + case 11: + case 12: + info->serial |= (uint32_t) b << ((12 - i2) * 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 + if(sd_raw_send_command(CMD_SEND_CSD, 0)) + { + unselect_card(); + return 0; + } + while(sd_raw_rec_byte() != 0xfe); + for(i2 = 0; i2 < 18; ++i2) + { + uint8_t b = sd_raw_rec_byte(); + + if(i2 == 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(sd_raw_card_type & (1 << SD_RAW_SPEC_2)) + { + switch(i) + { + case 7: + b &= 0x3f; + case 8: + case 9: + csd_c_size <<= 8; + csd_c_size |= b; + break; + } + if(i2 == 9) + { + ++csd_c_size; + info->capacity = (offset_t) csd_c_size * 512 * 1024; + } + } + else +#endif + { + switch(i2) + { + 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/FAT16/sd_raw.h b/FAT16/sd_raw.h new file mode 100644 index 0000000..549c080 --- /dev/null +++ b/FAT16/sd_raw.h @@ -0,0 +1,148 @@ + +/* + * Copyright (c) 2006-2009 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(void); +uint8_t sd_raw_available(void); +uint8_t sd_raw_locked(void); + +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(void); + +uint8_t sd_raw_get_info(struct sd_raw_info* info); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/FAT16/sd_raw_config.h b/FAT16/sd_raw_config.h new file mode 100644 index 0000000..afe6969 --- /dev/null +++ b/FAT16/sd_raw_config.h @@ -0,0 +1,146 @@ + +/* + * Copyright (c) 2006-2009 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 + +/** + * @} + */ + +/* defines for customisation of sd/mmc port access */ +/*#if defined(__AVR_ATmega8__) || \ + defined(__AVR_ATmega48__) || \ + defined(__AVR_ATmega88__) || \ + defined(__AVR_ATmega168__) || \ + defined(__AVR_ATmega328__) || \ + defined(__AVR_atmega328p__) +*/ + #define configure_pin_mosi() DDRB |= (1 << DDB3) + #define configure_pin_sck() DDRB |= (1 << DDB5) + #define configure_pin_ss() DDRB |= (1 << DDB0) + #define configure_pin_miso() DDRB &= ~(1 << DDB4) + + #define select_card() PORTB &= ~(1 << PINB0) + #define unselect_card() PORTB |= (1 << PINB0) +/* +#elif defined(__AVR_ATmega16__) || \ + defined(__AVR_ATmega32__) + #define configure_pin_mosi() DDRB |= (1 << DDB5) + #define configure_pin_sck() DDRB |= (1 << DDB7) + #define configure_pin_ss() DDRB |= (1 << DDB4) + #define configure_pin_miso() DDRB &= ~(1 << DDB6) + + #define select_card() PORTB &= ~(1 << PB4) + #define unselect_card() PORTB |= (1 << PB4) +#elif defined(__AVR_ATmega64__) || \ + defined(__AVR_ATmega128__) || \ + defined(__AVR_ATmega169__) + #define configure_pin_mosi() DDRB |= (1 << DDB2) + #define configure_pin_sck() DDRB |= (1 << DDB1) + #define configure_pin_ss() DDRB |= (1 << DDB0) + #define configure_pin_miso() DDRB &= ~(1 << DDB3) + + #define select_card() PORTB &= ~(1 << PB0) + #define unselect_card() PORTB |= (1 << PB0) +#else + #error "no sd/mmc pin mapping available!" +#endif +*/ +/* +#define configure_pin_available() DDRC &= ~(1 << DDC4) +#define configure_pin_locked() DDRC &= ~(1 << DDC5) + +#define get_pin_available() ((PINC >> PINC4) & 0x01) +#define get_pin_locked() ((PINC >> PINC5) & 0x01) +*/ +#define configure_pin_available() //NOTHING +#define configure_pin_locked() //Nothing + +#define get_pin_available() 0 +#define get_pin_locked() 1 + +#if SD_RAW_SDHC + typedef uint64_t offset_t; +#else + typedef uint32_t offset_t; +#endif + +/* configuration checks */ +#if SD_RAW_WRITE_SUPPORT +#undef SD_RAW_SAVE_RAM +#define SD_RAW_SAVE_RAM 0 +#else +#undef SD_RAW_WRITE_BUFFERING +#define SD_RAW_WRITE_BUFFERING 0 +#endif + +#ifdef __cplusplus +} +#endif + +#endif +