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