Index: sd-reader_config.h
===================================================================
--- sd-reader_config.h	(.../sd-reader_release_20060901)	(Revision 0)
+++ sd-reader_config.h	(.../sd-reader_release_20061101)	(Revision 106)
@@ -0,0 +1,40 @@
+
+/* 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.
+ */
+
+#ifndef SD_READER_CONFIG_H
+#define SD_READER_CONFIG_H
+
+/**
+ * \addtogroup config Sd-reader configuration
+ *
+ * @{
+ */
+
+/**
+ * \file
+ * Common sd-reader configuration used by all modules.
+ *
+ * \note This file contains only configuration items relevant to
+ * all sd-reader implementation files. For module specific configuration
+ * options, please see the files fat16_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
+
+/**
+ * @}
+ */
+
+#endif
+

Eigenschaftsänderungen: sd-reader_config.h
___________________________________________________________________
Name: svn:keywords
   + Id

Index: Doxyfile
===================================================================
--- Doxyfile	(.../sd-reader_release_20060901)	(Revision 106)
+++ Doxyfile	(.../sd-reader_release_20061101)	(Revision 106)
@@ -83,10 +83,6 @@
 # configuration options related to the input files
 #---------------------------------------------------------------------------
 INPUT                  = .
-#                         fat16.c \
-#                         fat16.h \
-#                         sd_raw.c \
-#                         sd_raw.h
 FILE_PATTERNS          = *.c \
                          *.h
 RECURSIVE              = NO
@@ -195,7 +191,7 @@
 SEARCH_INCLUDES        = YES
 INCLUDE_PATH           = 
 INCLUDE_FILE_PATTERNS  = 
-PREDEFINED             = 
+PREDEFINED             = DOXYGEN=1
 EXPAND_AS_DEFINED      = 
 SKIP_FUNCTION_MACROS   = YES
 #---------------------------------------------------------------------------
Index: partition_config.h
===================================================================
--- partition_config.h	(.../sd-reader_release_20060901)	(Revision 0)
+++ partition_config.h	(.../sd-reader_release_20061101)	(Revision 106)
@@ -0,0 +1,31 @@
+
+/* 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.
+ */
+
+#ifndef PARTITION_CONFIG_H
+#define PARTITION_CONFIG_G
+
+/**
+ * \addtogroup partition
+ *
+ * @{
+ */
+/**
+ * \file
+ * Partition configuration.
+ */
+
+/**
+ * \ingroup partition_config
+ * Maximum number of partition handles.
+ */
+#define PARTITION_COUNT 1
+
+/**
+ * @}
+ */
+
+#endif
+

Eigenschaftsänderungen: partition_config.h
___________________________________________________________________
Name: svn:keywords
   + Id

Index: ChangeLog
===================================================================
--- ChangeLog	(.../sd-reader_release_20060901)	(Revision 106)
+++ ChangeLog	(.../sd-reader_release_20061101)	(Revision 106)
@@ -1,4 +1,10 @@
 
+2006-11-01 sd-reader
+	* Implement creation and deletion of directories.
+	* Clear the directory entries of new directory clusters.
+	* Prevent linkage against printf().
+	* Make the use of malloc()/free() optional.
+
 2006-09-01 sd-reader
 	* Fix shortening files.
 	* Fix free disk space calculation.
Index: sd_raw_config.h
===================================================================
--- sd_raw_config.h	(.../sd-reader_release_20060901)	(Revision 106)
+++ sd_raw_config.h	(.../sd-reader_release_20061101)	(Revision 106)
@@ -69,7 +69,8 @@
     #define select_card() PORTB &= ~(1 << PB4)
     #define unselect_card() PORTB |= (1 << PB4)
 #elif defined(__AVR_ATmega64__) || \
-      defined(__AVR_ATmega128__)
+      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)
Index: partition.c
===================================================================
--- partition.c	(.../sd-reader_release_20060901)	(Revision 106)
+++ partition.c	(.../sd-reader_release_20061101)	(Revision 106)
@@ -5,10 +5,15 @@
  */
 
 #include "partition.h"
+#include "partition_config.h"
+#include "sd-reader_config.h"
 
-#include <stdlib.h>
 #include <string.h>
 
+#if USE_DYNAMIC_MEMORY
+    #include <stdlib.h>
+#endif
+
 /**
  * \addtogroup partition Partition table support
  *
@@ -24,6 +29,15 @@
  */
 
 /**
+ * \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
@@ -34,6 +48,7 @@
  * \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
@@ -41,7 +56,7 @@
  * \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, int8_t index)
+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];
@@ -61,15 +76,31 @@
     }
 
     /* 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)
     {
@@ -109,7 +140,11 @@
         return 0;
 
     /* destroy partition descriptor */
+#if USE_DYNAMIC_MEMORY
     free(partition);
+#else
+    partition->type = PARTITION_TYPE_FREE;
+#endif
 
     return 1;
 }
Index: uart.c
===================================================================
--- uart.c	(.../sd-reader_release_20060901)	(Revision 106)
+++ uart.c	(.../sd-reader_release_20061101)	(Revision 106)
@@ -7,6 +7,7 @@
 #include <stdio.h>
 #include <avr/interrupt.h>
 #include <avr/io.h>
+#include <avr/pgmspace.h>
 #include <avr/sfr_defs.h>
 #include <avr/sleep.h>
 
@@ -58,11 +59,6 @@
 #define UBRRVAL (F_CPU/(BAUD*16)-1)
 #define USE_SLEEP 1
 
-static int _uart_putc(char c, FILE* stream);
-static int _uart_getc(FILE* stream);
-
-static FILE stdio = FDEV_SETUP_STREAM(_uart_putc, _uart_getc, _FDEV_SETUP_RW);
-
 void uart_init()
 {
     /* set baud rate */
@@ -78,11 +74,6 @@
 #endif
 }
 
-void uart_connect_stdio()
-{
-    stdin = stdout = stderr = &stdio;
-}
-
 void uart_putc(uint8_t c)
 {
     if(c == '\n')
@@ -95,12 +86,6 @@
     UDR = c;
 }
 
-int _uart_putc(char c, FILE* stream)
-{
-    uart_putc(c);
-    return 0;
-}
-
 void uart_putc_hex(uint8_t b)
 {
     /* upper nibble */
@@ -116,6 +101,74 @@
         uart_putc((b & 0x0f) - 0x0a + 'a');
 }
 
+void uart_putw_hex(uint16_t w)
+{
+    uart_putc_hex((uint8_t) (w >> 8));
+    uart_putc_hex((uint8_t) (w & 0xff));
+}
+
+void uart_putdw_hex(uint32_t dw)
+{
+    uart_putw_hex((uint16_t) (dw >> 16));
+    uart_putw_hex((uint16_t) (dw & 0xffff));
+}
+
+void uart_putw_dec(uint16_t w)
+{
+    uint16_t num = 10000;
+    uint8_t started = 0;
+
+    while(num > 0)
+    {
+        uint8_t b = w / num;
+        if(b > 0 || started || num == 1)
+        {
+            uart_putc('0' + b);
+            started = 1;
+        }
+        w -= b * num;
+
+        num /= 10;
+    }
+}
+
+void uart_putdw_dec(uint32_t dw)
+{
+    uint32_t num = 1000000000;
+    uint8_t started = 0;
+
+    while(num > 0)
+    {
+        uint8_t b = dw / num;
+        if(b > 0 || started || num == 1)
+        {
+            uart_putc('0' + b);
+            started = 1;
+        }
+        dw -= b * num;
+
+        num /= 10;
+    }
+}
+
+void uart_puts(const char* str)
+{
+    while(*str)
+        uart_putc(*str++);
+}
+
+void uart_puts_p(PGM_P str)
+{
+    while(1)
+    {
+        uint8_t b = pgm_read_byte_near(str++);
+        if(!b)
+            break;
+
+        uart_putc(b);
+    }
+}
+
 uint8_t uart_getc()
 {
     /* wait until receive buffer is full */
@@ -138,10 +191,5 @@
     return b;
 }
 
-int _uart_getc(FILE* stream)
-{
-    return uart_getc();
-}
-
 EMPTY_INTERRUPT(USART_RXC_vect)
 
Index: fat16_config.h
===================================================================
--- fat16_config.h	(.../sd-reader_release_20060901)	(Revision 106)
+++ fat16_config.h	(.../sd-reader_release_20061101)	(Revision 106)
@@ -26,6 +26,24 @@
 #define FAT16_WRITE_SUPPORT 1
 
 /**
+ * \ingroup fat16_config
+ * Maximum number of filesystem handles.
+ */
+#define FAT16_FS_COUNT 1
+
+/**
+ * \ingroup fat16_config
+ * Maximum number of file handles.
+ */
+#define FAT16_FILE_COUNT 1
+
+/**
+ * \ingroup fat16_config
+ * Maximum number of directory handles.
+ */
+#define FAT16_DIR_COUNT 2
+
+/**
  * @}
  */
 
Index: partition.h
===================================================================
--- partition.h	(.../sd-reader_release_20060901)	(Revision 106)
+++ partition.h	(.../sd-reader_release_20061101)	(Revision 106)
@@ -99,13 +99,42 @@
  */
 typedef uint8_t (*device_read_interval_t)(uint32_t offset, uint8_t* buffer, uint16_t interval, uint16_t length, device_read_callback_t callback, void* p);
 /**
- * A function pointer used to write from the partition.
+ * 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)(uint32_t offset, const uint8_t* buffer, uint16_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 uint16_t (*device_write_callback_t)(uint8_t* buffer, uint32_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)(uint32_t offset, uint8_t* buffer, uint16_t length, device_write_callback_t callback, void* p);
 
 /**
  * Describes a partition.
@@ -133,6 +162,13 @@
      *       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.
@@ -150,7 +186,7 @@
     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, int8_t index);
+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);
 
 /**
Index: sd_raw.c
===================================================================
--- sd_raw.c	(.../sd-reader_release_20060901)	(Revision 106)
+++ sd_raw.c	(.../sd-reader_release_20061101)	(Revision 106)
@@ -381,7 +381,7 @@
  * \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
+ * \see sd_raw_read_interval, sd_raw_write, sd_raw_write_interval
  */
 uint8_t sd_raw_read(uint32_t offset, uint8_t* buffer, uint16_t length)
 {
@@ -490,9 +490,9 @@
  * \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_read, sd_raw_write
+ * \see sd_raw_write_interval, sd_raw_read, sd_raw_write
  */
-uint8_t sd_raw_read_interval(uint32_t offset, uint8_t* buffer, uint16_t interval, uint16_t length, sd_raw_interval_handler callback, void* p)
+uint8_t sd_raw_read_interval(uint32_t offset, uint8_t* buffer, uint16_t interval, uint16_t length, sd_raw_read_interval_handler_t callback, void* p)
 {
     if(!buffer || interval == 0 || length < interval || !callback)
         return 0;
@@ -598,7 +598,7 @@
  * \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_read
+ * \see sd_raw_write_interval, sd_raw_read, sd_raw_read_interval
  */
 uint8_t sd_raw_write(uint32_t offset, const uint8_t* buffer, uint16_t length)
 {
@@ -699,6 +699,61 @@
 
 /**
  * \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(uint32_t offset, uint8_t* buffer, uint16_t length, sd_raw_write_interval_handler_t callback, void* p)
+{
+#if SD_RAW_WRITE_SUPPORT
+
+#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;
+
+#else
+    return 0;
+#endif
+}
+
+/**
+ * \ingroup sd_raw
  * Writes the write buffer's content to the card.
  *
  * \note When write buffering is enabled, you should
Index: uart.h
===================================================================
--- uart.h	(.../sd-reader_release_20060901)	(Revision 106)
+++ uart.h	(.../sd-reader_release_20061101)	(Revision 106)
@@ -8,11 +8,22 @@
 #define UART_H
 
 #include <stdint.h>
+#include <avr/pgmspace.h>
 
 void uart_init();
-void uart_connect_stdio();
+
 void uart_putc(uint8_t c);
+
 void uart_putc_hex(uint8_t b);
+void uart_putw_hex(uint16_t w);
+void uart_putdw_hex(uint32_t dw);
+
+void uart_putw_dec(uint16_t w);
+void uart_putdw_dec(uint32_t dw);
+
+void uart_puts(const char* str);
+void uart_puts_p(PGM_P str);
+
 uint8_t uart_getc();
 
 #endif
Index: fat16.c
===================================================================
--- fat16.c	(.../sd-reader_release_20060901)	(Revision 106)
+++ fat16.c	(.../sd-reader_release_20061101)	(Revision 106)
@@ -7,10 +7,14 @@
 #include "partition.h"
 #include "fat16.h"
 #include "fat16_config.h"
+#include "sd-reader_config.h"
 
-#include <stdlib.h>
 #include <string.h>
 
+#if USE_DYNAMIC_MEMORY
+    #include <stdlib.h>
+#endif
+
 /**
  * \addtogroup fat16 FAT16 support
  *
@@ -168,6 +172,12 @@
     uint8_t buffer_size;
 };
 
+#if !USE_DYNAMIC_MEMORY
+static struct fat16_fs_struct fat16_fs_handlers[FAT16_FS_COUNT];
+static struct fat16_file_struct fat16_file_handlers[FAT16_FILE_COUNT];
+static struct fat16_dir_struct fat16_dir_handlers[FAT16_DIR_COUNT];
+#endif
+
 static uint8_t fat16_read_header(struct fat16_fs_struct* fs);
 static uint8_t fat16_read_root_dir_entry(const struct fat16_fs_struct* fs, uint16_t entry_num, struct fat16_dir_entry_struct* dir_entry);
 static uint8_t fat16_read_sub_dir_entry(const struct fat16_fs_struct* fs, uint16_t entry_num, const struct fat16_dir_entry_struct* parent, struct fat16_dir_entry_struct* dir_entry);
@@ -178,6 +188,9 @@
 static uint16_t fat16_append_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num, uint16_t count);
 static uint8_t fat16_free_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num);
 static uint8_t fat16_terminate_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num);
+static uint8_t fat16_clear_cluster(const struct fat16_fs_struct* fs, uint16_t cluster_num);
+static uint16_t fat16_clear_cluster_callback(uint8_t* buffer, uint32_t offset, void* p);
+static uint32_t fat16_find_offset_for_dir_entry(const struct fat16_fs_struct* fs, const struct fat16_dir_struct* parent, const struct fat16_dir_entry_struct* dir_entry);
 static uint8_t fat16_write_dir_entry(const struct fat16_fs_struct* fs, const struct fat16_dir_entry_struct* dir_entry);
 
 static uint8_t fat16_get_fs_free_callback(uint8_t* buffer, uint32_t offset, void* p);
@@ -194,22 +207,42 @@
 {
     if(!partition ||
 #if FAT16_WRITE_SUPPORT
-       !partition->device_write
+       !partition->device_write ||
+       !partition->device_write_interval
 #else
        0
 #endif
       )
         return 0;
 
+#if USE_DYNAMIC_MEMORY
     struct fat16_fs_struct* fs = malloc(sizeof(*fs));
     if(!fs)
         return 0;
+#else
+    struct fat16_fs_struct* fs = fat16_fs_handlers;
+    uint8_t i;
+    for(i = 0; i < FAT16_FS_COUNT; ++i)
+    {
+        if(!fs->partition)
+            break;
+
+        ++fs;
+    }
+    if(i >= FAT16_FS_COUNT)
+        return 0;
+#endif
+
     memset(fs, 0, sizeof(*fs));
 
     fs->partition = partition;
     if(!fat16_read_header(fs))
     {
+#if USE_DYNAMIC_MEMORY
         free(fs);
+#else
+        fs->partition = 0;
+#endif
         return 0;
     }
     
@@ -231,7 +264,11 @@
     if(!fs)
         return;
 
+#if USE_DYNAMIC_MEMORY
     free(fs);
+#else
+    fs->partition = 0;
+#endif
 }
 
 /**
@@ -885,6 +922,50 @@
 }
 
 /**
+ * \ingroup fat16_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 fat16_clear_cluster(const struct fat16_fs_struct* fs, uint16_t cluster_num)
+{
+#if FAT16_WRITE_SUPPORT
+    if(cluster_num < 2)
+        return 0;
+
+    uint32_t cluster_offset = fs->header.cluster_zero_offset +
+                              (uint32_t) (cluster_num - 2) * fs->header.cluster_size;
+    uint8_t zero[16];
+    return fs->partition->device_write_interval(cluster_offset,
+                                                zero,
+                                                fs->header.cluster_size,
+                                                fat16_clear_cluster_callback,
+                                                0
+                                               );
+#else
+    return 0;
+#endif
+}
+
+/**
+ * \ingroup fat16_fs
+ * Callback function for clearing a cluster.
+ */
+uint16_t fat16_clear_cluster_callback(uint8_t* buffer, uint32_t offset, void* p)
+{
+#if FAT16_WRITE_SUPPORT
+    memset(buffer, 0, 16);
+    return 32;
+#else
+    return 0;
+#endif
+}
+
+/**
  * \ingroup fat16_file
  * Opens a file on a FAT16 filesystem.
  *
@@ -898,9 +979,23 @@
     if(!fs || !dir_entry || (dir_entry->attributes & FAT16_ATTRIB_DIR))
         return 0;
 
+#if USE_DYNAMIC_MEMORY
     struct fat16_file_struct* fd = malloc(sizeof(*fd));
     if(!fd)
         return 0;
+#else
+    struct fat16_file_struct* fd = fat16_file_handlers;
+    uint8_t i;
+    for(i = 0; i < FAT16_FILE_COUNT; ++i)
+    {
+        if(!fd->fs)
+            break;
+
+        ++fd;
+    }
+    if(i >= FAT16_FILE_COUNT)
+        return 0;
+#endif
     
     memcpy(&fd->dir_entry, dir_entry, sizeof(*dir_entry));
     fd->fs = fs;
@@ -920,7 +1015,11 @@
 void fat16_close_file(struct fat16_file_struct* fd)
 {
     if(fd)
+#if USE_DYNAMIC_MEMORY
         free(fd);
+#else
+        fd->fs = 0;
+#endif
 }
 
 /**
@@ -1323,9 +1422,23 @@
     if(!fs || !dir_entry || !(dir_entry->attributes & FAT16_ATTRIB_DIR))
         return 0;
 
+#if USE_DYNAMIC_MEMORY
     struct fat16_dir_struct* dd = malloc(sizeof(*dd));
     if(!dd)
         return 0;
+#else
+    struct fat16_dir_struct* dd = fat16_dir_handlers;
+    uint8_t i;
+    for(i = 0; i < FAT16_DIR_COUNT; ++i)
+    {
+        if(!dd->fs)
+            break;
+
+        ++dd;
+    }
+    if(i >= FAT16_DIR_COUNT)
+        return 0;
+#endif
     
     memcpy(&dd->dir_entry, dir_entry, sizeof(*dir_entry));
     dd->fs = fs;
@@ -1348,7 +1461,11 @@
 void fat16_close_dir(struct fat16_dir_struct* dd)
 {
     if(dd)
+#if USE_DYNAMIC_MEMORY
         free(dd);
+#else
+        dd->fs = 0;
+#endif
 }
 
 /**
@@ -1412,6 +1529,108 @@
 
 /**
  * \ingroup fat16_fs
+ * Searches for space where to store a directory entry.
+ *
+ * \param[in] fs The filesystem on which to operate.
+ * \param[in] dir_entry The directory entry for which to search space.
+ * \returns 0 on failure, a device offset on success.
+ */
+uint32_t fat16_find_offset_for_dir_entry(const struct fat16_fs_struct* fs, const struct fat16_dir_struct* parent, const struct fat16_dir_entry_struct* dir_entry)
+{
+#if FAT16_WRITE_SUPPORT
+    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) / 13 + 1 + 1;
+    uint8_t free_dir_entries_found = 0;
+    uint16_t cluster_num = parent->dir_entry.cluster;
+    uint32_t dir_entry_offset = 0;
+    uint32_t offset = 0;
+    uint32_t offset_to = 0;
+
+    if(cluster_num == 0)
+    {
+        /* 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 entry
+                 * 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.
+                 */
+
+                uint16_t cluster_next = fat16_get_next_cluster(fs, cluster_num);
+                if(!cluster_next)
+                {
+                    cluster_next = fat16_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 +
+                                       (uint32_t) (cluster_next - 2) * fs->header.cluster_size;
+
+                    /* clear cluster to avoid garbage directory entries */
+                    fat16_clear_cluster(fs, cluster_next);
+
+                    break;
+                }
+                cluster_num = cluster_next;
+            }
+
+            offset = fs->header.cluster_zero_offset +
+                     (uint32_t) (cluster_num - 2) * fs->header.cluster_size;
+            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 == FAT16_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;
+
+#else
+    return 0;
+#endif
+}
+
+/**
+ * \ingroup fat16_fs
  * Writes a directory entry to disk.
  *
  * \note The file name is not checked for invalid characters.
@@ -1578,7 +1797,7 @@
 uint8_t fat16_create_file(struct fat16_dir_struct* parent, const char* file, struct fat16_dir_entry_struct* dir_entry)
 {
 #if FAT16_WRITE_SUPPORT
-    if(!parent || !file || !file[0])
+    if(!parent || !file || !file[0] || !dir_entry)
         return 0;
 
     /* check if the file already exists */
@@ -1590,97 +1809,20 @@
         if(strcmp(file, dir_entry->long_name) == 0)
         {
             fat16_reset_dir(parent);
-            return 1;
+            return 0;
         }
     }
 
+    struct fat16_fs_struct* fs = parent->fs;
+
     memset(dir_entry, 0, sizeof(*dir_entry));
     strncpy(dir_entry->long_name, file, sizeof(dir_entry->long_name) - 1);
 
-    /* search for a place where to write the directory entry to disk */
-    uint8_t free_dir_entries_needed = strlen(file) / 13 + 1 + 1;
-    uint8_t free_dir_entries_found = 0;
-    struct fat16_fs_struct* fs = parent->fs;
-    uint16_t cluster_num = parent->dir_entry.cluster;
-    uint32_t dir_entry_offset = 0;
-    uint32_t offset = 0;
-    uint32_t offset_to = 0;
-
-    if(cluster_num == 0)
-    {
-        /* 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;
-    }
+    /* find place where to store directory entry */
+    if(!(dir_entry->entry_offset = fat16_find_offset_for_dir_entry(fs, parent, dir_entry)))
+        return 0;
     
-    while(1)
-    {
-        if(offset == offset_to)
-        {
-            if(cluster_num == 0)
-                /* We iterated through the whole root directory entry
-                 * 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.
-                 */
-
-                uint16_t cluster_next = fat16_get_next_cluster(fs, cluster_num);
-                if(!cluster_next)
-                {
-                    cluster_next = fat16_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 +
-                                       (uint32_t) (cluster_next - 2) * fs->header.cluster_size;
-
-                    /* TODO: This cluster has to be zeroed in an efficient way, or at least
-                     *       every 32th byte should be set to FAT16_DIRENTRY_DELETED.
-                     */
-                    break;
-                }
-                cluster_num = cluster_next;
-            }
-
-            offset = fs->header.cluster_zero_offset +
-                     (uint32_t) (cluster_num - 2) * fs->header.cluster_size;
-            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 == FAT16_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;
-        }
-    }
-    
     /* write directory entry to disk */
-    dir_entry->entry_offset = dir_entry_offset;
     if(!fat16_write_dir_entry(fs, dir_entry))
         return 0;
     
@@ -1695,7 +1837,6 @@
  * \ingroup fat16_file
  * Deletes a file or directory.
  *
- * It is not checked if the file to delete is a 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
@@ -1748,6 +1889,121 @@
 }
 
 /**
+ * \ingroup fat16_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 fat16_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 fat16_delete_dir
+ */
+uint8_t fat16_create_dir(struct fat16_dir_struct* parent, const char* dir, struct fat16_dir_entry_struct* dir_entry)
+{
+#if FAT16_WRITE_SUPPORT
+    if(!parent || !dir || !dir[0] || !dir_entry)
+        return 0;
+
+    /* check if the file or directory already exists */
+    while(1)
+    {
+        if(!fat16_read_dir(parent, dir_entry))
+            break;
+
+        if(strcmp(dir, dir_entry->long_name) == 0)
+        {
+            fat16_reset_dir(parent);
+            return 0;
+        }
+    }
+
+    struct fat16_fs_struct* fs = parent->fs;
+
+    /* allocate cluster which will hold directory entries */
+    uint16_t dir_cluster = fat16_append_clusters(fs, 0, 1);
+    if(!dir_cluster)
+        return 0;
+
+    /* clear cluster to prevent bogus directory entries */
+    fat16_clear_cluster(fs, dir_entry->cluster);
+    
+    memset(dir_entry, 0, sizeof(*dir_entry));
+    dir_entry->attributes = FAT16_ATTRIB_DIR;
+
+    /* create "." directory self reference */
+    dir_entry->entry_offset = fs->header.cluster_zero_offset +
+                              (uint32_t) (dir_cluster - 2) * fs->header.cluster_size;
+    dir_entry->long_name[0] = '.';
+    dir_entry->cluster = dir_cluster;
+    if(!fat16_write_dir_entry(fs, dir_entry))
+    {
+        fat16_free_clusters(fs, dir_cluster);
+        return 0;
+    }
+
+    /* create ".." parent directory reference */
+    dir_entry->entry_offset += 2 * 32;
+    dir_entry->long_name[1] = '.';
+    dir_entry->cluster = parent->dir_entry.cluster;
+    if(!fat16_write_dir_entry(fs, dir_entry))
+    {
+        fat16_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 = fat16_find_offset_for_dir_entry(fs, parent, dir_entry)))
+    {
+        fat16_free_clusters(fs, dir_cluster);
+        return 0;
+    }
+
+    /* write directory to disk */
+    if(!fat16_write_dir_entry(fs, dir_entry))
+    {
+        fat16_free_clusters(fs, dir_cluster);
+        return 0;
+    }
+
+    return 1;
+    
+#else
+    return 0;
+#endif
+}
+
+/**
+ * \ingroup fat16_dir
+ * Deletes a directory.
+ *
+ * This is just a synonym for fat16_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 fat16_create_dir
+ */
+#ifdef DOXYGEN
+uint8_t fat16_delete_dir(struct fat16_fs_struct* fs, struct fat16_dir_entry_struct* dir_entry);
+#endif
+
+/**
  * \ingroup fat16_fs
  * Returns the amount of total storage capacity of the filesystem in bytes.
  *
Index: sd_raw.h
===================================================================
--- sd_raw.h	(.../sd-reader_release_20060901)	(Revision 106)
+++ sd_raw.h	(.../sd-reader_release_20061101)	(Revision 106)
@@ -112,15 +112,17 @@
     uint8_t format;
 };
 
-typedef uint8_t (*sd_raw_interval_handler)(uint8_t* buffer, uint32_t offset, void* p);
+typedef uint8_t (*sd_raw_read_interval_handler_t)(uint8_t* buffer, uint32_t offset, void* p);
+typedef uint16_t (*sd_raw_write_interval_handler_t)(uint8_t* buffer, uint32_t offset, void* p);
 
 uint8_t sd_raw_init();
 uint8_t sd_raw_available();
 uint8_t sd_raw_locked();
 
 uint8_t sd_raw_read(uint32_t offset, uint8_t* buffer, uint16_t length);
-uint8_t sd_raw_read_interval(uint32_t offset, uint8_t* buffer, uint16_t interval, uint16_t length, sd_raw_interval_handler callback, void* p);
+uint8_t sd_raw_read_interval(uint32_t offset, uint8_t* buffer, uint16_t interval, uint16_t length, sd_raw_read_interval_handler_t callback, void* p);
 uint8_t sd_raw_write(uint32_t offset, const uint8_t* buffer, uint16_t length);
+uint8_t sd_raw_write_interval(uint32_t offset, uint8_t* buffer, uint16_t length, sd_raw_write_interval_handler_t callback, void* p);
 uint8_t sd_raw_sync();
 
 uint8_t sd_raw_get_info(struct sd_raw_info* info);
Index: main.c
===================================================================
--- main.c	(.../sd-reader_release_20060901)	(Revision 106)
+++ main.c	(.../sd-reader_release_20061101)	(Revision 106)
@@ -4,8 +4,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 #include <avr/pgmspace.h>
 #include <avr/sleep.h>
@@ -63,6 +61,8 @@
  *   Shows card manufacturer, status, filesystem capacity and free storage space.
  * - <tt>ls</tt>\n
  *   Shows the content of the current directory.
+ * - <tt>mkdir \<directory\></tt>\n
+ *   Creates a directory called \<directory\>.
  * - <tt>rm \<file\></tt>\n
  *   Deletes \<file\>.
  * - <tt>sync</tt>\n
@@ -75,7 +75,7 @@
  *
  * \htmlonly
  * <p>
- * The following table shows some typical code sizes in bytes:
+ * The following table shows some typical code sizes in bytes, using malloc()/free():
  * </p>
  *
  * <table border="1" cellpadding="2">
@@ -86,27 +86,27 @@
  *     </tr>
  *     <tr>
  *         <td>MMC/SD (read-only)</td>
- *         <td align="right">1570</td>
+ *         <td align="right">1576</td>
  *         <td align="right">0</td>
  *     </tr>
  *     <tr>
  *         <td>MMC/SD (read-write)</td>
- *         <td align="right">2022</td>
+ *         <td align="right">2202</td>
  *         <td align="right">517</td>
  *     </tr>
  *     <tr>
  *         <td>Partition</td>
- *         <td align="right">408</td>
+ *         <td align="right">418</td>
  *         <td align="right">0</td>
  *     </tr>
  *     <tr>
  *         <td>FAT16 (read-only)</td>
- *         <td align="right">3828</td>
+ *         <td align="right">3834</td>
  *         <td align="right">0</td>
  *     </tr>
  *     <tr>
  *         <td>FAT16 (read-write)</td>
- *         <td align="right">7298</td>
+ *         <td align="right">7932</td>
  *         <td align="right">0</td>
  *     </tr>
  * </table>
@@ -124,17 +124,18 @@
  * 
  * <p>
  * When opening a partition, filesystem, file or directory, a little amount
- * of dynamic RAM is used, as listed in the following table.
+ * of dynamic RAM is used, as listed in the following table. Alternatively,
+ * the same amount of static RAM can be used.
  * </p>
  *
  * <table border="1" cellpadding="2">
  *     <tr>
  *         <th>descriptor</th>
- *         <th>dynamic RAM</th>
+ *         <th>dynamic/static RAM</th>
  *     </tr>
  *     <tr>
  *         <td>partition</td>
- *         <td align="right">15</td>
+ *         <td align="right">17</td>
  *     </tr>
  *     <tr>
  *         <td>filesystem</td>
@@ -159,9 +160,10 @@
  * 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 disable
- * write support completely by changing the corresponding defines in the
- * sd_raw_config.h and fat16_config.h files.
+ * microcontrollers or different clock speeds. You might also want to change
+ * the configuration defines in the files fat16_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.
  * 
  * \section bugs Bugs or comments?
  * If you have comments or found a bug in the software - there might be some
@@ -180,6 +182,7 @@
  */
 
 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 fat16_fs_struct* fs, struct fat16_dir_struct* dd, const char* name, struct fat16_dir_entry_struct* dir_entry);
 static struct fat16_file_struct* open_file_in_dir(struct fat16_fs_struct* fs, struct fat16_dir_struct* dd, const char* name); 
 static uint8_t print_disk_info(const struct fat16_fs_struct* fs);
@@ -191,14 +194,12 @@
 
     /* setup uart */
     uart_init();
-    /* setup stdio */
-    uart_connect_stdio();
 
     /* setup sd card slot */
     if(!sd_raw_init())
     {
 #if DEBUG
-        printf_P(PSTR("MMC/SD initialization failed\n"));
+        uart_puts_p(PSTR("MMC/SD initialization failed\n"));
 #endif
         return 1;
     }
@@ -207,7 +208,9 @@
     struct partition_struct* partition = partition_open(sd_raw_read,
                                                         sd_raw_read_interval,
                                                         sd_raw_write,
-                                                        0);
+                                                        sd_raw_write_interval,
+                                                        0
+                                                       );
 
     if(!partition)
     {
@@ -217,12 +220,13 @@
         partition = partition_open(sd_raw_read,
                                    sd_raw_read_interval,
                                    sd_raw_write,
+                                   sd_raw_write_interval,
                                    -1
                                   );
         if(!partition)
         {
 #if DEBUG
-            printf_P(PSTR("opening partition failed\n"));
+            uart_puts_p(PSTR("opening partition failed\n"));
 #endif
             return 1;
         }
@@ -233,7 +237,7 @@
     if(!fs)
     {
 #if DEBUG
-        printf_P(PSTR("opening filesystem failed\n"));
+        uart_puts_p(PSTR("opening filesystem failed\n"));
 #endif
         return 1;
     }
@@ -246,7 +250,7 @@
     if(!dd)
     {
 #if DEBUG
-        printf_P(PSTR("opening root directory failed\n"));
+        uart_puts_p(PSTR("opening root directory failed\n"));
 #endif
         return 1;
     }
@@ -287,7 +291,9 @@
                 }
             }
 
-            printf_P(PSTR("directory not found: %s\n"), command);
+            uart_puts_p(PSTR("directory not found: "));
+            uart_puts(command);
+            uart_putc('\n');
         }
         else if(strcmp_P(command, PSTR("ls")) == 0)
         {
@@ -295,11 +301,14 @@
             struct fat16_dir_entry_struct dir_entry;
             while(fat16_read_dir(dd, &dir_entry))
             {
-                printf_P(PSTR("%10lu  %s%c\n"),
-                         dir_entry.file_size,
-                         dir_entry.long_name,
-                         (dir_entry.attributes & FAT16_ATTRIB_DIR) ? '/' : ' '
-                        );
+                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 & FAT16_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)
@@ -312,7 +321,9 @@
             struct fat16_file_struct* fd = open_file_in_dir(fs, dd, command);
             if(!fd)
             {
-                printf_P(PSTR("error opening %s\n"), command);
+                uart_puts_p(PSTR("error opening "));
+                uart_puts(command);
+                uart_putc('\n');
                 continue;
             }
 
@@ -321,17 +332,14 @@
             uint32_t offset = 0;
             while(fat16_read_file(fd, buffer, sizeof(buffer)) > 0)
             {
-                printf_P(PSTR("%08lx: %02x %02x %02x %02x %02x %02x %02x %02x\n"),
-                         offset,
-                         buffer[0],
-                         buffer[1],
-                         buffer[2],
-                         buffer[3],
-                         buffer[4],
-                         buffer[5],
-                         buffer[6],
-                         buffer[7]
-                        );
+                uart_putdw_hex(offset);
+                uart_putc(':');
+                for(uint8_t i = 0; i < 8; ++i)
+                {
+                    uart_putc(' ');
+                    uart_putc_hex(buffer[i]);
+                }
+                uart_putc('\n');
                 offset += 8;
             }
 
@@ -340,7 +348,7 @@
         else if(strcmp_P(command, PSTR("disk")) == 0)
         {
             if(!print_disk_info(fs))
-                printf_P(PSTR("error reading disk info\n"));
+                uart_puts_p(PSTR("error reading disk info\n"));
         }
 #if FAT16_WRITE_SUPPORT
         else if(strncmp_P(command, PSTR("rm "), 3) == 0)
@@ -356,7 +364,9 @@
                     continue;
             }
 
-            printf_P(PSTR("error deleting file: %s\n"), command);
+            uart_puts_p(PSTR("error deleting file: "));
+            uart_puts(command);
+            uart_putc('\n');
         }
         else if(strncmp_P(command, PSTR("touch "), 6) == 0)
         {
@@ -366,7 +376,11 @@
 
             struct fat16_dir_entry_struct file_entry;
             if(!fat16_create_file(dd, command, &file_entry))
-                printf_P(PSTR("error creating file: %s\n"), command);
+            {
+                uart_puts_p(PSTR("error creating file: "));
+                uart_puts(command);
+                uart_putc('\n');
+            }
         }
         else if(strncmp_P(command, PSTR("write "), 6) == 0)
         {
@@ -387,14 +401,19 @@
             struct fat16_file_struct* fd = open_file_in_dir(fs, dd, command);
             if(!fd)
             {
-                printf_P(PSTR("error opening %s\n"), command);
+                uart_puts_p(PSTR("error opening "));
+                uart_puts(command);
+                uart_putc('\n');
                 continue;
             }
 
-            int32_t offset = strtol(offset_value, 0, 0);
+            int32_t offset = strtolong(offset_value);
             if(!fat16_seek_file(fd, &offset, FAT16_SEEK_SET))
             {
-                printf_P(PSTR("error seeking on %s\n"), command);
+                uart_puts_p(PSTR("error seeking on "));
+                uart_puts(command);
+                uart_putc('\n');
+
                 fat16_close_file(fd);
                 continue;
             }
@@ -415,24 +434,40 @@
                 /* write text to file */
                 if(fat16_write_file(fd, (uint8_t*) buffer, data_len) != data_len)
                 {
-                    printf_P(PSTR("error writing to file\n"));
+                    uart_puts_p(PSTR("error writing to file\n"));
                     break;
                 }
             }
 
             fat16_close_file(fd);
         }
+        else if(strncmp_P(command, PSTR("mkdir "), 6) == 0)
+        {
+            command += 6;
+            if(command[0] == '\0')
+                continue;
+
+            struct fat16_dir_entry_struct dir_entry;
+            if(!fat16_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())
-                printf_P(PSTR("error syncing disk\n"));
+                uart_puts_p(PSTR("error syncing disk\n"));
         }
 #endif
         else
         {
-            printf_P(PSTR("unknown command: %s\n"), command);
+            uart_puts_p(PSTR("unknown command: "));
+            uart_puts(command);
+            uart_putc('\n');
         }
     }
 
@@ -486,6 +521,15 @@
     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 fat16_fs_struct* fs, struct fat16_dir_struct* dd, const char* name, struct fat16_dir_entry_struct* dir_entry)
 {
     while(fat16_read_dir(dd, dir_entry))
@@ -518,17 +562,20 @@
     if(!sd_raw_get_info(&disk_info))
         return 0;
 
-    printf_P(PSTR("manuf:  0x%02x\n"), disk_info.manufacturer);
-    printf_P(PSTR("oem:    %s\n"), disk_info.oem);
-    printf_P(PSTR("prod:   %s\n"), disk_info.product);
-    printf_P(PSTR("rev:    %02x\n"), disk_info.revision);
-    printf_P(PSTR("serial: 0x%08lx\n"), disk_info.serial);
-    printf_P(PSTR("date:   %02d/%02d\n"), disk_info.manufacturing_month, disk_info.manufacturing_year);
-    printf_P(PSTR("size:   %ld\n"), disk_info.capacity);
-    printf_P(PSTR("copy:   %d\n"), disk_info.flag_copy);
-    printf_P(PSTR("wr.pr.: %d/%d\n"), disk_info.flag_write_protect_temp, disk_info.flag_write_protect);
-    printf_P(PSTR("format: %d\n"), disk_info.format);
-    printf_P(PSTR("free:   %ld/%ld\n"), fat16_get_fs_free(fs), fat16_get_fs_size(fs));
+    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); uart_putc('\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(fat16_get_fs_free(fs)); uart_putc('/');
+                                   uart_putdw_dec(fat16_get_fs_size(fs)); uart_putc('\n');
 
     return 1;
 }
Index: fat16.h
===================================================================
--- fat16.h	(.../sd-reader_release_20060901)	(Revision 106)
+++ fat16.h	(.../sd-reader_release_20061101)	(Revision 106)
@@ -90,6 +90,8 @@
 
 uint8_t fat16_create_file(struct fat16_dir_struct* parent, const char* file, struct fat16_dir_entry_struct* dir_entry);
 uint8_t fat16_delete_file(struct fat16_fs_struct* fs, struct fat16_dir_entry_struct* dir_entry);
+uint8_t fat16_create_dir(struct fat16_dir_struct* parent, const char* dir, struct fat16_dir_entry_struct* dir_entry);
+#define fat16_delete_dir fat16_delete_file
 
 uint8_t fat16_get_dir_entry_of_path(struct fat16_fs_struct* fs, const char* path, struct fat16_dir_entry_struct* dir_entry);
 
