2 Copyright 2005, 2006, 2007 Dennis van Weeren
4 This file is part of Minimig
6 Minimig is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 Minimig is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 This is a simple FAT16 handler. It works on a sector basis to allow fastest acces on disk
22 Simplified from Minimig version
32 #include "fat16_defs.h"
34 /* Enable / Disable Debug info */
37 /* Structure containing selected partition data */
38 struct partition_info {
39 unsigned char fatType; // 0x10=FAT16, 0x20=FAT32
40 unsigned long partStart; // start LBA of partition
41 unsigned long fatStart; // start LBA of first FAT table
42 unsigned long dataStart; // start LBA of data field
43 unsigned long rootDirCluster; // root directory cluster
44 unsigned long rootDirStart; // start LBA of root directory table
45 unsigned short rootDirEntries; // start LBA of root directory table
46 unsigned char fatNo; // number of FAT tables
47 unsigned char clusterSize; // size of a cluster in blocks
48 unsigned long clusterMask; // binary mask of cluster number
54 unsigned char name[12]; // Short file name
55 unsigned char attributes; // File attributes
56 unsigned short entry; // File-entry index in directory table
57 unsigned long sector; // Sector index in file
58 unsigned long len; // File size
59 unsigned long cluster; // Current cluster
60 unsigned long firstCluster; // First file cluster
64 #define MAX_LFN_SIZE 128 // Maximum supported long file name, default is 256
66 /* Directory Seek Modes */
67 #define DIRECTORY_BROWSE_START 0 // start search from beginning of directory
68 #define DIRECTORY_BROWSE_NEXT 2 // find next file in directory
70 /* Sector buffer. Shared globally to save space on PIC */
71 static unsigned long cached_cur_lba;
73 /* Selected Partition */
74 static struct partition_info fat_partition;
76 static unsigned char longFilename[MAX_LFN_SIZE]; // Default long filename entry
78 static struct file_info fat_cur_file; // global file handle
79 static struct file_info fat_cur_dir; // global directory file handle
81 // Long filename entry char pos
82 static const unsigned char charLFNPos[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 };
86 #define pr_debug(args...) printf_tiny(args)
88 #define pr_debug(args...)
91 /* Caching functions for MMC */
92 static unsigned char cached_read(unsigned long lba)
94 if (cached_cur_lba == lba)
97 if (!mmc_read(lba, secbuf))
100 cached_cur_lba = lba;
104 static unsigned char cached_write(unsigned long lba)
106 cached_cur_lba = lba;
107 return mmc_write(lba, secbuf);
110 /* Calculate LBA from current file position cluster and sector */
111 static unsigned long fat_get_lba(struct file_info near *file)
115 /* Calc sector to read, when first cluster is 0 it is root directory of FAT16 */
116 if (file->firstCluster == 0) {
117 lba = fat_partition.rootDirStart;
120 lba = fat_partition.dataStart; // start of data in partition
121 lba += (file->cluster-2) * fat_partition.clusterSize; // cluster offset
122 lba += file->sector & ~fat_partition.clusterMask; // sector offset in cluster
127 /* Find next file cluster from the FAT */
128 static unsigned char fat_get_next_cluster(struct file_info near *file)
130 unsigned short index;
131 unsigned long fatSectorLba;
133 /* calculate sector that contains FAT-link */
134 fatSectorLba = fat_partition.fatStart;
136 if (fat_partition.fatType & 0x10) {
137 /* FAT16 256 cluster infos in sector on disk */
138 fatSectorLba += (file->cluster) >> 8;
140 /* calculate offset */
141 index = (file->cluster) & 0xFF;
144 /* FAT32 128 cluster infos in sector on disk */
145 fatSectorLba += (file->cluster) >> 7;
147 /* FAT32 128 cluster infos in sector */
148 index = (file->cluster) & 0x7F;
152 /* Read sector of FAT */
153 if (!cached_read(fatSectorLba))
156 if (fat_partition.fatType & 0x10) {
158 file->cluster = *((unsigned short*)&secbuf[index]);
160 /* Exit if end of chain */
161 if (file->cluster == 0xffff)
164 /* For FAT32 Read long instead short */
165 file->cluster = *((unsigned long*)&secbuf[index]);
166 file->cluster &= 0x0FFFFFFF;
168 /* Exit if end of chain */
169 if (file->cluster == 0x0fffffff)
175 /* Point to next sector in file */
176 static unsigned char fat_do_next_sector(struct file_info near *file)
178 /* increment sector index */
181 /* if we are now in another cluster, look up cluster */
182 if ((file->sector & ~fat_partition.clusterMask) == 0)
183 if (!fat_get_next_cluster(file))
186 pr_debug(" n s=0x%lx c=%lx\r\n", file->sector, file->cluster);
191 /* Seek within a file (or directory) entry */
192 static unsigned char fat_file_do_seek(struct file_info near *file, unsigned long sector)
194 long currentClusterNo;
197 /* Calculate Current ClusterNo to avoid seek if new secor is on
200 currentClusterNo = (long)file->sector;
201 currentClusterNo /= (long)fat_partition.clusterSize;
203 /* Sector in file to read */
204 file->sector = sector;
206 /* Calculate new ClusterNO in file */
207 clusterNo = (long)file->sector;
208 clusterNo /= (long)fat_partition.clusterSize;
210 /* Calc ClusterNo Difference */
211 currentClusterNo -= clusterNo;
213 /* Check if we are on same cluster */
214 if (currentClusterNo == 0) {
215 pr_debug(" S s=0x%lx c=%lx\r\n", file->sector, file->cluster);
219 /* Reset current cluster in file to first */
220 file->cluster = file->firstCluster;
222 /* If first cluster in file exit */
226 /* Loop through cluster links */
227 while(--clusterNo >= 0) {
228 if (!fat_get_next_cluster(file))
230 // pr_debug(" -seek: clusterNo: 0x%lx", clusterNo);
231 // pr_debug(" , cluster: 0x%lx\r\n", file->cluster);
234 pr_debug(" s s=0x%lx c=%lx\r\n", file->sector, file->cluster);
238 static unsigned char fat_process_dir_entry(struct file_info near *file, union FAT_directoryEntry * dirEntry)
243 /* Check if we are at directory end */
244 if (dirEntry->entry.shortName.name[0] == FAT_ENTRY_FREE) {
245 pr_debug("Entry free %d\r\n", file->entry);
249 /* Check if file deleted */
250 if (FAT_ENTRY_DELETED != dirEntry->entry.shortName.name[0]) {
251 /* Check if longfilename */
252 if(FAT_ATTRIB_LFN_TEXT == dirEntry->entry.attributes) {
253 /* Calc pos to copy part of LFN */
254 char_offset = ((dirEntry->LFN.sequenceNo & 0x3f)-1) * 13;
256 /* Copy part of LFN */
257 for(i = 0; i < 13 && (char_offset + i) < (MAX_LFN_SIZE - 1); i++)
258 longFilename[char_offset + i] = dirEntry->bytes[charLFNPos[i]];
259 } else if (dirEntry->entry.attributes &
260 (FAT_ATTRIB_HIDDEN | FAT_ATTRIB_SYSTEM | FAT_ATTRIB_VOLUME)) {
261 pr_debug("FAT: Skiping entry no:%d attr:0x%02X\n",
262 file->entry, dirEntry->entry.attributes);
263 /* Clear longfile name just in case */
264 memset(longFilename,0,MAX_LFN_SIZE);
267 memcpy(&file->name, &dirEntry->entry.shortName, 11);
268 file->name[11] = 0x00; /* In theory not needed since never trashed */
269 file->attributes = dirEntry->entry.attributes;
270 file->len = dirEntry->entry.length; /* get length of file */
271 file->firstCluster = (unsigned long)dirEntry->entry.firstClusterLow; // get first cluster of file
272 /* FAT32 high bytes of cluster */
273 if (fat_partition.fatType & 0x20)
274 file->firstCluster |= ((unsigned long)dirEntry->entry.firstClusterHigh) << 16;
275 file->cluster = file->firstCluster; /* Copy current cluster to first cluster */
276 file->sector = 0; /* Reset current sector index */
278 /* Check if there is no LFN than copy short name as LFN */
279 /* Skip entries that have LFN and entries starting with '.' (Parent and Current dir) */
280 if(!longFilename[0] && (file->name[0] != '.')) {
282 for(i=0; i < 11; i++) {
283 if(file->name[i] != ' ')
284 longFilename[char_offset++] = file->name[i];
286 longFilename[char_offset++] = '.';
288 /* Delete Last dot */
289 if(longFilename[char_offset-1]=='.')
291 longFilename[char_offset] = 0x00;
299 /* Iterate directory entries */
300 static unsigned char fat_get_dir_entry(struct file_info near *file, struct file_info near *dir,
306 /* Clear long file name */
307 memset(longFilename,0,MAX_LFN_SIZE);
309 /* Check for iteration mode */
311 case DIRECTORY_BROWSE_NEXT:
314 case DIRECTORY_BROWSE_START:
320 /* Seek directory to selected file entry */
321 if(!fat_file_do_seek(dir, file->entry >> 4))
324 /* Infinite loop for search */
326 /* Read sector in buffer */
327 if (!cached_read(fat_get_lba(dir)))
330 /* Calculate file entry offset in directory sector, 16 entries in sector 32 byte big */
331 entryOffset = (file->entry & 0xF) << 5;
333 /* Process directory entry */
334 rc = fat_process_dir_entry(file, (union FAT_directoryEntry *)(secbuf + entryOffset));
340 /* Go to next Fat Entry */
343 /* Check should we seek directory to next sector */
344 if (!(file->entry & 0xF))
345 if (!fat_do_next_sector(dir))
351 /* Move to root directory */
352 static void fat_cd_root_dir(struct file_info near *dir)
355 memset(dir, 0, sizeof(struct file_info));
357 dir->attributes = FAT_ATTRIB_DIR;
358 dir->firstCluster = fat_partition.rootDirCluster;
359 dir->cluster = dir->firstCluster;
362 /* Find file by name and open it, return len or -1 */
363 long fat_open(const unsigned char *name)
365 struct file_info near *file = &fat_cur_file;
366 unsigned char mode = DIRECTORY_BROWSE_START;
369 pr_debug("FAT: Open file: %s\n",name);
371 while (fat_get_dir_entry(file, &fat_cur_dir, mode)) {
372 pr_debug(" found: %s...",file->name);
374 if (file->name[i]!=name[i])
377 pr_debug(" GOOD !\n");
381 mode = DIRECTORY_BROWSE_NEXT;
384 pr_debug("FAT: File not found\n");
389 unsigned char fat_file_read(void)
391 return cached_read(fat_get_lba(&fat_cur_file));
394 unsigned char fat_file_write(void)
396 return cached_write(fat_get_lba(&fat_cur_file));
399 unsigned char fat_file_seek(unsigned long sector)
401 return fat_file_do_seek(&fat_cur_file, sector);
404 unsigned char fat_file_next_sector(void)
406 return fat_do_next_sector(&fat_cur_file);
409 void fat_inval_cache(void)
411 cached_cur_lba = 0xffffffff;
416 * Checks if a card is present. if a card is present it will check for
417 * a valid FAT16 or FAT32 primary partition
419 unsigned char fat_init(void)
421 unsigned long fatsize; // size of fat
422 unsigned long dirsize; // size of directory region in sectors
424 struct MBR_Disk *mbr = (struct MBR_Disk *)secbuf;
425 struct FAT_Boot_Sector *boot = (struct FAT_Boot_Sector *)secbuf;
427 /* Invalidate cache */
428 cached_cur_lba = 0xffffffff;
430 /* Read partition sector */
431 if (!cached_read(0)) {
432 pr_debug("FAT: Error Unable to read partition sector\n");
436 /* Check for signature */
437 if (mbr->signature != 0xAA55) {
438 pr_debug("FAT: Error invalid FAT signature\n");
442 /* Check first partition type */
443 switch (mbr->partitions[0].Type) {
444 case PARTITION_TYPE_FAT16_32MB:
445 case PARTITION_TYPE_FAT16:
446 /* First partition filesystem type: FAT16 */
447 fat_partition.fatType = 0x10;
448 /* Get start of first partition */
449 fat_partition.partStart = mbr->partitions[0].LBAFirst;
452 case PARTITION_TYPE_FAT32:
453 case PARTITION_TYPE_FAT32_LBA:
454 /* First partition filesystem type: FAT32 */
455 fat_partition.fatType = 0x20;
456 /* get start of first partition */
457 fat_partition.partStart = mbr->partitions[0].LBAFirst;
461 pr_debug("FAT: Error no supported partition found\n");
465 /* Read boot sector */
466 if (!cached_read(fat_partition.partStart)) {
467 pr_debug("FAT: Error unable to read boot sector\n");
471 /* Check for near-jump or short-jump opcode */
472 if (0xE9 != boot->jumpInstruction[0] && 0xEB != boot->jumpInstruction[0]) {
473 pr_debug("FAT: Error invalid 0x%x boot sector jump\n",
474 boot->jumpInstruction[0]);
478 /* Check if blocksize is really 512 bytes */
479 if (0x200 != boot->bytesPerSector) {
480 pr_debug("FAT: Error block size not 512 bytes it is 0x%x\n",
481 boot->bytesPerSector);
485 /* Check medium descriptor byte, must be 0xf8 for hard drive */
486 if (0xF8 != boot->mediaDescriptor) {
487 pr_debug("Error invalid media descriptor\n");
491 /* Get cluster size */
492 fat_partition.clusterSize = boot->sectorsPerCluster;
493 /* calculate cluster mask */
494 fat_partition.clusterMask = ~(fat_partition.clusterSize-1);
497 /* Calculate drive's parameters from bootsector, first up is size of directory */
498 /* Get Max ROOT Dir entries FAT16 only !!*/
499 fat_partition.rootDirEntries = boot->maxRootEntries;
501 /* Calculate start of FAT, size of FAT and number of FAT's */
502 fat_partition.fatStart = fat_partition.partStart + boot->reservedSectorCount;
503 fat_partition.fatNo = boot->noOfFATs;
506 if (0x10 == fat_partition.fatType) {
507 fatsize = boot->sectorsPerFAT;
508 // Fat 16 Root dir cluster
509 fat_partition.rootDirCluster = 0;
510 // calculate start of FAT16 ROOT directory
511 fat_partition.rootDirStart = fat_partition.fatStart + (fat_partition.fatNo * fatsize);
512 // Calculate dire size in sectors
513 dirsize = ((fat_partition.rootDirEntries<<5)+511)>>9;
514 // calculate start of data
515 fat_partition.dataStart = fat_partition.rootDirStart + dirsize;
517 fatsize = boot->extParams.fat32Ext.sectorsPerFAT;
518 // calculate data start
519 fat_partition.dataStart = fat_partition.fatStart + (fat_partition.fatNo * fatsize);
520 // Fat 32 Root dir cluster
521 fat_partition.rootDirCluster = boot->extParams.fat32Ext.rootDirCluster;
522 // Get FAT32 root dir start
523 fat_partition.rootDirStart = fat_partition.dataStart +
524 (fat_partition.rootDirCluster-2) * fat_partition.clusterSize;
527 /* Open Root Directory */
528 fat_cd_root_dir(&fat_cur_dir);
530 pr_debug("FAT%d partition found.\n", fat_partition.fatType);
531 pr_debug("Fat Size: %ld\n",fatsize);
532 pr_debug("No of fats: %d\n",fat_partition.fatNo);
533 pr_debug("Fat start: %ld\n", fat_partition.fatStart);
534 pr_debug("Direntries: %d\n",fat_partition.rootDirEntries);
535 pr_debug("Dir start: %ld\n", fat_partition.rootDirStart);
536 pr_debug("Data start: %ld\n", fat_partition.dataStart);
537 pr_debug("Cluster size: %d\n",fat_partition.clusterSize);
538 pr_debug("Cluster mask: %lx\n",fat_partition.clusterMask);