move main.c (from fat driver) to sd_main.c
authorOlivier Matz <zer0@droids-corp.org>
Thu, 26 Jun 2014 18:25:02 +0000 (20:25 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 26 Jun 2014 18:25:02 +0000 (20:25 +0200)
main.c [deleted file]
sd_main.c [new file with mode: 0644]

diff --git a/main.c b/main.c
deleted file mode 100644 (file)
index 47298d4..0000000
--- a/main.c
+++ /dev/null
@@ -1,666 +0,0 @@
-
-/*
- * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
- *
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <string.h>
-#include <avr/pgmspace.h>
-#include <avr/sleep.h>
-#include "fat.h"
-#include "fat_config.h"
-#include "partition.h"
-#include "sd_raw.h"
-#include "sd_raw_config.h"
-#include "uart.h"
-
-#define DEBUG 1
-
-/**
- * \mainpage MMC/SD/SDHC card library
- *
- * This project provides a general purpose library which implements read and write
- * support for MMC, SD and SDHC memory cards.
- *
- * It includes
- * - low-level \link sd_raw MMC, SD and SDHC read/write routines \endlink
- * - \link partition partition table support \endlink
- * - a simple \link fat FAT16/FAT32 read/write implementation \endlink
- *
- * \section circuit The circuit
- * The circuit which was mainly used during development consists of an Atmel AVR
- * microcontroller with some passive components. It is quite simple and provides
- * an easy test environment. The circuit which can be downloaded on the
- * <a href="http://www.roland-riegel.de/sd-reader/">project homepage</a> has been
- * improved with regard to operation stability.
- *
- * I used different microcontrollers during development, the ATmega8 with 8kBytes
- * of flash, and its pin-compatible alternative, the ATmega168 with 16kBytes flash.
- * The first one is the one I started with, but when I implemented FAT16 write
- * support, I ran out of flash space and switched to the ATmega168. For FAT32, an
- * ATmega328 is required.
- * 
- * The circuit board is a self-made and self-soldered board consisting of a single
- * copper layer and standard DIL components, except of the MMC/SD card connector.
- *
- * The connector is soldered to the bottom side of the board. It has a simple
- * eject button which, when a card is inserted, needs some space beyond the connector
- * itself. As an additional feature the connector has two electrical switches
- * to detect wether a card is inserted and wether this card is write-protected.
- * 
- * \section pictures Pictures
- * \image html pic01.jpg "The circuit board used to implement and test this application."
- * \image html pic02.jpg "The MMC/SD card connector on the soldering side of the circuit board."
- *
- * \section software The software
- * The software is written in C (ISO C99). It might not be the smallest or
- * the fastest one, but I think it is quite flexible. See the project's
- * <a href="http://www.roland-riegel.de/sd-reader/benchmarks/">benchmark page</a> to get an
- * idea of the possible data rates.
- *
- * I implemented an example application providing a simple command prompt which is accessible
- * via the UART at 9600 Baud. With commands similiar to the Unix shell you can browse different
- * directories, read and write files, create new ones and delete them again. Not all commands are
- * available in all software configurations.
- * - <tt>cat \<file\></tt>\n
- *   Writes a hexdump of \<file\> to the terminal.
- * - <tt>cd \<directory\></tt>\n
- *   Changes current working directory to \<directory\>.
- * - <tt>disk</tt>\n
- *   Shows card manufacturer, status, filesystem capacity and free storage space.
- * - <tt>init</tt>\n
- *   Reinitializes and reopens the memory card.
- * - <tt>ls</tt>\n
- *   Shows the content of the current directory.
- * - <tt>mkdir \<directory\></tt>\n
- *   Creates a directory called \<directory\>.
- * - <tt>mv \<file\> \<file_new\></tt>\n
- *   Renames \<file\> to \<file_new\>. 
- * - <tt>rm \<file\></tt>\n
- *   Deletes \<file\>.
- * - <tt>sync</tt>\n
- *   Ensures all buffered data is written to the card.
- * - <tt>touch \<file\></tt>\n
- *   Creates \<file\>.
- * - <tt>write \<file\> \<offset\></tt>\n
- *   Writes text to \<file\>, starting from \<offset\>. The text is read
- *   from the UART, line by line. Finish with an empty line.
- *
- * \htmlonly
- * <p>
- * The following table shows some typical code sizes in bytes, using the 20090330 release with a
- * buffered read-write MMC/SD configuration, FAT16 and static memory allocation:
- * </p>
- *
- * <table border="1" cellpadding="2">
- *     <tr>
- *         <th>layer</th>
- *         <th>code size</th>
- *         <th>static RAM usage</th>
- *     </tr>
- *     <tr>
- *         <td>MMC/SD</td>
- *         <td align="right">2410</td>
- *         <td align="right">518</td>
- *     </tr>
- *     <tr>
- *         <td>Partition</td>
- *         <td align="right">456</td>
- *         <td align="right">17</td>
- *     </tr>
- *     <tr>
- *         <td>FAT16</td>
- *         <td align="right">7928</td>
- *         <td align="right">188</td>
- *     </tr>
- * </table>
- *
- * <p>
- * The static RAM is mostly used for buffering memory card access, which
- * improves performance and reduces implementation complexity.
- * </p>
- * 
- * <p>
- * Please note that the numbers above do not include the C library functions
- * used, e.g. some string functions. These will raise the numbers somewhat
- * if they are not already used in other program parts.
- * </p>
- * 
- * <p>
- * When opening a partition, filesystem, file or directory, a little amount
- * of RAM is used, as listed in the following table. Depending on the library
- * configuration, the memory is either allocated statically or dynamically.
- * </p>
- *
- * <table border="1" cellpadding="2">
- *     <tr>
- *         <th>descriptor</th>
- *         <th>dynamic/static RAM</th>
- *     </tr>
- *     <tr>
- *         <td>partition</td>
- *         <td align="right">17</td>
- *     </tr>
- *     <tr>
- *         <td>filesystem</td>
- *         <td align="right">26</td>
- *     </tr>
- *     <tr>
- *         <td>file</td>
- *         <td align="right">53</td>
- *     </tr>
- *     <tr>
- *         <td>directory</td>
- *         <td align="right">49</td>
- *     </tr>
- * </table>
- * 
- * \endhtmlonly
- *
- * \section adaptation Adapting the software to your needs
- * The only hardware dependent part is the communication layer talking to the
- * memory card. The other parts like partition table and FAT support are
- * completely independent, you could use them even for managing Compact Flash
- * cards or standard ATAPI hard disks.
- *
- * By changing the MCU* variables in the Makefile, you can use other Atmel
- * microcontrollers or different clock speeds. You might also want to change
- * the configuration defines in the files fat_config.h, partition_config.h,
- * sd_raw_config.h and sd-reader_config.h. For example, you could disable
- * write support completely if you only need read support.
- *
- * For further information, visit the project's
- * <a href="http://www.roland-riegel.de/sd-reader/faq/">FAQ page</a>.
- * 
- * \section bugs Bugs or comments?
- * If you have comments or found a bug in the software - there might be some
- * of them - you may contact me per mail at feedback@roland-riegel.de.
- *
- * \section acknowledgements Acknowledgements
- * Thanks go to Ulrich Radig, who explained on his homepage how to interface
- * MMC cards to the Atmel microcontroller (http://www.ulrichradig.de/).
- * I adapted his work for my circuit.
- * 
- * \section copyright Copyright 2006-2012 by Roland Riegel
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation (http://www.gnu.org/copyleft/gpl.html).
- * At your option, you can alternatively redistribute and/or modify the following
- * files under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation (http://www.gnu.org/copyleft/lgpl.html):
- * - byteordering.c
- * - byteordering.h
- * - fat.c
- * - fat.h
- * - fat_config.h
- * - partition.c
- * - partition.h
- * - partition_config.h
- * - sd_raw.c
- * - sd_raw.h
- * - sd_raw_config.h
- * - sd-reader_config.h
- */
-
-static uint8_t read_line(char* buffer, uint8_t buffer_length);
-static uint32_t strtolong(const char* str);
-static 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);
-static struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name); 
-static uint8_t print_disk_info(const struct fat_fs_struct* fs);
-
-int main()
-{
-    /* we will just use ordinary idle mode */
-    set_sleep_mode(SLEEP_MODE_IDLE);
-
-    /* setup uart */
-    uart_init();
-
-    while(1)
-    {
-        /* setup sd card slot */
-        if(!sd_raw_init())
-        {
-#if DEBUG
-            uart_puts_p(PSTR("MMC/SD initialization failed\n"));
-#endif
-            continue;
-        }
-
-        /* open first partition */
-        struct partition_struct* 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)
-            {
-#if DEBUG
-                uart_puts_p(PSTR("opening partition failed\n"));
-#endif
-                continue;
-            }
-        }
-
-        /* open file system */
-        struct fat_fs_struct* fs = fat_open(partition);
-        if(!fs)
-        {
-#if DEBUG
-            uart_puts_p(PSTR("opening filesystem failed\n"));
-#endif
-            continue;
-        }
-
-        /* open root directory */
-        struct fat_dir_entry_struct directory;
-        fat_get_dir_entry_of_path(fs, "/", &directory);
-
-        struct fat_dir_struct* dd = fat_open_dir(fs, &directory);
-        if(!dd)
-        {
-#if DEBUG
-            uart_puts_p(PSTR("opening root directory failed\n"));
-#endif
-            continue;
-        }
-        
-        /* print some card information as a boot message */
-        print_disk_info(fs);
-
-        /* provide a simple shell */
-        char buffer[24];
-        while(1)
-        {
-            /* print prompt */
-            uart_putc('>');
-            uart_putc(' ');
-
-            /* read command */
-            char* command = buffer;
-            if(read_line(command, sizeof(buffer)) < 1)
-                continue;
-
-            /* execute command */
-            if(strcmp_P(command, PSTR("init")) == 0)
-            {
-                break;
-            }
-            else if(strncmp_P(command, PSTR("cd "), 3) == 0)
-            {
-                command += 3;
-                if(command[0] == '\0')
-                    continue;
-
-                /* change directory */
-                struct fat_dir_entry_struct subdir_entry;
-                if(find_file_in_dir(fs, dd, command, &subdir_entry))
-                {
-                    struct fat_dir_struct* dd_new = fat_open_dir(fs, &subdir_entry);
-                    if(dd_new)
-                    {
-                        fat_close_dir(dd);
-                        dd = dd_new;
-                        continue;
-                    }
-                }
-
-                uart_puts_p(PSTR("directory not found: "));
-                uart_puts(command);
-                uart_putc('\n');
-            }
-            else if(strcmp_P(command, PSTR("ls")) == 0)
-            {
-                /* print directory listing */
-                struct fat_dir_entry_struct dir_entry;
-                while(fat_read_dir(dd, &dir_entry))
-                {
-                    uint8_t spaces = sizeof(dir_entry.long_name) - strlen(dir_entry.long_name) + 4;
-
-                    uart_puts(dir_entry.long_name);
-                    uart_putc(dir_entry.attributes & FAT_ATTRIB_DIR ? '/' : ' ');
-                    while(spaces--)
-                        uart_putc(' ');
-                    uart_putdw_dec(dir_entry.file_size);
-                    uart_putc('\n');
-                }
-            }
-            else if(strncmp_P(command, PSTR("cat "), 4) == 0)
-            {
-                command += 4;
-                if(command[0] == '\0')
-                    continue;
-                
-                /* search file in current directory and open it */
-                struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
-                if(!fd)
-                {
-                    uart_puts_p(PSTR("error opening "));
-                    uart_puts(command);
-                    uart_putc('\n');
-                    continue;
-                }
-
-                /* print file contents */
-                uint8_t buffer[8];
-                uint32_t offset = 0;
-                intptr_t count;
-                while((count = fat_read_file(fd, buffer, sizeof(buffer))) > 0)
-                {
-                    uart_putdw_hex(offset);
-                    uart_putc(':');
-                    for(intptr_t i = 0; i < count; ++i)
-                    {
-                        uart_putc(' ');
-                        uart_putc_hex(buffer[i]);
-                    }
-                    uart_putc('\n');
-                    offset += 8;
-                }
-
-                fat_close_file(fd);
-            }
-            else if(strcmp_P(command, PSTR("disk")) == 0)
-            {
-                if(!print_disk_info(fs))
-                    uart_puts_p(PSTR("error reading disk info\n"));
-            }
-#if FAT_WRITE_SUPPORT
-            else if(strncmp_P(command, PSTR("rm "), 3) == 0)
-            {
-                command += 3;
-                if(command[0] == '\0')
-                    continue;
-                
-                struct fat_dir_entry_struct file_entry;
-                if(find_file_in_dir(fs, dd, command, &file_entry))
-                {
-                    if(fat_delete_file(fs, &file_entry))
-                        continue;
-                }
-
-                uart_puts_p(PSTR("error deleting file: "));
-                uart_puts(command);
-                uart_putc('\n');
-            }
-            else if(strncmp_P(command, PSTR("touch "), 6) == 0)
-            {
-                command += 6;
-                if(command[0] == '\0')
-                    continue;
-
-                struct fat_dir_entry_struct file_entry;
-                if(!fat_create_file(dd, command, &file_entry))
-                {
-                    uart_puts_p(PSTR("error creating file: "));
-                    uart_puts(command);
-                    uart_putc('\n');
-                }
-            }
-            else if(strncmp_P(command, PSTR("mv "), 3) == 0)
-            {
-                command += 3;
-                if(command[0] == '\0')
-                    continue;
-
-                char* target = command;
-                while(*target != ' ' && *target != '\0')
-                    ++target;
-
-                if(*target == ' ')
-                    *target++ = '\0';
-                else
-                    continue;
-
-                struct fat_dir_entry_struct file_entry;
-                if(find_file_in_dir(fs, dd, command, &file_entry))
-                {
-                    if(fat_move_file(fs, &file_entry, dd, target))
-                        continue;
-                }
-
-                uart_puts_p(PSTR("error moving file: "));
-                uart_puts(command);
-                uart_putc('\n');
-            }
-            else if(strncmp_P(command, PSTR("write "), 6) == 0)
-            {
-                command += 6;
-                if(command[0] == '\0')
-                    continue;
-
-                char* offset_value = command;
-                while(*offset_value != ' ' && *offset_value != '\0')
-                    ++offset_value;
-
-                if(*offset_value == ' ')
-                    *offset_value++ = '\0';
-                else
-                    continue;
-
-                /* search file in current directory and open it */
-                struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
-                if(!fd)
-                {
-                    uart_puts_p(PSTR("error opening "));
-                    uart_puts(command);
-                    uart_putc('\n');
-                    continue;
-                }
-
-                int32_t offset = strtolong(offset_value);
-                if(!fat_seek_file(fd, &offset, FAT_SEEK_SET))
-                {
-                    uart_puts_p(PSTR("error seeking on "));
-                    uart_puts(command);
-                    uart_putc('\n');
-
-                    fat_close_file(fd);
-                    continue;
-                }
-
-                /* read text from the shell and write it to the file */
-                uint8_t data_len;
-                while(1)
-                {
-                    /* give a different prompt */
-                    uart_putc('<');
-                    uart_putc(' ');
-
-                    /* read one line of text */
-                    data_len = read_line(buffer, sizeof(buffer));
-                    if(!data_len)
-                        break;
-
-                    /* write text to file */
-                    if(fat_write_file(fd, (uint8_t*) buffer, data_len) != data_len)
-                    {
-                        uart_puts_p(PSTR("error writing to file\n"));
-                        break;
-                    }
-                }
-
-                fat_close_file(fd);
-            }
-            else if(strncmp_P(command, PSTR("mkdir "), 6) == 0)
-            {
-                command += 6;
-                if(command[0] == '\0')
-                    continue;
-
-                struct fat_dir_entry_struct dir_entry;
-                if(!fat_create_dir(dd, command, &dir_entry))
-                {
-                    uart_puts_p(PSTR("error creating directory: "));
-                    uart_puts(command);
-                    uart_putc('\n');
-                }
-            }
-#endif
-#if SD_RAW_WRITE_BUFFERING
-            else if(strcmp_P(command, PSTR("sync")) == 0)
-            {
-                if(!sd_raw_sync())
-                    uart_puts_p(PSTR("error syncing disk\n"));
-            }
-#endif
-            else
-            {
-                uart_puts_p(PSTR("unknown command: "));
-                uart_puts(command);
-                uart_putc('\n');
-            }
-        }
-
-        /* close directory */
-        fat_close_dir(dd);
-
-        /* close file system */
-        fat_close(fs);
-
-        /* close partition */
-        partition_close(partition);
-    }
-    
-    return 0;
-}
-
-uint8_t read_line(char* buffer, uint8_t buffer_length)
-{
-    memset(buffer, 0, buffer_length);
-
-    uint8_t read_length = 0;
-    while(read_length < buffer_length - 1)
-    {
-        uint8_t c = uart_getc();
-
-        if(c == 0x08 || c == 0x7f)
-        {
-            if(read_length < 1)
-                continue;
-
-            --read_length;
-            buffer[read_length] = '\0';
-
-            uart_putc(0x08);
-            uart_putc(' ');
-            uart_putc(0x08);
-
-            continue;
-        }
-
-        uart_putc(c);
-
-        if(c == '\n')
-        {
-            buffer[read_length] = '\0';
-            break;
-        }
-        else
-        {
-            buffer[read_length] = c;
-            ++read_length;
-        }
-    }
-
-    return read_length;
-}
-
-uint32_t strtolong(const char* str)
-{
-    uint32_t l = 0;
-    while(*str >= '0' && *str <= '9')
-        l = l * 10 + (*str++ - '0');
-
-    return l;
-}
-
-uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry)
-{
-    while(fat_read_dir(dd, dir_entry))
-    {
-        if(strcmp(dir_entry->long_name, name) == 0)
-        {
-            fat_reset_dir(dd);
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-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);
-}
-
-uint8_t print_disk_info(const struct fat_fs_struct* fs)
-{
-    if(!fs)
-        return 0;
-
-    struct sd_raw_info disk_info;
-    if(!sd_raw_get_info(&disk_info))
-        return 0;
-
-    uart_puts_p(PSTR("manuf:  0x")); uart_putc_hex(disk_info.manufacturer); uart_putc('\n');
-    uart_puts_p(PSTR("oem:    ")); uart_puts((char*) disk_info.oem); uart_putc('\n');
-    uart_puts_p(PSTR("prod:   ")); uart_puts((char*) disk_info.product); uart_putc('\n');
-    uart_puts_p(PSTR("rev:    ")); uart_putc_hex(disk_info.revision); uart_putc('\n');
-    uart_puts_p(PSTR("serial: 0x")); uart_putdw_hex(disk_info.serial); uart_putc('\n');
-    uart_puts_p(PSTR("date:   ")); uart_putw_dec(disk_info.manufacturing_month); uart_putc('/');
-                                   uart_putw_dec(disk_info.manufacturing_year); uart_putc('\n');
-    uart_puts_p(PSTR("size:   ")); uart_putdw_dec(disk_info.capacity / 1024 / 1024); uart_puts_p(PSTR("MB\n"));
-    uart_puts_p(PSTR("copy:   ")); uart_putw_dec(disk_info.flag_copy); uart_putc('\n');
-    uart_puts_p(PSTR("wr.pr.: ")); uart_putw_dec(disk_info.flag_write_protect_temp); uart_putc('/');
-                                   uart_putw_dec(disk_info.flag_write_protect); uart_putc('\n');
-    uart_puts_p(PSTR("format: ")); uart_putw_dec(disk_info.format); uart_putc('\n');
-    uart_puts_p(PSTR("free:   ")); uart_putdw_dec(fat_get_fs_free(fs)); uart_putc('/');
-                                   uart_putdw_dec(fat_get_fs_size(fs)); uart_putc('\n');
-
-    return 1;
-}
-
-#if FAT_DATETIME_SUPPORT
-void get_datetime(uint16_t* year, uint8_t* month, uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec)
-{
-    *year = 2007;
-    *month = 1;
-    *day = 1;
-    *hour = 0;
-    *min = 0;
-    *sec = 0;
-}
-#endif
-
-
diff --git a/sd_main.c b/sd_main.c
new file mode 100644 (file)
index 0000000..47298d4
--- /dev/null
+++ b/sd_main.c
@@ -0,0 +1,666 @@
+
+/*
+ * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <string.h>
+#include <avr/pgmspace.h>
+#include <avr/sleep.h>
+#include "fat.h"
+#include "fat_config.h"
+#include "partition.h"
+#include "sd_raw.h"
+#include "sd_raw_config.h"
+#include "uart.h"
+
+#define DEBUG 1
+
+/**
+ * \mainpage MMC/SD/SDHC card library
+ *
+ * This project provides a general purpose library which implements read and write
+ * support for MMC, SD and SDHC memory cards.
+ *
+ * It includes
+ * - low-level \link sd_raw MMC, SD and SDHC read/write routines \endlink
+ * - \link partition partition table support \endlink
+ * - a simple \link fat FAT16/FAT32 read/write implementation \endlink
+ *
+ * \section circuit The circuit
+ * The circuit which was mainly used during development consists of an Atmel AVR
+ * microcontroller with some passive components. It is quite simple and provides
+ * an easy test environment. The circuit which can be downloaded on the
+ * <a href="http://www.roland-riegel.de/sd-reader/">project homepage</a> has been
+ * improved with regard to operation stability.
+ *
+ * I used different microcontrollers during development, the ATmega8 with 8kBytes
+ * of flash, and its pin-compatible alternative, the ATmega168 with 16kBytes flash.
+ * The first one is the one I started with, but when I implemented FAT16 write
+ * support, I ran out of flash space and switched to the ATmega168. For FAT32, an
+ * ATmega328 is required.
+ * 
+ * The circuit board is a self-made and self-soldered board consisting of a single
+ * copper layer and standard DIL components, except of the MMC/SD card connector.
+ *
+ * The connector is soldered to the bottom side of the board. It has a simple
+ * eject button which, when a card is inserted, needs some space beyond the connector
+ * itself. As an additional feature the connector has two electrical switches
+ * to detect wether a card is inserted and wether this card is write-protected.
+ * 
+ * \section pictures Pictures
+ * \image html pic01.jpg "The circuit board used to implement and test this application."
+ * \image html pic02.jpg "The MMC/SD card connector on the soldering side of the circuit board."
+ *
+ * \section software The software
+ * The software is written in C (ISO C99). It might not be the smallest or
+ * the fastest one, but I think it is quite flexible. See the project's
+ * <a href="http://www.roland-riegel.de/sd-reader/benchmarks/">benchmark page</a> to get an
+ * idea of the possible data rates.
+ *
+ * I implemented an example application providing a simple command prompt which is accessible
+ * via the UART at 9600 Baud. With commands similiar to the Unix shell you can browse different
+ * directories, read and write files, create new ones and delete them again. Not all commands are
+ * available in all software configurations.
+ * - <tt>cat \<file\></tt>\n
+ *   Writes a hexdump of \<file\> to the terminal.
+ * - <tt>cd \<directory\></tt>\n
+ *   Changes current working directory to \<directory\>.
+ * - <tt>disk</tt>\n
+ *   Shows card manufacturer, status, filesystem capacity and free storage space.
+ * - <tt>init</tt>\n
+ *   Reinitializes and reopens the memory card.
+ * - <tt>ls</tt>\n
+ *   Shows the content of the current directory.
+ * - <tt>mkdir \<directory\></tt>\n
+ *   Creates a directory called \<directory\>.
+ * - <tt>mv \<file\> \<file_new\></tt>\n
+ *   Renames \<file\> to \<file_new\>. 
+ * - <tt>rm \<file\></tt>\n
+ *   Deletes \<file\>.
+ * - <tt>sync</tt>\n
+ *   Ensures all buffered data is written to the card.
+ * - <tt>touch \<file\></tt>\n
+ *   Creates \<file\>.
+ * - <tt>write \<file\> \<offset\></tt>\n
+ *   Writes text to \<file\>, starting from \<offset\>. The text is read
+ *   from the UART, line by line. Finish with an empty line.
+ *
+ * \htmlonly
+ * <p>
+ * The following table shows some typical code sizes in bytes, using the 20090330 release with a
+ * buffered read-write MMC/SD configuration, FAT16 and static memory allocation:
+ * </p>
+ *
+ * <table border="1" cellpadding="2">
+ *     <tr>
+ *         <th>layer</th>
+ *         <th>code size</th>
+ *         <th>static RAM usage</th>
+ *     </tr>
+ *     <tr>
+ *         <td>MMC/SD</td>
+ *         <td align="right">2410</td>
+ *         <td align="right">518</td>
+ *     </tr>
+ *     <tr>
+ *         <td>Partition</td>
+ *         <td align="right">456</td>
+ *         <td align="right">17</td>
+ *     </tr>
+ *     <tr>
+ *         <td>FAT16</td>
+ *         <td align="right">7928</td>
+ *         <td align="right">188</td>
+ *     </tr>
+ * </table>
+ *
+ * <p>
+ * The static RAM is mostly used for buffering memory card access, which
+ * improves performance and reduces implementation complexity.
+ * </p>
+ * 
+ * <p>
+ * Please note that the numbers above do not include the C library functions
+ * used, e.g. some string functions. These will raise the numbers somewhat
+ * if they are not already used in other program parts.
+ * </p>
+ * 
+ * <p>
+ * When opening a partition, filesystem, file or directory, a little amount
+ * of RAM is used, as listed in the following table. Depending on the library
+ * configuration, the memory is either allocated statically or dynamically.
+ * </p>
+ *
+ * <table border="1" cellpadding="2">
+ *     <tr>
+ *         <th>descriptor</th>
+ *         <th>dynamic/static RAM</th>
+ *     </tr>
+ *     <tr>
+ *         <td>partition</td>
+ *         <td align="right">17</td>
+ *     </tr>
+ *     <tr>
+ *         <td>filesystem</td>
+ *         <td align="right">26</td>
+ *     </tr>
+ *     <tr>
+ *         <td>file</td>
+ *         <td align="right">53</td>
+ *     </tr>
+ *     <tr>
+ *         <td>directory</td>
+ *         <td align="right">49</td>
+ *     </tr>
+ * </table>
+ * 
+ * \endhtmlonly
+ *
+ * \section adaptation Adapting the software to your needs
+ * The only hardware dependent part is the communication layer talking to the
+ * memory card. The other parts like partition table and FAT support are
+ * completely independent, you could use them even for managing Compact Flash
+ * cards or standard ATAPI hard disks.
+ *
+ * By changing the MCU* variables in the Makefile, you can use other Atmel
+ * microcontrollers or different clock speeds. You might also want to change
+ * the configuration defines in the files fat_config.h, partition_config.h,
+ * sd_raw_config.h and sd-reader_config.h. For example, you could disable
+ * write support completely if you only need read support.
+ *
+ * For further information, visit the project's
+ * <a href="http://www.roland-riegel.de/sd-reader/faq/">FAQ page</a>.
+ * 
+ * \section bugs Bugs or comments?
+ * If you have comments or found a bug in the software - there might be some
+ * of them - you may contact me per mail at feedback@roland-riegel.de.
+ *
+ * \section acknowledgements Acknowledgements
+ * Thanks go to Ulrich Radig, who explained on his homepage how to interface
+ * MMC cards to the Atmel microcontroller (http://www.ulrichradig.de/).
+ * I adapted his work for my circuit.
+ * 
+ * \section copyright Copyright 2006-2012 by Roland Riegel
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation (http://www.gnu.org/copyleft/gpl.html).
+ * At your option, you can alternatively redistribute and/or modify the following
+ * files under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation (http://www.gnu.org/copyleft/lgpl.html):
+ * - byteordering.c
+ * - byteordering.h
+ * - fat.c
+ * - fat.h
+ * - fat_config.h
+ * - partition.c
+ * - partition.h
+ * - partition_config.h
+ * - sd_raw.c
+ * - sd_raw.h
+ * - sd_raw_config.h
+ * - sd-reader_config.h
+ */
+
+static uint8_t read_line(char* buffer, uint8_t buffer_length);
+static uint32_t strtolong(const char* str);
+static 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);
+static struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name); 
+static uint8_t print_disk_info(const struct fat_fs_struct* fs);
+
+int main()
+{
+    /* we will just use ordinary idle mode */
+    set_sleep_mode(SLEEP_MODE_IDLE);
+
+    /* setup uart */
+    uart_init();
+
+    while(1)
+    {
+        /* setup sd card slot */
+        if(!sd_raw_init())
+        {
+#if DEBUG
+            uart_puts_p(PSTR("MMC/SD initialization failed\n"));
+#endif
+            continue;
+        }
+
+        /* open first partition */
+        struct partition_struct* 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)
+            {
+#if DEBUG
+                uart_puts_p(PSTR("opening partition failed\n"));
+#endif
+                continue;
+            }
+        }
+
+        /* open file system */
+        struct fat_fs_struct* fs = fat_open(partition);
+        if(!fs)
+        {
+#if DEBUG
+            uart_puts_p(PSTR("opening filesystem failed\n"));
+#endif
+            continue;
+        }
+
+        /* open root directory */
+        struct fat_dir_entry_struct directory;
+        fat_get_dir_entry_of_path(fs, "/", &directory);
+
+        struct fat_dir_struct* dd = fat_open_dir(fs, &directory);
+        if(!dd)
+        {
+#if DEBUG
+            uart_puts_p(PSTR("opening root directory failed\n"));
+#endif
+            continue;
+        }
+        
+        /* print some card information as a boot message */
+        print_disk_info(fs);
+
+        /* provide a simple shell */
+        char buffer[24];
+        while(1)
+        {
+            /* print prompt */
+            uart_putc('>');
+            uart_putc(' ');
+
+            /* read command */
+            char* command = buffer;
+            if(read_line(command, sizeof(buffer)) < 1)
+                continue;
+
+            /* execute command */
+            if(strcmp_P(command, PSTR("init")) == 0)
+            {
+                break;
+            }
+            else if(strncmp_P(command, PSTR("cd "), 3) == 0)
+            {
+                command += 3;
+                if(command[0] == '\0')
+                    continue;
+
+                /* change directory */
+                struct fat_dir_entry_struct subdir_entry;
+                if(find_file_in_dir(fs, dd, command, &subdir_entry))
+                {
+                    struct fat_dir_struct* dd_new = fat_open_dir(fs, &subdir_entry);
+                    if(dd_new)
+                    {
+                        fat_close_dir(dd);
+                        dd = dd_new;
+                        continue;
+                    }
+                }
+
+                uart_puts_p(PSTR("directory not found: "));
+                uart_puts(command);
+                uart_putc('\n');
+            }
+            else if(strcmp_P(command, PSTR("ls")) == 0)
+            {
+                /* print directory listing */
+                struct fat_dir_entry_struct dir_entry;
+                while(fat_read_dir(dd, &dir_entry))
+                {
+                    uint8_t spaces = sizeof(dir_entry.long_name) - strlen(dir_entry.long_name) + 4;
+
+                    uart_puts(dir_entry.long_name);
+                    uart_putc(dir_entry.attributes & FAT_ATTRIB_DIR ? '/' : ' ');
+                    while(spaces--)
+                        uart_putc(' ');
+                    uart_putdw_dec(dir_entry.file_size);
+                    uart_putc('\n');
+                }
+            }
+            else if(strncmp_P(command, PSTR("cat "), 4) == 0)
+            {
+                command += 4;
+                if(command[0] == '\0')
+                    continue;
+                
+                /* search file in current directory and open it */
+                struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
+                if(!fd)
+                {
+                    uart_puts_p(PSTR("error opening "));
+                    uart_puts(command);
+                    uart_putc('\n');
+                    continue;
+                }
+
+                /* print file contents */
+                uint8_t buffer[8];
+                uint32_t offset = 0;
+                intptr_t count;
+                while((count = fat_read_file(fd, buffer, sizeof(buffer))) > 0)
+                {
+                    uart_putdw_hex(offset);
+                    uart_putc(':');
+                    for(intptr_t i = 0; i < count; ++i)
+                    {
+                        uart_putc(' ');
+                        uart_putc_hex(buffer[i]);
+                    }
+                    uart_putc('\n');
+                    offset += 8;
+                }
+
+                fat_close_file(fd);
+            }
+            else if(strcmp_P(command, PSTR("disk")) == 0)
+            {
+                if(!print_disk_info(fs))
+                    uart_puts_p(PSTR("error reading disk info\n"));
+            }
+#if FAT_WRITE_SUPPORT
+            else if(strncmp_P(command, PSTR("rm "), 3) == 0)
+            {
+                command += 3;
+                if(command[0] == '\0')
+                    continue;
+                
+                struct fat_dir_entry_struct file_entry;
+                if(find_file_in_dir(fs, dd, command, &file_entry))
+                {
+                    if(fat_delete_file(fs, &file_entry))
+                        continue;
+                }
+
+                uart_puts_p(PSTR("error deleting file: "));
+                uart_puts(command);
+                uart_putc('\n');
+            }
+            else if(strncmp_P(command, PSTR("touch "), 6) == 0)
+            {
+                command += 6;
+                if(command[0] == '\0')
+                    continue;
+
+                struct fat_dir_entry_struct file_entry;
+                if(!fat_create_file(dd, command, &file_entry))
+                {
+                    uart_puts_p(PSTR("error creating file: "));
+                    uart_puts(command);
+                    uart_putc('\n');
+                }
+            }
+            else if(strncmp_P(command, PSTR("mv "), 3) == 0)
+            {
+                command += 3;
+                if(command[0] == '\0')
+                    continue;
+
+                char* target = command;
+                while(*target != ' ' && *target != '\0')
+                    ++target;
+
+                if(*target == ' ')
+                    *target++ = '\0';
+                else
+                    continue;
+
+                struct fat_dir_entry_struct file_entry;
+                if(find_file_in_dir(fs, dd, command, &file_entry))
+                {
+                    if(fat_move_file(fs, &file_entry, dd, target))
+                        continue;
+                }
+
+                uart_puts_p(PSTR("error moving file: "));
+                uart_puts(command);
+                uart_putc('\n');
+            }
+            else if(strncmp_P(command, PSTR("write "), 6) == 0)
+            {
+                command += 6;
+                if(command[0] == '\0')
+                    continue;
+
+                char* offset_value = command;
+                while(*offset_value != ' ' && *offset_value != '\0')
+                    ++offset_value;
+
+                if(*offset_value == ' ')
+                    *offset_value++ = '\0';
+                else
+                    continue;
+
+                /* search file in current directory and open it */
+                struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
+                if(!fd)
+                {
+                    uart_puts_p(PSTR("error opening "));
+                    uart_puts(command);
+                    uart_putc('\n');
+                    continue;
+                }
+
+                int32_t offset = strtolong(offset_value);
+                if(!fat_seek_file(fd, &offset, FAT_SEEK_SET))
+                {
+                    uart_puts_p(PSTR("error seeking on "));
+                    uart_puts(command);
+                    uart_putc('\n');
+
+                    fat_close_file(fd);
+                    continue;
+                }
+
+                /* read text from the shell and write it to the file */
+                uint8_t data_len;
+                while(1)
+                {
+                    /* give a different prompt */
+                    uart_putc('<');
+                    uart_putc(' ');
+
+                    /* read one line of text */
+                    data_len = read_line(buffer, sizeof(buffer));
+                    if(!data_len)
+                        break;
+
+                    /* write text to file */
+                    if(fat_write_file(fd, (uint8_t*) buffer, data_len) != data_len)
+                    {
+                        uart_puts_p(PSTR("error writing to file\n"));
+                        break;
+                    }
+                }
+
+                fat_close_file(fd);
+            }
+            else if(strncmp_P(command, PSTR("mkdir "), 6) == 0)
+            {
+                command += 6;
+                if(command[0] == '\0')
+                    continue;
+
+                struct fat_dir_entry_struct dir_entry;
+                if(!fat_create_dir(dd, command, &dir_entry))
+                {
+                    uart_puts_p(PSTR("error creating directory: "));
+                    uart_puts(command);
+                    uart_putc('\n');
+                }
+            }
+#endif
+#if SD_RAW_WRITE_BUFFERING
+            else if(strcmp_P(command, PSTR("sync")) == 0)
+            {
+                if(!sd_raw_sync())
+                    uart_puts_p(PSTR("error syncing disk\n"));
+            }
+#endif
+            else
+            {
+                uart_puts_p(PSTR("unknown command: "));
+                uart_puts(command);
+                uart_putc('\n');
+            }
+        }
+
+        /* close directory */
+        fat_close_dir(dd);
+
+        /* close file system */
+        fat_close(fs);
+
+        /* close partition */
+        partition_close(partition);
+    }
+    
+    return 0;
+}
+
+uint8_t read_line(char* buffer, uint8_t buffer_length)
+{
+    memset(buffer, 0, buffer_length);
+
+    uint8_t read_length = 0;
+    while(read_length < buffer_length - 1)
+    {
+        uint8_t c = uart_getc();
+
+        if(c == 0x08 || c == 0x7f)
+        {
+            if(read_length < 1)
+                continue;
+
+            --read_length;
+            buffer[read_length] = '\0';
+
+            uart_putc(0x08);
+            uart_putc(' ');
+            uart_putc(0x08);
+
+            continue;
+        }
+
+        uart_putc(c);
+
+        if(c == '\n')
+        {
+            buffer[read_length] = '\0';
+            break;
+        }
+        else
+        {
+            buffer[read_length] = c;
+            ++read_length;
+        }
+    }
+
+    return read_length;
+}
+
+uint32_t strtolong(const char* str)
+{
+    uint32_t l = 0;
+    while(*str >= '0' && *str <= '9')
+        l = l * 10 + (*str++ - '0');
+
+    return l;
+}
+
+uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry)
+{
+    while(fat_read_dir(dd, dir_entry))
+    {
+        if(strcmp(dir_entry->long_name, name) == 0)
+        {
+            fat_reset_dir(dd);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+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);
+}
+
+uint8_t print_disk_info(const struct fat_fs_struct* fs)
+{
+    if(!fs)
+        return 0;
+
+    struct sd_raw_info disk_info;
+    if(!sd_raw_get_info(&disk_info))
+        return 0;
+
+    uart_puts_p(PSTR("manuf:  0x")); uart_putc_hex(disk_info.manufacturer); uart_putc('\n');
+    uart_puts_p(PSTR("oem:    ")); uart_puts((char*) disk_info.oem); uart_putc('\n');
+    uart_puts_p(PSTR("prod:   ")); uart_puts((char*) disk_info.product); uart_putc('\n');
+    uart_puts_p(PSTR("rev:    ")); uart_putc_hex(disk_info.revision); uart_putc('\n');
+    uart_puts_p(PSTR("serial: 0x")); uart_putdw_hex(disk_info.serial); uart_putc('\n');
+    uart_puts_p(PSTR("date:   ")); uart_putw_dec(disk_info.manufacturing_month); uart_putc('/');
+                                   uart_putw_dec(disk_info.manufacturing_year); uart_putc('\n');
+    uart_puts_p(PSTR("size:   ")); uart_putdw_dec(disk_info.capacity / 1024 / 1024); uart_puts_p(PSTR("MB\n"));
+    uart_puts_p(PSTR("copy:   ")); uart_putw_dec(disk_info.flag_copy); uart_putc('\n');
+    uart_puts_p(PSTR("wr.pr.: ")); uart_putw_dec(disk_info.flag_write_protect_temp); uart_putc('/');
+                                   uart_putw_dec(disk_info.flag_write_protect); uart_putc('\n');
+    uart_puts_p(PSTR("format: ")); uart_putw_dec(disk_info.format); uart_putc('\n');
+    uart_puts_p(PSTR("free:   ")); uart_putdw_dec(fat_get_fs_free(fs)); uart_putc('/');
+                                   uart_putdw_dec(fat_get_fs_size(fs)); uart_putc('\n');
+
+    return 1;
+}
+
+#if FAT_DATETIME_SUPPORT
+void get_datetime(uint16_t* year, uint8_t* month, uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec)
+{
+    *year = 2007;
+    *month = 1;
+    *day = 1;
+    *hour = 0;
+    *min = 0;
+    *sec = 0;
+}
+#endif
+
+