d2ee6c5afd8ce7641d68474ca59765c252f382c8
[protos/imu.git] / sd_main.c
1
2 /*
3  * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
4  *
5  * This file is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9
10 #include <string.h>
11 #include <avr/pgmspace.h>
12 #include <avr/sleep.h>
13 #include "fat.h"
14 #include "fat_config.h"
15 #include "partition.h"
16 #include "sd_raw.h"
17 #include "sd_raw_config.h"
18 #include "uart.h"
19
20 #define DEBUG 1
21
22 /**
23  * \mainpage MMC/SD/SDHC card library
24  *
25  * This project provides a general purpose library which implements read and write
26  * support for MMC, SD and SDHC memory cards.
27  *
28  * It includes
29  * - low-level \link sd_raw MMC, SD and SDHC read/write routines \endlink
30  * - \link partition partition table support \endlink
31  * - a simple \link fat FAT16/FAT32 read/write implementation \endlink
32  *
33  * \section circuit The circuit
34  * The circuit which was mainly used during development consists of an Atmel AVR
35  * microcontroller with some passive components. It is quite simple and provides
36  * an easy test environment. The circuit which can be downloaded on the
37  * <a href="http://www.roland-riegel.de/sd-reader/">project homepage</a> has been
38  * improved with regard to operation stability.
39  *
40  * I used different microcontrollers during development, the ATmega8 with 8kBytes
41  * of flash, and its pin-compatible alternative, the ATmega168 with 16kBytes flash.
42  * The first one is the one I started with, but when I implemented FAT16 write
43  * support, I ran out of flash space and switched to the ATmega168. For FAT32, an
44  * ATmega328 is required.
45  *
46  * The circuit board is a self-made and self-soldered board consisting of a single
47  * copper layer and standard DIL components, except of the MMC/SD card connector.
48  *
49  * The connector is soldered to the bottom side of the board. It has a simple
50  * eject button which, when a card is inserted, needs some space beyond the connector
51  * itself. As an additional feature the connector has two electrical switches
52  * to detect wether a card is inserted and wether this card is write-protected.
53  *
54  * \section pictures Pictures
55  * \image html pic01.jpg "The circuit board used to implement and test this application."
56  * \image html pic02.jpg "The MMC/SD card connector on the soldering side of the circuit board."
57  *
58  * \section software The software
59  * The software is written in C (ISO C99). It might not be the smallest or
60  * the fastest one, but I think it is quite flexible. See the project's
61  * <a href="http://www.roland-riegel.de/sd-reader/benchmarks/">benchmark page</a> to get an
62  * idea of the possible data rates.
63  *
64  * I implemented an example application providing a simple command prompt which is accessible
65  * via the UART at 9600 Baud. With commands similiar to the Unix shell you can browse different
66  * directories, read and write files, create new ones and delete them again. Not all commands are
67  * available in all software configurations.
68  * - <tt>cat \<file\></tt>\n
69  *   Writes a hexdump of \<file\> to the terminal.
70  * - <tt>cd \<directory\></tt>\n
71  *   Changes current working directory to \<directory\>.
72  * - <tt>disk</tt>\n
73  *   Shows card manufacturer, status, filesystem capacity and free storage space.
74  * - <tt>init</tt>\n
75  *   Reinitializes and reopens the memory card.
76  * - <tt>ls</tt>\n
77  *   Shows the content of the current directory.
78  * - <tt>mkdir \<directory\></tt>\n
79  *   Creates a directory called \<directory\>.
80  * - <tt>mv \<file\> \<file_new\></tt>\n
81  *   Renames \<file\> to \<file_new\>.
82  * - <tt>rm \<file\></tt>\n
83  *   Deletes \<file\>.
84  * - <tt>sync</tt>\n
85  *   Ensures all buffered data is written to the card.
86  * - <tt>touch \<file\></tt>\n
87  *   Creates \<file\>.
88  * - <tt>write \<file\> \<offset\></tt>\n
89  *   Writes text to \<file\>, starting from \<offset\>. The text is read
90  *   from the UART, line by line. Finish with an empty line.
91  *
92  * \htmlonly
93  * <p>
94  * The following table shows some typical code sizes in bytes, using the 20090330 release with a
95  * buffered read-write MMC/SD configuration, FAT16 and static memory allocation:
96  * </p>
97  *
98  * <table border="1" cellpadding="2">
99  *     <tr>
100  *         <th>layer</th>
101  *         <th>code size</th>
102  *         <th>static RAM usage</th>
103  *     </tr>
104  *     <tr>
105  *         <td>MMC/SD</td>
106  *         <td align="right">2410</td>
107  *         <td align="right">518</td>
108  *     </tr>
109  *     <tr>
110  *         <td>Partition</td>
111  *         <td align="right">456</td>
112  *         <td align="right">17</td>
113  *     </tr>
114  *     <tr>
115  *         <td>FAT16</td>
116  *         <td align="right">7928</td>
117  *         <td align="right">188</td>
118  *     </tr>
119  * </table>
120  *
121  * <p>
122  * The static RAM is mostly used for buffering memory card access, which
123  * improves performance and reduces implementation complexity.
124  * </p>
125  *
126  * <p>
127  * Please note that the numbers above do not include the C library functions
128  * used, e.g. some string functions. These will raise the numbers somewhat
129  * if they are not already used in other program parts.
130  * </p>
131  *
132  * <p>
133  * When opening a partition, filesystem, file or directory, a little amount
134  * of RAM is used, as listed in the following table. Depending on the library
135  * configuration, the memory is either allocated statically or dynamically.
136  * </p>
137  *
138  * <table border="1" cellpadding="2">
139  *     <tr>
140  *         <th>descriptor</th>
141  *         <th>dynamic/static RAM</th>
142  *     </tr>
143  *     <tr>
144  *         <td>partition</td>
145  *         <td align="right">17</td>
146  *     </tr>
147  *     <tr>
148  *         <td>filesystem</td>
149  *         <td align="right">26</td>
150  *     </tr>
151  *     <tr>
152  *         <td>file</td>
153  *         <td align="right">53</td>
154  *     </tr>
155  *     <tr>
156  *         <td>directory</td>
157  *         <td align="right">49</td>
158  *     </tr>
159  * </table>
160  *
161  * \endhtmlonly
162  *
163  * \section adaptation Adapting the software to your needs
164  * The only hardware dependent part is the communication layer talking to the
165  * memory card. The other parts like partition table and FAT support are
166  * completely independent, you could use them even for managing Compact Flash
167  * cards or standard ATAPI hard disks.
168  *
169  * By changing the MCU* variables in the Makefile, you can use other Atmel
170  * microcontrollers or different clock speeds. You might also want to change
171  * the configuration defines in the files fat_config.h, partition_config.h,
172  * sd_raw_config.h and sd-reader_config.h. For example, you could disable
173  * write support completely if you only need read support.
174  *
175  * For further information, visit the project's
176  * <a href="http://www.roland-riegel.de/sd-reader/faq/">FAQ page</a>.
177  *
178  * \section bugs Bugs or comments?
179  * If you have comments or found a bug in the software - there might be some
180  * of them - you may contact me per mail at feedback@roland-riegel.de.
181  *
182  * \section acknowledgements Acknowledgements
183  * Thanks go to Ulrich Radig, who explained on his homepage how to interface
184  * MMC cards to the Atmel microcontroller (http://www.ulrichradig.de/).
185  * I adapted his work for my circuit.
186  *
187  * \section copyright Copyright 2006-2012 by Roland Riegel
188  * This program is free software; you can redistribute it and/or modify it under
189  * the terms of the GNU General Public License version 2 as published by
190  * the Free Software Foundation (http://www.gnu.org/copyleft/gpl.html).
191  * At your option, you can alternatively redistribute and/or modify the following
192  * files under the terms of the GNU Lesser General Public License version 2.1
193  * as published by the Free Software Foundation (http://www.gnu.org/copyleft/lgpl.html):
194  * - byteordering.c
195  * - byteordering.h
196  * - fat.c
197  * - fat.h
198  * - fat_config.h
199  * - partition.c
200  * - partition.h
201  * - partition_config.h
202  * - sd_raw.c
203  * - sd_raw.h
204  * - sd_raw_config.h
205  * - sd-reader_config.h
206  */
207
208 static uint8_t read_line(char* buffer, uint8_t buffer_length);
209 static uint32_t strtolong(const char* str);
210 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);
211 static struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name);
212 static uint8_t print_disk_info(const struct fat_fs_struct* fs);
213
214 int main()
215 {
216     /* we will just use ordinary idle mode */
217     set_sleep_mode(SLEEP_MODE_IDLE);
218
219     /* setup uart */
220     uart_init();
221
222     while(1)
223     {
224         /* setup sd card slot */
225         if(!sd_raw_init())
226         {
227 #if DEBUG
228             uart_puts_p(PSTR("MMC/SD initialization failed\n"));
229 #endif
230             continue;
231         }
232
233         /* open first partition */
234         struct partition_struct* partition = partition_open(sd_raw_read,
235                                                             sd_raw_read_interval,
236 #if SD_RAW_WRITE_SUPPORT
237                                                             sd_raw_write,
238                                                             sd_raw_write_interval,
239 #else
240                                                             0,
241                                                             0,
242 #endif
243                                                             0
244                                                            );
245
246         if(!partition)
247         {
248             /* If the partition did not open, assume the storage device
249              * is a "superfloppy", i.e. has no MBR.
250              */
251             partition = partition_open(sd_raw_read,
252                                        sd_raw_read_interval,
253 #if SD_RAW_WRITE_SUPPORT
254                                        sd_raw_write,
255                                        sd_raw_write_interval,
256 #else
257                                        0,
258                                        0,
259 #endif
260                                        -1
261                                       );
262             if(!partition)
263             {
264 #if DEBUG
265                 uart_puts_p(PSTR("opening partition failed\n"));
266 #endif
267                 continue;
268             }
269         }
270
271         /* open file system */
272         struct fat_fs_struct* fs = fat_open(partition);
273         if(!fs)
274         {
275 #if DEBUG
276             uart_puts_p(PSTR("opening filesystem failed\n"));
277 #endif
278             continue;
279         }
280
281         /* open root directory */
282         struct fat_dir_entry_struct directory;
283         fat_get_dir_entry_of_path(fs, "/", &directory);
284
285         struct fat_dir_struct* dd = fat_open_dir(fs, &directory);
286         if(!dd)
287         {
288 #if DEBUG
289             uart_puts_p(PSTR("opening root directory failed\n"));
290 #endif
291             continue;
292         }
293
294         /* print some card information as a boot message */
295         print_disk_info(fs);
296
297         /* provide a simple shell */
298         char buffer[24];
299         while(1)
300         {
301             /* print prompt */
302             uart_putc('>');
303             uart_putc(' ');
304
305             /* read command */
306             char* command = buffer;
307             if(read_line(command, sizeof(buffer)) < 1)
308                 continue;
309
310             /* execute command */
311             if(strcmp_P(command, PSTR("init")) == 0)
312             {
313                 break;
314             }
315             else if(strncmp_P(command, PSTR("cd "), 3) == 0)
316             {
317                 command += 3;
318                 if(command[0] == '\0')
319                     continue;
320
321                 /* change directory */
322                 struct fat_dir_entry_struct subdir_entry;
323                 if(find_file_in_dir(fs, dd, command, &subdir_entry))
324                 {
325                     struct fat_dir_struct* dd_new = fat_open_dir(fs, &subdir_entry);
326                     if(dd_new)
327                     {
328                         fat_close_dir(dd);
329                         dd = dd_new;
330                         continue;
331                     }
332                 }
333
334                 uart_puts_p(PSTR("directory not found: "));
335                 uart_puts(command);
336                 uart_putc('\n');
337             }
338             else if(strcmp_P(command, PSTR("ls")) == 0)
339             {
340                 /* print directory listing */
341                 struct fat_dir_entry_struct dir_entry;
342                 while(fat_read_dir(dd, &dir_entry))
343                 {
344                     uint8_t spaces = sizeof(dir_entry.long_name) - strlen(dir_entry.long_name) + 4;
345
346                     uart_puts(dir_entry.long_name);
347                     uart_putc(dir_entry.attributes & FAT_ATTRIB_DIR ? '/' : ' ');
348                     while(spaces--)
349                         uart_putc(' ');
350                     uart_putdw_dec(dir_entry.file_size);
351                     uart_putc('\n');
352                 }
353             }
354             else if(strncmp_P(command, PSTR("cat "), 4) == 0)
355             {
356                 command += 4;
357                 if(command[0] == '\0')
358                     continue;
359
360                 /* search file in current directory and open it */
361                 struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
362                 if(!fd)
363                 {
364                     uart_puts_p(PSTR("error opening "));
365                     uart_puts(command);
366                     uart_putc('\n');
367                     continue;
368                 }
369
370                 /* print file contents */
371                 uint8_t buffer[8];
372                 uint32_t offset = 0;
373                 intptr_t count;
374                 while((count = fat_read_file(fd, buffer, sizeof(buffer))) > 0)
375                 {
376                     uart_putdw_hex(offset);
377                     uart_putc(':');
378                     for(intptr_t i = 0; i < count; ++i)
379                     {
380                         uart_putc(' ');
381                         uart_putc_hex(buffer[i]);
382                     }
383                     uart_putc('\n');
384                     offset += 8;
385                 }
386
387                 fat_close_file(fd);
388             }
389             else if(strcmp_P(command, PSTR("disk")) == 0)
390             {
391                 if(!print_disk_info(fs))
392                     uart_puts_p(PSTR("error reading disk info\n"));
393             }
394 #if FAT_WRITE_SUPPORT
395             else if(strncmp_P(command, PSTR("rm "), 3) == 0)
396             {
397                 command += 3;
398                 if(command[0] == '\0')
399                     continue;
400
401                 struct fat_dir_entry_struct file_entry;
402                 if(find_file_in_dir(fs, dd, command, &file_entry))
403                 {
404                     if(fat_delete_file(fs, &file_entry))
405                         continue;
406                 }
407
408                 uart_puts_p(PSTR("error deleting file: "));
409                 uart_puts(command);
410                 uart_putc('\n');
411             }
412             else if(strncmp_P(command, PSTR("touch "), 6) == 0)
413             {
414                 command += 6;
415                 if(command[0] == '\0')
416                     continue;
417
418                 struct fat_dir_entry_struct file_entry;
419                 if(!fat_create_file(dd, command, &file_entry))
420                 {
421                     uart_puts_p(PSTR("error creating file: "));
422                     uart_puts(command);
423                     uart_putc('\n');
424                 }
425             }
426             else if(strncmp_P(command, PSTR("mv "), 3) == 0)
427             {
428                 command += 3;
429                 if(command[0] == '\0')
430                     continue;
431
432                 char* target = command;
433                 while(*target != ' ' && *target != '\0')
434                     ++target;
435
436                 if(*target == ' ')
437                     *target++ = '\0';
438                 else
439                     continue;
440
441                 struct fat_dir_entry_struct file_entry;
442                 if(find_file_in_dir(fs, dd, command, &file_entry))
443                 {
444                     if(fat_move_file(fs, &file_entry, dd, target))
445                         continue;
446                 }
447
448                 uart_puts_p(PSTR("error moving file: "));
449                 uart_puts(command);
450                 uart_putc('\n');
451             }
452             else if(strncmp_P(command, PSTR("write "), 6) == 0)
453             {
454                 command += 6;
455                 if(command[0] == '\0')
456                     continue;
457
458                 char* offset_value = command;
459                 while(*offset_value != ' ' && *offset_value != '\0')
460                     ++offset_value;
461
462                 if(*offset_value == ' ')
463                     *offset_value++ = '\0';
464                 else
465                     continue;
466
467                 /* search file in current directory and open it */
468                 struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
469                 if(!fd)
470                 {
471                     uart_puts_p(PSTR("error opening "));
472                     uart_puts(command);
473                     uart_putc('\n');
474                     continue;
475                 }
476
477                 int32_t offset = strtolong(offset_value);
478                 if(!fat_seek_file(fd, &offset, FAT_SEEK_SET))
479                 {
480                     uart_puts_p(PSTR("error seeking on "));
481                     uart_puts(command);
482                     uart_putc('\n');
483
484                     fat_close_file(fd);
485                     continue;
486                 }
487
488                 /* read text from the shell and write it to the file */
489                 uint8_t data_len;
490                 while(1)
491                 {
492                     /* give a different prompt */
493                     uart_putc('<');
494                     uart_putc(' ');
495
496                     /* read one line of text */
497                     data_len = read_line(buffer, sizeof(buffer));
498                     if(!data_len)
499                         break;
500
501                     /* write text to file */
502                     if(fat_write_file(fd, (uint8_t*) buffer, data_len) != data_len)
503                     {
504                         uart_puts_p(PSTR("error writing to file\n"));
505                         break;
506                     }
507                 }
508
509                 fat_close_file(fd);
510             }
511             else if(strncmp_P(command, PSTR("mkdir "), 6) == 0)
512             {
513                 command += 6;
514                 if(command[0] == '\0')
515                     continue;
516
517                 struct fat_dir_entry_struct dir_entry;
518                 if(!fat_create_dir(dd, command, &dir_entry))
519                 {
520                     uart_puts_p(PSTR("error creating directory: "));
521                     uart_puts(command);
522                     uart_putc('\n');
523                 }
524             }
525 #endif
526 #if SD_RAW_WRITE_BUFFERING
527             else if(strcmp_P(command, PSTR("sync")) == 0)
528             {
529                 if(!sd_raw_sync())
530                     uart_puts_p(PSTR("error syncing disk\n"));
531             }
532 #endif
533             else
534             {
535                 uart_puts_p(PSTR("unknown command: "));
536                 uart_puts(command);
537                 uart_putc('\n');
538             }
539         }
540
541         /* close directory */
542         fat_close_dir(dd);
543
544         /* close file system */
545         fat_close(fs);
546
547         /* close partition */
548         partition_close(partition);
549     }
550
551     return 0;
552 }
553
554 uint8_t read_line(char* buffer, uint8_t buffer_length)
555 {
556     memset(buffer, 0, buffer_length);
557
558     uint8_t read_length = 0;
559     while(read_length < buffer_length - 1)
560     {
561         uint8_t c = uart_getc();
562
563         if(c == 0x08 || c == 0x7f)
564         {
565             if(read_length < 1)
566                 continue;
567
568             --read_length;
569             buffer[read_length] = '\0';
570
571             uart_putc(0x08);
572             uart_putc(' ');
573             uart_putc(0x08);
574
575             continue;
576         }
577
578         uart_putc(c);
579
580         if(c == '\n')
581         {
582             buffer[read_length] = '\0';
583             break;
584         }
585         else
586         {
587             buffer[read_length] = c;
588             ++read_length;
589         }
590     }
591
592     return read_length;
593 }
594
595 uint32_t strtolong(const char* str)
596 {
597     uint32_t l = 0;
598     while(*str >= '0' && *str <= '9')
599         l = l * 10 + (*str++ - '0');
600
601     return l;
602 }
603
604 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)
605 {
606         (void)fs;
607
608     while(fat_read_dir(dd, dir_entry))
609     {
610         if(strcmp(dir_entry->long_name, name) == 0)
611         {
612             fat_reset_dir(dd);
613             return 1;
614         }
615     }
616
617     return 0;
618 }
619
620 struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name)
621 {
622     struct fat_dir_entry_struct file_entry;
623     if(!find_file_in_dir(fs, dd, name, &file_entry))
624         return 0;
625
626     return fat_open_file(fs, &file_entry);
627 }
628
629 uint8_t print_disk_info(const struct fat_fs_struct* fs)
630 {
631     if(!fs)
632         return 0;
633
634     struct sd_raw_info disk_info;
635     if(!sd_raw_get_info(&disk_info))
636         return 0;
637
638     uart_puts_p(PSTR("manuf:  0x")); uart_putc_hex(disk_info.manufacturer); uart_putc('\n');
639     uart_puts_p(PSTR("oem:    ")); uart_puts((char*) disk_info.oem); uart_putc('\n');
640     uart_puts_p(PSTR("prod:   ")); uart_puts((char*) disk_info.product); uart_putc('\n');
641     uart_puts_p(PSTR("rev:    ")); uart_putc_hex(disk_info.revision); uart_putc('\n');
642     uart_puts_p(PSTR("serial: 0x")); uart_putdw_hex(disk_info.serial); uart_putc('\n');
643     uart_puts_p(PSTR("date:   ")); uart_putw_dec(disk_info.manufacturing_month); uart_putc('/');
644                                    uart_putw_dec(disk_info.manufacturing_year); uart_putc('\n');
645     uart_puts_p(PSTR("size:   ")); uart_putdw_dec(disk_info.capacity / 1024 / 1024); uart_puts_p(PSTR("MB\n"));
646     uart_puts_p(PSTR("copy:   ")); uart_putw_dec(disk_info.flag_copy); uart_putc('\n');
647     uart_puts_p(PSTR("wr.pr.: ")); uart_putw_dec(disk_info.flag_write_protect_temp); uart_putc('/');
648                                    uart_putw_dec(disk_info.flag_write_protect); uart_putc('\n');
649     uart_puts_p(PSTR("format: ")); uart_putw_dec(disk_info.format); uart_putc('\n');
650     uart_puts_p(PSTR("free:   ")); uart_putdw_dec(fat_get_fs_free(fs)); uart_putc('/');
651                                    uart_putdw_dec(fat_get_fs_size(fs)); uart_putc('\n');
652
653     return 1;
654 }
655
656 #if FAT_DATETIME_SUPPORT
657 void get_datetime(uint16_t* year, uint8_t* month, uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec)
658 {
659     *year = 2007;
660     *month = 1;
661     *day = 1;
662     *hour = 0;
663     *min = 0;
664     *sec = 0;
665 }
666 #endif
667
668