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