]> git.ozlabs.org Git - minimigmac.git/blob - pic/fat16.c
Initial commit
[minimigmac.git] / pic / fat16.c
1 /*
2 Copyright 2005, 2006, 2007 Dennis van Weeren
3
4 This file is part of Minimig
5
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.
10
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.
15
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/>.
18
19 This is a simple FAT16 handler. It works on a sector basis to allow fastest acces on disk
20 images.
21
22 Simplified from Minimig version
23
24 */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdbool.h>
29
30 #include "mmc.h"
31 #include "fat16.h"
32 #include "fat16_defs.h"
33
34 /* Enable / Disable Debug info */
35 #undef DEBUG_FAT
36
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
49 };
50
51
52 /* Open file info */
53 struct file_info {
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
61 };
62
63
64 #define MAX_LFN_SIZE    128                             // Maximum supported long file name, default is 256
65
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
69
70 /* Sector buffer. Shared globally to save space on PIC */
71 static unsigned long cached_cur_lba;
72
73 /* Selected Partition */
74 static struct partition_info fat_partition;
75
76 static unsigned char longFilename[MAX_LFN_SIZE];        // Default long filename entry
77
78 static struct file_info fat_cur_file;                   // global file handle
79 static struct file_info fat_cur_dir;                    // global directory file handle
80
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 };
83
84
85 #ifdef DEBUG_FAT
86 #define pr_debug(args...)       printf_tiny(args)
87 #else
88 #define pr_debug(args...)
89 #endif
90
91 /* Caching functions for MMC */
92 static unsigned char cached_read(unsigned long lba)
93 {
94         if (cached_cur_lba == lba)
95                 return true;
96
97         if (!mmc_read(lba, secbuf))
98                 return false;
99
100         cached_cur_lba = lba;
101         return true;
102 }
103
104 static unsigned char cached_write(unsigned long lba)
105 {
106         cached_cur_lba = lba;
107         return mmc_write(lba, secbuf);
108 }
109
110 /* Calculate LBA from current file position cluster and sector */
111 static unsigned long fat_get_lba(struct file_info near *file)
112 {
113         unsigned long lba;
114
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;
118                 lba += file->sector;
119         } else {
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
123         }
124         return lba;
125 }
126
127 /* Find next file cluster from the FAT */
128 static unsigned char fat_get_next_cluster(struct file_info near *file)
129 {
130         unsigned short index;
131         unsigned long fatSectorLba;
132
133         /* calculate sector that contains FAT-link */
134         fatSectorLba = fat_partition.fatStart;
135
136         if (fat_partition.fatType & 0x10) {
137                 /* FAT16 256 cluster infos in sector on disk */
138                 fatSectorLba += (file->cluster) >> 8;
139
140                 /* calculate offset */
141                 index = (file->cluster) & 0xFF;
142                 index <<= 1;
143         } else {
144                 /* FAT32 128 cluster infos in sector on disk */
145                 fatSectorLba += (file->cluster) >> 7;
146
147                 /* FAT32 128 cluster infos in sector */
148                 index = (file->cluster) & 0x7F;
149                 index <<= 2;
150         }
151
152         /* Read sector of FAT */
153         if (!cached_read(fatSectorLba))
154                 return false;
155
156         if (fat_partition.fatType & 0x10) {
157                 /* get FAT-link */
158                 file->cluster = *((unsigned short*)&secbuf[index]);
159
160                 /* Exit if end of chain */
161                 if (file->cluster == 0xffff)
162                         return false;
163         } else {
164                 /* For FAT32 Read long instead short */
165                 file->cluster = *((unsigned long*)&secbuf[index]);
166                 file->cluster &= 0x0FFFFFFF;
167
168                 /* Exit if end of chain */
169                 if (file->cluster == 0x0fffffff)
170                         return false;
171         }
172         return true;
173 }
174
175 /* Point to next sector in file */
176 static unsigned char fat_do_next_sector(struct file_info near *file)
177 {
178         /* increment sector index */
179         file->sector++;
180
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))
184                         return false;
185
186         pr_debug(" n s=0x%lx c=%lx\r\n", file->sector, file->cluster);
187
188         return true;
189 }
190
191 /* Seek within a file (or directory) entry */
192 static unsigned char fat_file_do_seek(struct file_info near *file, unsigned long sector)
193 {
194         long currentClusterNo;
195         long clusterNo;
196
197         /* Calculate Current ClusterNo to avoid seek if new secor is on
198          * same cluster
199          */
200         currentClusterNo = (long)file->sector;
201         currentClusterNo /= (long)fat_partition.clusterSize;
202         
203         /* Sector in file to read */
204         file->sector = sector;
205
206         /* Calculate new ClusterNO in file */
207         clusterNo = (long)file->sector;
208         clusterNo /= (long)fat_partition.clusterSize;
209
210         /* Calc ClusterNo Difference */
211         currentClusterNo -= clusterNo; 
212         
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);
216                 return true;
217         }
218
219         /* Reset current cluster in file to first */
220         file->cluster = file->firstCluster;
221                 
222         /* If first cluster in file exit */
223         if (clusterNo == 0)
224                 return true;
225
226         /* Loop through cluster links */
227         while(--clusterNo >= 0) {
228                 if (!fat_get_next_cluster(file))
229                         return false;
230                 //              pr_debug(" -seek: clusterNo: 0x%lx", clusterNo);
231                 //              pr_debug(" , cluster: 0x%lx\r\n", file->cluster);
232         }
233
234         pr_debug(" s s=0x%lx c=%lx\r\n", file->sector, file->cluster);
235         return true;
236 }
237
238 static unsigned char fat_process_dir_entry(struct file_info near *file, union FAT_directoryEntry * dirEntry)
239 {
240         short i;
241         short char_offset;
242
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);
246                 return 0;       
247         }
248
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;
255                 
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);
265                 } else {
266                         /* Copy name */
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 */
277
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] != '.')) {
281                                 char_offset = 0;
282                                 for(i=0; i < 11; i++) {
283                                         if(file->name[i] != ' ')
284                                                 longFilename[char_offset++] = file->name[i];
285                                         if(7 == i)
286                                                 longFilename[char_offset++] = '.';
287                                 }
288                                 /* Delete Last dot */
289                                 if(longFilename[char_offset-1]=='.')
290                                         char_offset--;
291                                 longFilename[char_offset] = 0x00;
292                         }
293                         return 1;                       
294                 }
295         }
296         return 2;
297 }
298
299 /* Iterate directory entries */
300 static unsigned char fat_get_dir_entry(struct file_info near *file, struct file_info near *dir,
301                                        unsigned char mode)
302 {
303         short entryOffset;
304         char rc;
305
306         /* Clear long file name */
307         memset(longFilename,0,MAX_LFN_SIZE);
308
309         /* Check for iteration mode */
310         switch(mode) {
311         case DIRECTORY_BROWSE_NEXT:
312                 file->entry++;
313                 break;
314         case DIRECTORY_BROWSE_START:
315         default:
316                 file->entry = 0;
317                 break;
318         }
319         
320         /* Seek directory to selected file entry */
321         if(!fat_file_do_seek(dir, file->entry >> 4))
322                 return false;
323
324         /* Infinite loop for search */
325         while(1) {
326                 /* Read sector in buffer */
327                 if (!cached_read(fat_get_lba(dir)))
328                         return false;
329
330                 /* Calculate file entry offset in directory sector, 16 entries in sector 32 byte big */
331                 entryOffset = (file->entry & 0xF) << 5;
332
333                 /* Process directory entry */
334                 rc = fat_process_dir_entry(file, (union FAT_directoryEntry *)(secbuf + entryOffset));
335                 if (!rc)
336                         return false;
337                 if (rc == 1)
338                         return true;
339
340                 /* Go to next Fat Entry */
341                 file->entry++;
342                 
343                 /* Check should we seek directory to next sector */
344                 if (!(file->entry & 0xF))
345                         if (!fat_do_next_sector(dir))
346                                 return false;
347         }
348 }
349
350
351 /* Move to root directory */
352 static void fat_cd_root_dir(struct file_info near *dir)
353 {
354         /* Clear Entry */
355         memset(dir, 0, sizeof(struct file_info));
356         dir->name[0] = '/';
357         dir->attributes = FAT_ATTRIB_DIR;
358         dir->firstCluster = fat_partition.rootDirCluster;
359         dir->cluster = dir->firstCluster;
360 }
361
362 /* Find file by name and open it, return len or -1 */
363 long fat_open(const unsigned char *name)
364 {
365         struct file_info near *file = &fat_cur_file;
366         unsigned char mode = DIRECTORY_BROWSE_START;
367         unsigned char i;
368
369         pr_debug("FAT: Open file: %s\n",name);
370         
371         while (fat_get_dir_entry(file, &fat_cur_dir, mode)) {
372                 pr_debug(" found: %s...",file->name);
373                 for(i=0; i<11; i++)
374                         if (file->name[i]!=name[i])
375                                 break;
376                 if (i==11) {
377                         pr_debug(" GOOD !\n"); 
378                         return file->len;
379                 }
380                 pr_debug("\n"); 
381                 mode = DIRECTORY_BROWSE_NEXT;
382         }
383         
384         pr_debug("FAT: File not found\n");
385
386         return -1;
387 }
388
389 unsigned char fat_file_read(void)
390 {
391         return cached_read(fat_get_lba(&fat_cur_file));
392 }
393
394 unsigned char fat_file_write(void)
395 {
396         return cached_write(fat_get_lba(&fat_cur_file));
397 }
398
399 unsigned char fat_file_seek(unsigned long sector)
400 {
401         return fat_file_do_seek(&fat_cur_file, sector);
402 }
403  
404 unsigned char fat_file_next_sector(void)
405 {
406         return fat_do_next_sector(&fat_cur_file);
407 }
408  
409 void fat_inval_cache(void)
410 {
411         cached_cur_lba = 0xffffffff;
412 }
413
414 /* fat_init()
415  *
416  * Checks if a card is present. if a card is present it will check for
417  * a valid FAT16 or FAT32 primary partition
418  */
419 unsigned char fat_init(void)
420 {
421         unsigned long fatsize;  // size of fat
422         unsigned long dirsize;  // size of directory region in sectors
423
424         struct MBR_Disk *mbr = (struct MBR_Disk *)secbuf;
425         struct FAT_Boot_Sector *boot = (struct FAT_Boot_Sector *)secbuf;
426
427         /* Invalidate cache */
428         cached_cur_lba = 0xffffffff;
429
430         /* Read partition sector */
431         if (!cached_read(0)) {
432                 pr_debug("FAT: Error Unable to read partition sector\n");
433                 return false;
434         }
435         
436         /* Check for signature */
437         if (mbr->signature != 0xAA55) { 
438                 pr_debug("FAT: Error invalid FAT signature\n");
439                 return false;
440         }
441
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;
450                         break;
451
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;
458                         break;
459                         
460                 default:
461                         pr_debug("FAT: Error no supported partition found\n");
462                         return false;
463         }
464
465         /* Read boot sector */
466         if (!cached_read(fat_partition.partStart)) {    
467                 pr_debug("FAT: Error unable to read boot sector\n");
468                 return false;
469         }
470
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]);
475                 return false;
476         }
477
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);
482                 return false;
483         }
484
485         /* Check medium descriptor byte, must be 0xf8 for hard drive */
486         if (0xF8 != boot->mediaDescriptor) {
487                 pr_debug("Error invalid media descriptor\n");
488                 return false;
489         }
490         
491         /* Get cluster size */
492         fat_partition.clusterSize = boot->sectorsPerCluster;
493         /* calculate cluster mask */
494         fat_partition.clusterMask = ~(fat_partition.clusterSize-1);
495
496         
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;
500
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;
504
505         /* When FAT16 */ 
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;
516         } else {
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;
525         }
526
527         /* Open Root Directory */
528         fat_cd_root_dir(&fat_cur_dir);
529         
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);
539
540         return true;
541 }
542