/* * Milkymist SoC (Software) * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include //#define DEBUG #define BLOCK_SIZE 512 struct partition_descriptor { unsigned char flags; unsigned char start_head; unsigned short start_cylinder; unsigned char type; unsigned char end_head; unsigned short end_cylinder; unsigned int start_sector; unsigned int end_sector; } __attribute__((packed)); struct firstsector { unsigned char bootsector[446]; struct partition_descriptor partitions[4]; unsigned char signature[2]; } __attribute__((packed)); struct fat16_firstsector { /* Common to FATxx */ char jmp[3]; char oem[8]; unsigned short bytes_per_sector; unsigned char sectors_per_cluster; unsigned short reserved_sectors; unsigned char number_of_fat; unsigned short max_root_entries; unsigned short total_sectors_short; unsigned char media_descriptor; unsigned short sectors_per_fat; unsigned short sectors_per_track; unsigned short head_count; unsigned int hidden_sectors; unsigned int total_sectors; /* FAT16 specific */ unsigned char drive_nr; unsigned char reserved; unsigned char ext_boot_signature; unsigned int id; unsigned char volume_label[11]; unsigned char fstype[8]; unsigned char bootcode[448]; unsigned char signature[2]; } __attribute__((packed)); struct directory_entry { unsigned char filename[8]; unsigned char extension[3]; unsigned char attributes; unsigned char reserved; unsigned char create_time_ms; unsigned short create_time; unsigned short create_date; unsigned short last_access; unsigned short ea_index; unsigned short lastm_time; unsigned short lastm_date; unsigned short first_cluster; unsigned int file_size; } __attribute__((packed)); struct directory_entry_lfn { unsigned char seq; unsigned short name1[5]; /* UTF16 */ unsigned char attributes; unsigned char reserved; unsigned char checksum; unsigned short name2[6]; unsigned short first_cluster; unsigned short name3[2]; } __attribute__((packed)); #define PARTITION_TYPE_FAT16 0x06 #define PARTITION_TYPE_FAT32 0x0b static int fatfs_partition_start_sector; /* Sector# of the beginning of the FAT16 partition */ static int fatfs_sectors_per_cluster; static int fatfs_fat_sector; /* Sector of the first FAT */ static int fatfs_fat_entries; /* Number of entries in the FAT */ static int fatfs_max_root_entries; static int fatfs_root_table_sector; /* Sector# of the beginning of the root table */ static int fatfs_fat_cached_sector; static unsigned short int fatfs_fat_sector_cache[BLOCK_SIZE/2]; static int fatfs_dir_cached_sector; static struct directory_entry fatfs_dir_sector_cache[BLOCK_SIZE/sizeof(struct directory_entry)]; static int fatfs_data_start_sector; int fatfs_init(int devnr) { struct firstsector s0; struct fat16_firstsector s; int i; if(!bd_init(devnr)) { printf("E: Unable to initialize memory card driver\n"); return 0; } if(bd_has_part_table(devnr)) { /* Read sector 0, with partition table */ if(!bd_readblock(0, (void *)&s0)) { printf("E: Unable to read block 0\n"); return 0; } fatfs_partition_start_sector = -1; for(i=0;i<4;i++) if((s0.partitions[i].type == PARTITION_TYPE_FAT16) ||(s0.partitions[i].type == PARTITION_TYPE_FAT32)) { #ifdef DEBUG printf("I: Using partition #%d: start sector %08x, end sector %08x\n", i, le32toh(s0.partitions[i].start_sector), le32toh(s0.partitions[i].end_sector)); #endif fatfs_partition_start_sector = le32toh(s0.partitions[i].start_sector); break; } if(fatfs_partition_start_sector == -1) { printf("E: No FAT partition was found\n"); return 0; } } else fatfs_partition_start_sector = 0; /* Read first FAT16 sector */ if(!bd_readblock(fatfs_partition_start_sector, (void *)&s)) { printf("E: Unable to read first FAT sector\n"); return 0; } #ifdef DEBUG { char oem[9]; char volume_label[12]; memcpy(oem, s.oem, 8); oem[8] = 0; memcpy(volume_label, s.volume_label, 11); volume_label[11] = 0; printf("I: OEM name: %s\n", oem); printf("I: Volume label: %s\n", volume_label); } #endif if(le16toh(s.bytes_per_sector) != BLOCK_SIZE) { printf("E: Unexpected number of bytes per sector (%d)\n", le16toh(s.bytes_per_sector)); return 0; } fatfs_sectors_per_cluster = s.sectors_per_cluster; fatfs_fat_entries = (le16toh(s.sectors_per_fat)*BLOCK_SIZE)/2; fatfs_fat_sector = fatfs_partition_start_sector + 1; fatfs_fat_cached_sector = -1; fatfs_max_root_entries = le16toh(s.max_root_entries); fatfs_root_table_sector = fatfs_fat_sector + s.number_of_fat*le16toh(s.sectors_per_fat); fatfs_dir_cached_sector = -1; fatfs_data_start_sector = fatfs_root_table_sector + (fatfs_max_root_entries*sizeof(struct directory_entry))/BLOCK_SIZE; if(fatfs_max_root_entries == 0) { printf("E: Your memory card uses FAT32, which is not supported.\n"); printf("E: Please reformat your card using FAT16, e.g. use mkdosfs -F 16\n"); printf("E: FAT32 support would be an appreciated contribution.\n"); return 0; } #ifdef DEBUG printf("I: Cluster is %d sectors, FAT has %d entries, FAT 1 is at sector %d,\nI: root table is at sector %d (max %d), data is at sector %d, nb of fat: %d\n", fatfs_sectors_per_cluster, fatfs_fat_entries, fatfs_fat_sector, fatfs_root_table_sector, fatfs_max_root_entries, fatfs_data_start_sector, s.number_of_fat); #endif return 1; } static int fatfs_read_fat(int offset) { int wanted_sector; if((offset < 0) || (offset >= fatfs_fat_entries)) { printf("E: Incorrect offset %d in fatfs_read_fat\n", offset); return -1; } wanted_sector = fatfs_fat_sector + (offset*2)/BLOCK_SIZE; if(wanted_sector != fatfs_fat_cached_sector) { if(!bd_readblock(wanted_sector, (void *)&fatfs_fat_sector_cache)) { printf("E: Memory card failed (FAT), sector %d\n", wanted_sector); return -1; } fatfs_fat_cached_sector = wanted_sector; } return le16toh(fatfs_fat_sector_cache[offset % (BLOCK_SIZE/2)]); } static const struct directory_entry *fatfs_read_root_directory(int offset) { int wanted_sector; if((offset < 0) || (offset >= fatfs_max_root_entries)) return NULL; wanted_sector = fatfs_root_table_sector + (offset*sizeof(struct directory_entry))/BLOCK_SIZE; if(wanted_sector != fatfs_dir_cached_sector) { if(!bd_readblock(wanted_sector, (void *)&fatfs_dir_sector_cache)) { printf("E: Memory card failed (Rootdir), sector %d\n", wanted_sector); return NULL; } fatfs_dir_cached_sector = wanted_sector; } return &fatfs_dir_sector_cache[offset % (BLOCK_SIZE/sizeof(struct directory_entry))]; } static void lfn_to_ascii(const struct directory_entry_lfn *entry, char *name, int terminate) { int i; unsigned short c; for(i=0;i<5;i++) { c = le16toh(entry->name1[i]); if(c <= 255) { *name = c; name++; if(c == 0) return; } } for(i=0;i<6;i++) { c = le16toh(entry->name2[i]); if(c <= 255) { *name = c; name++; if(c == 0) return; } } for(i=0;i<2;i++) { c = le16toh(entry->name3[i]); if(c <= 255) { *name = c; name++; if(c == 0) return; } } if(terminate) *name = 0; } static int fatfs_is_regular(const struct directory_entry *entry) { return ((entry->attributes & 0x10) == 0) && ((entry->attributes & 0x08) == 0) && (entry->filename[0] != 0xe5); } int fatfs_list_files(fatfs_dir_callback cb, void *param) { const struct directory_entry *entry; char fmtbuf[8+1+3+1]; char longname[131]; int has_longname; int i, j, k; has_longname = 0; longname[sizeof(longname)-1] = 0; /* avoid crashing when reading a corrupt FS */ for(k=0;kattributes); #endif if(entry->attributes == 0x0f) { const struct directory_entry_lfn *entry_lfn; unsigned char frag; int terminate; entry_lfn = (const struct directory_entry_lfn *)entry; frag = entry_lfn->seq & 0x3f; terminate = entry_lfn->seq & 0x40; if(frag*13 < sizeof(longname)) { lfn_to_ascii((const struct directory_entry_lfn *)entry, &longname[(frag-1)*13], terminate); if(frag == 1) has_longname = 1; } continue; } else { if(!fatfs_is_regular(entry)) { has_longname = 0; continue; } } if(entry == NULL) return 0; if(entry->filename[0] == 0) { has_longname = 0; break; } j = 0; for(i=0;i<8;i++) { if(entry->filename[i] == ' ') break; fmtbuf[j++] = entry->filename[i]; } fmtbuf[j++] = '.'; for(i=0;i<3;i++) { if(entry->extension[i] == ' ') break; fmtbuf[j++] = entry->extension[i]; } fmtbuf[j++] = 0; if(!cb(fmtbuf, has_longname ? longname : fmtbuf, param)) return 0; has_longname = 0; } return 1; } static const struct directory_entry *fatfs_find_file_by_name(const char *filename) { char searched_filename[8]; char searched_extension[3]; char *dot; const char *c; int i; const struct directory_entry *entry; dot = strrchr(filename, '.'); if(dot == NULL) return NULL; memset(searched_filename, ' ', 8); memset(searched_extension, ' ', 3); i = 0; for(c=filename;cfilename[0] == 0) break; if(!fatfs_is_regular(entry)) continue; if(!memcmp(searched_filename, entry->filename, 8) &&!memcmp(searched_extension, entry->extension, 3)) return entry; } return NULL; } static int fatfs_load_cluster(int clustern, char *buffer, int maxsectors) { int startsector; int i; int toread; clustern = clustern - 2; startsector = fatfs_data_start_sector + clustern*fatfs_sectors_per_cluster; if(maxsectors < fatfs_sectors_per_cluster) toread = maxsectors; else toread = fatfs_sectors_per_cluster; for(i=0;ifile_size); n = 0; cluster = le16toh(entry->first_cluster); while(size > 0) { if(!fatfs_load_cluster(cluster, buffer+n*cluster_size, size)) return 0; size -= fatfs_sectors_per_cluster; n++; cluster = fatfs_read_fat(cluster); if(cluster >= 0xFFF8) break; if(cluster == -1) return 0; } //putsnonl("\n"); return n*cluster_size; } void fatfs_done(void) { bd_done(); }