#include "partition.h"
#include "sd_raw.h"
#include "sd_raw_config.h"
-#include "uart.h"
-#define DEBUG 1
+#include <uart.h>
+#include <stdio.h>
+#include <aversive/error.h>
+#include "cmdline.h"
+
+
+#define SD_DEBUG 1
/**
* \mainpage MMC/SD/SDHC card library
* 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.
*
* 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."
* - <tt>mkdir \<directory\></tt>\n
* Creates a directory called \<directory\>.
* - <tt>mv \<file\> \<file_new\></tt>\n
- * Renames \<file\> to \<file_new\>.
+ * Renames \<file\> to \<file_new\>.
* - <tt>rm \<file\></tt>\n
* Deletes \<file\>.
* - <tt>sync</tt>\n
* 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
* <td align="right">49</td>
* </tr>
* </table>
- *
+ *
* \endhtmlonly
*
* \section adaptation Adapting the software to your needs
*
* 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.
* 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
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 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()
+int sd_main(void)
{
/* 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"));
+ printf_P(PSTR("init\n"));
+ /* setup sd card slot */
+ if(!sd_raw_init())
+ {
+#if SD_DEBUG
+ printf_P(PSTR("MMC/SD initialization failed\n"));
#endif
- continue;
- }
+ continue;
+ }
- /* open first partition */
- struct partition_struct* partition = partition_open(sd_raw_read,
- sd_raw_read_interval,
+ /* 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,
+ sd_raw_write,
+ sd_raw_write_interval,
#else
- 0,
- 0,
+ 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,
+ 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,
+ sd_raw_write,
+ sd_raw_write_interval,
#else
- 0,
- 0,
+ 0,
+ 0,
#endif
- -1
- );
- if(!partition)
- {
-#if DEBUG
- uart_puts_p(PSTR("opening partition failed\n"));
+ -1
+ );
+ if(!partition)
+ {
+#if SD_DEBUG
+ printf_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"));
+ continue;
+ }
+ }
+
+ /* open file system */
+ struct fat_fs_struct* fs = fat_open(partition);
+ if(!fs)
+ {
+#if SD_DEBUG
+ printf_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"));
+ 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 SD_DEBUG
+ printf_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"));
- }
+ continue;
+ }
+
+ /* print some card information as a boot message */
+ print_disk_info(fs);
+
+ /* provide a simple shell */
+ char buffer[24];
+ while(1)
+ {
+ /* print prompt */
+ printf_P(PSTR("> "));
+
+ /* 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;
+ }
+ }
+
+ printf_P(PSTR("directory not found: %s\n"), command);
+ }
+ 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;
+
+ printf_P(PSTR("%s%c"), dir_entry.long_name,
+ dir_entry.attributes & FAT_ATTRIB_DIR ? '/' : ' ');
+ while(spaces--)
+ uart_send(CMDLINE_UART, ' ');
+ printf_P(PSTR("%d\n"), dir_entry.file_size);
+ }
+ }
+ 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)
+ {
+ printf_P(PSTR("error opening: %s\n"), command);
+ 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)
+ {
+ printf_P(PSTR("%"PRIu32":"), offset);
+ for(intptr_t i = 0; i < count; ++i)
+ {
+ printf_P(PSTR(" %x"), buffer[i]);
+ }
+ printf_P(PSTR("\n"));
+ offset += 8;
+ }
+
+ fat_close_file(fd);
+ }
+ else if(strcmp_P(command, PSTR("disk")) == 0)
+ {
+ if(!print_disk_info(fs))
+ printf_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');
- }
- }
+ 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;
+ }
+
+ printf_P(PSTR("error deleting file: %s\n"), command);
+ }
+ 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))
+ {
+ printf_P(PSTR("error creating file: %s\n"), command);
+ }
+ }
+ 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;
+ }
+
+ printf_P(PSTR("error moving file: %s\n"), command);
+ }
+ 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)
+ {
+ printf_P(PSTR("error opening %s\n"), command);
+ continue;
+ }
+
+ int32_t offset = strtolong(offset_value);
+ if(!fat_seek_file(fd, &offset, FAT_SEEK_SET))
+ {
+ printf_P(PSTR("error seeking on %s\n"), command);
+
+ 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 */
+ printf_P(PSTR("< "));
+
+ /* 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)
+ {
+ printf_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))
+ {
+ printf_P(PSTR("error creating directory: %s\n"), command);
+ }
+ }
#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"));
- }
+ else if(strcmp_P(command, PSTR("sync")) == 0)
+ {
+ if(!sd_raw_sync())
+ printf_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);
+ else
+ {
+ printf_P(PSTR("unknown command: %s\n"), command);
+ }
+ }
+
+ /* close directory */
+ fat_close_dir(dd);
+
+ /* close file system */
+ fat_close(fs);
+
+ /* close partition */
+ partition_close(partition);
}
-
+
return 0;
}
+uint8_t uart_getc(void)
+{
+ uint8_t b = uart_recv(CMDLINE_UART);
+ if(b == '\r')
+ b = '\n';
+
+ return b;
+}
+
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;
- }
+ uint8_t c = uart_getc();
+
+ if(c == 0x08 || c == 0x7f)
+ {
+ if(read_length < 1)
+ continue;
+
+ --read_length;
+ buffer[read_length] = '\0';
+
+ uart_send(CMDLINE_UART, 0x08);
+ uart_send(CMDLINE_UART, ' ');
+ uart_send(CMDLINE_UART, 0x08);
+
+ continue;
+ }
+
+ uart_send(CMDLINE_UART, c);
+
+ if(c == '\n')
+ {
+ buffer[read_length] = '\0';
+ break;
+ }
+ else
+ {
+ buffer[read_length] = c;
+ ++read_length;
+ }
}
return read_length;
{
uint32_t l = 0;
while(*str >= '0' && *str <= '9')
- l = l * 10 + (*str++ - '0');
+ 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)
{
+ (void)fs;
+
while(fat_read_dir(dd, dir_entry))
{
- if(strcmp(dir_entry->long_name, name) == 0)
- {
- fat_reset_dir(dd);
- return 1;
- }
+ if(strcmp(dir_entry->long_name, name) == 0)
+ {
+ fat_reset_dir(dd);
+ return 1;
+ }
}
return 0;
{
struct fat_dir_entry_struct file_entry;
if(!find_file_in_dir(fs, dd, name, &file_entry))
- return 0;
+ return 0;
return fat_open_file(fs, &file_entry);
}
uint8_t print_disk_info(const struct fat_fs_struct* fs)
{
if(!fs)
- return 0;
+ 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 0;
+
+ printf_P(PSTR("manuf: 0x%x\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: 0x%x\n"), disk_info.revision);
+ printf_P(PSTR("serial: 0x%x\n"), disk_info.serial);
+ printf_P(PSTR("date: 0x%d 0x%d\n"), disk_info.manufacturing_month,
+ disk_info.manufacturing_year);
+ printf_P(PSTR("size: %"PRIu32"\n"), disk_info.capacity / 1024 / 1024);
+ printf_P(PSTR("copy: 0x%x\n"), disk_info.flag_copy);
+ printf_P(PSTR("wr.pr.: 0x%x 0x%x\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: %d %d\n"), fat_get_fs_free(fs), fat_get_fs_size(fs));
return 1;
}