// Initialize the library - locates the first FAT16 partition, // loads the relevant part of its boot sector to calculate // values needed for operation, and finally positions the // file reading routines to the start of root directory entries char fat16_init() { char i; unsigned long root_start; fat16_seek(0x1BE); for(i=0; i<4; i++) { fat16_read(sizeof(PartitionTable)); if(FAT16_part->partition_type == 4 || FAT16_part->partition_type == 6 || FAT16_part->partition_type == 14) break; } if(i == 4) // none of the partitions were FAT16 return FAT16_ERR_NO_PARTITION_FOUND; fat16_state.fat_start = 512 * FAT16_part->start_sector; // temporary fat16_seek(fat16_state.fat_start + FAT16_BOOT_OFFSET); fat16_read(sizeof(Fat16BootSectorFragment)); if(FAT16_boot->sector_size != 512) return FAT16_ERR_INVALID_SECTOR_SIZE; fat16_state.fat_start += FAT16_boot->reserved_sectors * 512; root_start = fat16_state.fat_start + FAT16_boot->fat_size_sectors * FAT16_boot->number_of_fats * 512; fat16_state.data_start = root_start + FAT16_boot->root_dir_entries * sizeof(Fat16Entry); fat16_state.sectors_per_cluster = FAT16_boot->sectors_per_cluster; // Prepare for fat16_open_file(), cluster is not needed fat16_state.file_left = FAT16_boot->root_dir_entries * sizeof(Fat16Entry); fat16_state.cluster_left = 0xFFFFFFFF; // avoid FAT lookup with root dir #ifdef DEBUG printf("FAT start at %08X, root dir at %08X, data at %08X\n", fat16_state.fat_start, root_start, fat16_state.data_start); #endif fat16_seek(root_start); return 0; }
// Assumes we are at the beginning of a directory and fat16_state.file_left // is set to amount of file entries. Reads on until a given file is found, // or no more file entries are available. char fat16_open_file(char *filename, char *ext) { char i, bytes; #ifdef DEBUG printf("Trying to open file [%s.%s]\n", filename, ext); #endif do { bytes = fat16_read_file(sizeof(Fat16Entry)); if(bytes < sizeof(Fat16Entry)) return FAT16_ERR_FILE_READ; #ifdef DEBUG if(FAT16_entry->filename[0]) printf("Found file [%.8s.%.3s]\n", FAT16_entry->filename, FAT16_entry->ext); #endif for(i=0; i<8; i++) // we don't have memcmp on a MCU... if(FAT16_entry->filename[i] != filename[i]) break; if(i < 8) // not the filename we are looking for continue; for(i=0; i<3; i++) // we don't have memcmp on a MCU... if(FAT16_entry->ext[i] != ext[i]) break; if(i < 3) // not the extension we are looking for continue; #ifdef DEBUG printf("File found at cluster %d!\n", FAT16_entry->starting_cluster); #endif // Initialize reading variables fat16_state.cluster = FAT16_entry->starting_cluster; fat16_state.cluster_left = fat16_state.sectors_per_cluster * 512; if(FAT16_entry->filename[0] == 0x2E || FAT16_entry->attributes & 0x10) { // directory // set file size so large that the file entries // are not limited by it, but by the sectors used fat16_state.file_left = 0xFFFFFFFF; } else { fat16_state.file_left = FAT16_entry->file_size; } // Go to first cluster fat16_seek(fat16_state.data_start + (fat16_state.cluster-2) * fat16_state.sectors_per_cluster * 512); return 0; } while(fat16_state.file_left > 0); #ifdef DEBUG printf("File not found: [%s.%s]!\n", filename, ext); #endif return FAT16_ERR_FILE_NOT_FOUND; }
char fat16_read_file(char bytes) { // returns the bytes read #ifdef DEBUG //printf("fat16_read_file: Cluster %d, bytes left %d/%d\n", fat16_state.cluster, fat16_state.file_left, fat16_state.cluster_left); #endif if(fat16_state.file_left == 0) return 0; if(fat16_state.cluster_left == 0) { fat16_seek(fat16_state.fat_start + fat16_state.cluster*2); fat16_read(2); fat16_state.cluster = FAT16_ushort[0]; fat16_state.cluster_left = fat16_state.sectors_per_cluster * 512; if(fat16_state.cluster == 0xFFFF) { // end of cluster chain fat16_state.file_left = 0; return 0; } // Go to first cluster fat16_seek(fat16_state.data_start + (fat16_state.cluster-2) * fat16_state.sectors_per_cluster * 512); #ifdef DEBUG printf("Next cluster %d\n", fat16_state.cluster); #endif } if(bytes > fat16_state.file_left) bytes = fat16_state.file_left; if(bytes > fat16_state.cluster_left) bytes = fat16_state.cluster_left; bytes = fat16_read(bytes); fat16_state.file_left -= bytes; fat16_state.cluster_left -= bytes; #ifdef DEBUG //printf("%d bytes read: Cluster %d, bytes left %d/%d\n", bytes, fat16_state.cluster, fat16_state.file_left, fat16_state.cluster_left); #endif return bytes; }
uint8_t fat16_read_file(struct fat16_buffer* buffer, uint8_t* data) { if (buffer->file_left == 0) { return 0; } if (buffer->cluster_offset == FAT16_CLUSTER_SIZE) { // look up the next cluster (current cluster * 2 bytes) fat16_seek(buffer, buffer->fat_start + buffer->current_cluster * 2); fat16_read16(buffer, &buffer->current_cluster); buffer->cluster_offset = 0; if (buffer->current_cluster == 0xffff) { // done reading! return 0; } } uint32_t data_start = buffer->directory_start + 16384; data_start += ((buffer->current_cluster - 2) * FAT16_CLUSTER_SIZE) + buffer->cluster_offset; fat16_seek(buffer, data_start); uint8_t bytes_to_read; if (buffer->file_left > FAT16_BUFFER_SIZE) { bytes_to_read = FAT16_BUFFER_SIZE; } else { bytes_to_read = buffer->file_left; } fat16_read(buffer, bytes_to_read); for (uint8_t index = 0; index < bytes_to_read; ++index) { data[index] = buffer->data[index]; } buffer->cluster_offset += bytes_to_read; buffer->file_left -= bytes_to_read; return bytes_to_read; } // fat16_read_file
int8_t fat16_init(struct fat16_buffer* buffer) { uint8_t partition_type; uint32_t partition_start_sector; uint16_t boot_sector_signature; uint16_t reserved_sectors; uint16_t fat_id; // used to help debug fat start uint16_t fat_sector_size; // seek to the partition table at an absolute offset of 0x1BE fat16_seek(buffer, 0x1BE); // read the first partition's type (0x1BE + 4) fat16_seek(buffer, 0x1BE + 4); fat16_read8(buffer, &partition_type); fprintf(stdout, "partition type is: %i\n", partition_type); if (partition_type != PT_FAT16_DOS331 // && partition_type != PT_FAT32_LBA // No support for FAT32 LBA yet ) { fprintf(stdout, "Unknown partition type: 0x%x\n", partition_type); return -1; } // read the first partition's start sector; offset at (0x1BE + 8) fat16_seek(buffer, 0x1BE + 8); fat16_read32(buffer, &partition_start_sector); // seek 14-bytes into the boot sector record so we can // read the # of reserved sectors. this code assumes it is 2. fat16_seek(buffer, (512 * partition_start_sector) + 14); fat16_read16(buffer, &reserved_sectors); ASSERT(reserved_sectors == 2); // read the fat sector size: 22 bytes into the boot sector fat16_seek(buffer, (512 * partition_start_sector) + 22); fat16_read16(buffer, &fat_sector_size); // seek the start of the first partition (and skip past the boot sector) // the boot sector record is 512 bytes. // we back up two bytes so we can verify the boot sector signature. fat16_seek(buffer, (512 * (partition_start_sector + 1) - 2)); fat16_read16(buffer, &boot_sector_signature); ASSERT(boot_sector_signature == 0xaa55); // Find the FAT: file allocation table. Seek past all the reserved sectors. // FAT should start with: 0xFFF8 0xFFFF buffer->fat_start = (512 * (partition_start_sector + reserved_sectors)); fat16_seek(buffer, buffer->fat_start); fat16_read16(buffer, &fat_id); ASSERT(fat_id == 0xfff8); // Skip past file allocation tables and reserved sectors on partition. // There are always 2 FATs; 239 sectors per fat buffer->directory_start = (512 * (partition_start_sector + reserved_sectors) + (512 * (2 * fat_sector_size))); return 0; } // fat16_init
uint32_t fat16_open_file(struct fat16_buffer* buffer, const char* filename, const char* extension) { for (uint16_t file_index = 0; file_index < 512; ++file_index) { uint8_t pos; fat16_seek(buffer, buffer->directory_start + (file_index * 32)); fat16_read(buffer, 32); // attributes is non-zero on files. if (buffer->data[11] > 0) { for (pos = 0; pos < 8; ++pos) { if (buffer->data[pos] != filename[pos]) { break; } } if (pos == 8) { // found the entry! for (pos = 0; pos < 3; ++pos) { if (buffer->data[8 + pos] != extension[pos]) { break; } } if (pos == 3) { // 2 bytes for cluster at buffer.data[26] // 4 bytes for file size at buffer.data[28] buffer->cluster_offset = 0; buffer->current_cluster = (buffer->data[26] | ((uint16_t)buffer->data[27]) << 8); buffer->file_left = (buffer->data[28] | ((uint32_t)buffer->data[29]) << 8 | ((uint32_t)buffer->data[30]) << 16 | ((uint32_t)buffer->data[31]) << 24); return buffer->file_left; } } } } return 0; } // fat16_open_file
int8_t fat32_init(struct fat32_buffer* buffer) { // A FAT system volume is composed of four basic regions. // reserved region: // Sector 0: boot sector and BIOS Parameter Block // Sector 1: FSInfo sector (FAT32) // FAT region // root directory region (non-existent on FAT32) // file and directory data region // Determine if there's an MBR present. This is inconsistent // but this doesn't need to be there for removable drives. fat32_read(buffer, 3); if (buffer->data[0] != 0 || buffer->data[1] != 0 || buffer->data[2] != 0) { // No MBR present; rewind and start reading the boot sector. fat32_seek(buffer, 0); } else { // Skip ahead to read the first partition's type. fat32_seek(buffer, 0x1BE + 4); uint8_t partition_type; fat32_read8(buffer, &partition_type); fprintf(stdout, "partition type is: %i\n", partition_type); fprintf(stdout, "No support for MBR reading at this time.\n"); return -1; } // We need to read some values from the Boot Sector and // BIOS Parameter Block. fat32_seek(buffer, 11); fat32_read16(buffer, &buffer->sector_size_bytes); fprintf(stdout, "sector size: %i bytes\n", buffer->sector_size_bytes); fat32_seek(buffer, 13); fat32_read8(buffer, &buffer->sectors_per_cluster); fprintf(stdout, "sectors per cluster: %i bytes\n", buffer->sectors_per_cluster); fat32_seek(buffer, 14); fat32_read16(buffer, &buffer->reserved_sectors); fprintf(stdout, "reserved sectors: %i\n", buffer->reserved_sectors); fat32_seek(buffer, 16); fat32_read8(buffer, &buffer->num_fats); fprintf(stdout, "num fats: %i\n", buffer->num_fats); fat32_seek(buffer, 36); fat32_read32(buffer, &buffer->fat_sectors); fprintf(stdout, "fat sectors: %lu\n", buffer->fat_sectors); fat32_seek(buffer, 44); fat32_read32(buffer, &buffer->root_cluster_index); fprintf(stdout, "root cluster index: %lu\n", buffer->root_cluster_index); // FAT32 assertions. We skip that here since this will run on embedded, // but in case weird problems arise; this will serve as a reference. // 1. bytes_per_sector * sectors_per_cluster must be <= 32768 // 2. root_directory_entries must be zero on FAT32 buffer->first_data_sector_index = buffer->reserved_sectors + (buffer->num_fats * buffer->fat_sectors); fprintf(stdout, "first data sector index: %lu\n", buffer->first_data_sector_index); #if 0 // read the first partition's type (0x1BE + 4) fat32_seek(buffer, 0x1BE + 4); fat32_read8(buffer, &partition_type); if (partition_type == PT_FAT32_CHS) { fprintf(stdout, "<= 2GB partitions are not supported.\n"); return -1; } if (partition_type != PT_FAT32_LBA) { fprintf(stdout, "Unsupported partition type: 0x%x\n", partition_type); return -1; } // read the first partition's start sector; offset at (0x1BE + 8) fat32_seek(buffer, 0x1BE + 8); fat32_read32(buffer, &partition_start_sector); fat32_seek(buffer, (0x1BE + 64 + partition_start_sector)); struct fat32_boot_sector boot_sector; fat32_read_direct(buffer, &boot_sector, sizeof(struct fat32_boot_sector)); #endif #if 0 fat32_seek(buffer, (512 * partition_start_sector) + 0x0B); fat32_read16(buffer, &buffer->sector_size_bytes); fat32_seek(buffer, (512 * partition_start_sector) + 0x0D); fat32_read16(buffer, &buffer->sectors_per_cluster); // seek 14-bytes into the boot sector record so we can // read the # of reserved sectors. this code assumes it is 2. fat32_seek(buffer, (512 * partition_start_sector) + 14); fat32_read16(buffer, &reserved_sectors); ASSERT(reserved_sectors == 2); // read the fat sector size: 22 bytes into the boot sector fat16_seek(buffer, (512 * partition_start_sector) + 22); fat16_read16(buffer, &fat_sector_size); // seek the start of the first partition (and skip past the boot sector) // the boot sector record is 512 bytes. // we back up two bytes so we can verify the boot sector signature. fat16_seek(buffer, (512 * (partition_start_sector + 1) - 2)); fat16_read16(buffer, &boot_sector_signature); ASSERT(boot_sector_signature == 0xaa55); // Find the FAT: file allocation table. Seek past all the reserved sectors. // FAT should start with: 0xFFF8 0xFFFF buffer->fat_start = (512 * (partition_start_sector + reserved_sectors)); fat16_seek(buffer, buffer->fat_start); fat16_read16(buffer, &fat_id); ASSERT(fat_id == 0xfff8); // Skip past file allocation tables and reserved sectors on partition. // There are always 2 FATs; 239 sectors per fat buffer->directory_start = (512 * (partition_start_sector + reserved_sectors) + (512 * (2 * fat_sector_size))); #endif return 0; }