Integer setting changes to make all those settings consistant on the target.
[Rockbox.git] / rbutil / ipodpatcher / fat32format.c
blobe0e30db602924587c0173a9958e28f7179f85608
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
11 * FAT32 formatting functions. Based on:
13 * Fat32 formatter version 1.03
14 * (c) Tom Thornhill 2005
15 * This software is covered by the GPL.
16 * By using this tool, you agree to absolve Ridgecrop of an liabilities for
17 * lost data.
18 * Please backup any data you value before using this tool.
21 * Modified June 2007 by Dave Chapman for use in ipodpatcher
24 * All files in this archive are subject to the GNU General Public License.
25 * See the file COPYING in the source tree root for full license agreement.
27 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
28 * KIND, either express or implied.
30 ****************************************************************************/
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdbool.h>
36 #include <stdint.h>
38 #include "ipodio.h"
40 static inline uint16_t swap16(uint16_t value)
42 return (value >> 8) | (value << 8);
45 static inline uint32_t swap32(uint32_t value)
47 uint32_t hi = swap16(value >> 16);
48 uint32_t lo = swap16(value & 0xffff);
49 return (lo << 16) | hi;
52 /* The following functions are not the most efficient, but are
53 self-contained and don't require needing to know endianness of CPU
54 at compile-time.
57 uint16_t htole16(uint16_t x)
59 uint16_t test = 0x1234;
60 unsigned char* p = (unsigned char*)&test;
62 if (p[0]==0x12) {
63 /* Big-endian */
64 return swap16(x);
65 } else {
66 return x;
70 uint32_t htole32(uint32_t x)
72 uint32_t test = 0x12345678;
73 unsigned char* p = (unsigned char*)&test;
75 if (p[0]==0x12) {
76 /* Big-endian */
77 return swap32(x);
78 } else {
79 return x;
84 /* A large aligned buffer for disk I/O */
85 extern unsigned char* sectorbuf;
87 /* TODO: Pass these as parameters to the various create_ functions */
89 /* can be zero for default or 1,2,4,8,16,32 or 64 */
90 static int sectors_per_cluster = 0;
92 /* Recommended values */
93 static uint32_t ReservedSectCount = 32;
94 static uint32_t NumFATs = 2;
95 static uint32_t BackupBootSect = 6;
96 static uint32_t VolumeId=0; /* calculated before format */
98 /* Calculated later */
99 static uint32_t FatSize=0;
100 static uint32_t BytesPerSect=0;
101 static uint32_t SectorsPerCluster=0;
102 static uint32_t TotalSectors=0;
103 static uint32_t SystemAreaSize=0;
104 static uint32_t UserAreaSize=0;
105 static uint8_t VolId[12] = "NO NAME ";
108 struct FAT_BOOTSECTOR32
110 /* Common fields. */
111 uint8_t sJmpBoot[3];
112 char sOEMName[8];
113 uint16_t wBytsPerSec;
114 uint8_t bSecPerClus;
115 uint16_t wRsvdSecCnt;
116 uint8_t bNumFATs;
117 uint16_t wRootEntCnt;
118 uint16_t wTotSec16; /* if zero, use dTotSec32 instead */
119 uint8_t bMedia;
120 uint16_t wFATSz16;
121 uint16_t wSecPerTrk;
122 uint16_t wNumHeads;
123 uint32_t dHiddSec;
124 uint32_t dTotSec32;
126 /* Fat 32/16 only */
127 uint32_t dFATSz32;
128 uint16_t wExtFlags;
129 uint16_t wFSVer;
130 uint32_t dRootClus;
131 uint16_t wFSInfo;
132 uint16_t wBkBootSec;
133 uint8_t Reserved[12];
134 uint8_t bDrvNum;
135 uint8_t Reserved1;
136 uint8_t bBootSig; /* == 0x29 if next three fields are ok */
137 uint32_t dBS_VolID;
138 uint8_t sVolLab[11];
139 uint8_t sBS_FilSysType[8];
140 } __attribute__((packed));
142 struct FAT_FSINFO {
143 uint32_t dLeadSig; // 0x41615252
144 uint8_t sReserved1[480]; // zeros
145 uint32_t dStrucSig; // 0x61417272
146 uint32_t dFree_Count; // 0xFFFFFFFF
147 uint32_t dNxt_Free; // 0xFFFFFFFF
148 uint8_t sReserved2[12]; // zeros
149 uint32_t dTrailSig; // 0xAA550000
150 } __attribute__((packed));
153 /* Write "count" zero sectors, starting at sector "sector" */
154 static int zero_sectors(struct ipod_t* ipod, uint64_t sector, int count)
156 int n;
158 if (ipod_seek(ipod, sector * ipod->sector_size) < 0) {
159 fprintf(stderr,"[ERR] Seek failed\n");
160 return -1;
163 memset(sectorbuf, 0, 128 * ipod->sector_size);
165 /* Write 128 sectors at a time */
166 while (count) {
167 if (count >= 128)
168 n = 128;
169 else
170 n = count;
172 if (ipod_write(ipod,sectorbuf,n * ipod->sector_size) < 0) {
173 perror("[ERR] Write failed in zero_sectors\n");
174 return -1;
177 count -= n;
180 return 0;
185 28.2 CALCULATING THE VOLUME SERIAL NUMBER
187 For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94
188 seconds. DOS takes the date and time just before it writes it to the
189 disk.
191 Low order word is calculated: Volume Serial Number is:
192 Month & Day 12/26 0c1ah
193 Sec & Hundrenths 41:94 295eh 3578:1d02
194 -----
195 3578h
197 High order word is calculated:
198 Hours & Minutes 21:55 1537h
199 Year 1995 07cbh
200 -----
201 1d02h
203 static uint32_t get_volume_id ( )
205 /* TODO */
206 #if 0
207 SYSTEMTIME s;
208 uint32_t d;
209 uint16_t lo,hi,tmp;
211 GetLocalTime( &s );
213 lo = s.wDay + ( s.wMonth << 8 );
214 tmp = (s.wMilliseconds/10) + (s.wSecond << 8 );
215 lo += tmp;
217 hi = s.wMinute + ( s.wHour << 8 );
218 hi += s.wYear;
220 d = lo + (hi << 16);
221 return(d);
222 #endif
223 return(0);
227 This is the Microsoft calculation from FATGEN
229 uint32_t RootDirSectors = 0;
230 uint32_t TmpVal1, TmpVal2, FATSz;
232 TmpVal1 = DskSize - ( ReservedSecCnt + RootDirSectors);
233 TmpVal2 = (256 * SecPerClus) + NumFATs;
234 TmpVal2 = TmpVal2 / 2;
235 FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
237 return( FatSz );
241 static uint32_t get_fat_size_sectors(uint32_t DskSize, uint32_t ReservedSecCnt,
242 uint32_t SecPerClus, uint32_t NumFATs,
243 uint32_t BytesPerSect)
245 uint64_t Numerator, Denominator;
246 uint64_t FatElementSize = 4;
247 uint64_t FatSz;
249 /* This is based on
250 http://hjem.get2net.dk/rune_moeller_barnkob/filesystems/fat.html
251 I've made the obvious changes for FAT32
254 Numerator = FatElementSize * ( DskSize - ReservedSecCnt );
255 Denominator = ( SecPerClus * BytesPerSect ) + ( FatElementSize * NumFATs );
256 FatSz = Numerator / Denominator;
258 /* round up */
259 FatSz += 1;
261 return((uint32_t)FatSz);
264 static uint8_t get_spc(uint32_t ClusterSizeKB, uint32_t BytesPerSect)
266 uint32_t spc = ( ClusterSizeKB * 1024 ) / BytesPerSect;
267 return( (uint8_t) spc );
270 static uint8_t get_sectors_per_cluster(uint32_t DiskSizeSectors,
271 uint32_t BytesPerSect)
273 uint8_t ret = 0x01; /* 1 sector per cluster */
274 uint64_t DiskSizeBytes = (uint64_t)DiskSizeSectors * (uint64_t)BytesPerSect;
275 int64_t DiskSizeMB = DiskSizeBytes / ( 1024*1024 );
277 /* 512 MB to 8,191 MB 4 KB */
278 if ( DiskSizeMB > 512 )
279 ret = get_spc( 4, BytesPerSect ); /* ret = 0x8; */
281 /* 8,192 MB to 16,383 MB 8 KB */
282 if ( DiskSizeMB > 8192 )
283 ret = get_spc( 8, BytesPerSect ); /* ret = 0x10; */
285 /* 16,384 MB to 32,767 MB 16 KB */
286 if ( DiskSizeMB > 16384 )
287 ret = get_spc( 16, BytesPerSect ); /* ret = 0x20; */
289 /* Larger than 32,768 MB 32 KB */
290 if ( DiskSizeMB > 32768 )
291 ret = get_spc( 32, BytesPerSect ); /* ret = 0x40; */
293 return( ret );
297 static void create_boot_sector(unsigned char* buf,
298 struct ipod_t* ipod, int partition)
300 struct FAT_BOOTSECTOR32* pFAT32BootSect = (struct FAT_BOOTSECTOR32*)buf;
302 /* fill out the boot sector and fs info */
303 pFAT32BootSect->sJmpBoot[0]=0xEB;
304 pFAT32BootSect->sJmpBoot[1]=0x5A;
305 pFAT32BootSect->sJmpBoot[2]=0x90;
306 strcpy( pFAT32BootSect->sOEMName, "MSWIN4.1" );
307 pFAT32BootSect->wBytsPerSec = htole16(BytesPerSect);
308 pFAT32BootSect->bSecPerClus = SectorsPerCluster ;
309 pFAT32BootSect->wRsvdSecCnt = htole16(ReservedSectCount);
310 pFAT32BootSect->bNumFATs = NumFATs;
311 pFAT32BootSect->wRootEntCnt = htole16(0);
312 pFAT32BootSect->wTotSec16 = htole16(0);
313 pFAT32BootSect->bMedia = 0xF8;
314 pFAT32BootSect->wFATSz16 = htole16(0);
315 pFAT32BootSect->wSecPerTrk = htole16(ipod->sectors_per_track);
316 pFAT32BootSect->wNumHeads = htole16(ipod->num_heads);
317 pFAT32BootSect->dHiddSec = htole16(ipod->pinfo[partition].start);
318 pFAT32BootSect->dTotSec32 = htole32(TotalSectors);
319 pFAT32BootSect->dFATSz32 = htole32(FatSize);
320 pFAT32BootSect->wExtFlags = htole16(0);
321 pFAT32BootSect->wFSVer = htole16(0);
322 pFAT32BootSect->dRootClus = htole32(2);
323 pFAT32BootSect->wFSInfo = htole16(1);
324 pFAT32BootSect->wBkBootSec = htole16(BackupBootSect);
325 pFAT32BootSect->bDrvNum = 0x80;
326 pFAT32BootSect->Reserved1 = 0;
327 pFAT32BootSect->bBootSig = 0x29;
328 pFAT32BootSect->dBS_VolID = htole32(VolumeId);
329 memcpy(pFAT32BootSect->sVolLab, VolId, 11);
330 memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8 );
332 buf[510] = 0x55;
333 buf[511] = 0xaa;
336 static void create_fsinfo(unsigned char* buf)
338 struct FAT_FSINFO* pFAT32FsInfo = (struct FAT_FSINFO*)buf;
340 /* FSInfo sect */
341 pFAT32FsInfo->dLeadSig = htole32(0x41615252);
342 pFAT32FsInfo->dStrucSig = htole32(0x61417272);
343 pFAT32FsInfo->dFree_Count = htole32((uint32_t) -1);
344 pFAT32FsInfo->dNxt_Free = htole32((uint32_t) -1);
345 pFAT32FsInfo->dTrailSig = htole32(0xaa550000);
346 pFAT32FsInfo->dFree_Count = htole32((UserAreaSize/SectorsPerCluster)-1);
348 /* clusters 0-1 reserved, we used cluster 2 for the root dir */
349 pFAT32FsInfo->dNxt_Free = htole32(3);
352 static void create_firstfatsector(unsigned char* buf)
354 uint32_t* p = (uint32_t*)buf; /* We know the buffer is aligned */
356 /* First FAT Sector */
357 p[0] = htole32(0x0ffffff8); /* Reserved cluster 1 media id in low byte */
358 p[1] = htole32(0x0fffffff); /* Reserved cluster 2 EOC */
359 p[2] = htole32(0x0fffffff); /* end of cluster chain for root dir */
362 int format_partition(struct ipod_t* ipod, int partition)
364 uint32_t i;
365 uint64_t qTotalSectors=0;
366 uint64_t FatNeeded;
368 VolumeId = get_volume_id( );
370 /* Only support hard disks at the moment */
371 if ( ipod->sector_size != 512 )
373 fprintf(stderr,"[ERR] Only disks with 512 bytes per sector are supported.\n");
374 return -1;
376 BytesPerSect = ipod->sector_size;
378 /* Checks on Disk Size */
379 qTotalSectors = ipod->pinfo[partition].size;
381 /* low end limit - 65536 sectors */
382 if ( qTotalSectors < 65536 )
384 /* I suspect that most FAT32 implementations would mount this
385 volume just fine, but the spec says that we shouldn't do
386 this, so we won't */
388 fprintf(stderr,"[ERR] This drive is too small for FAT32 - there must be at least 64K clusters\n" );
389 return -1;
392 if ( qTotalSectors >= 0xffffffff )
394 /* This is a more fundamental limitation on FAT32 - the total
395 sector count in the root dir ­s 32bit. With a bit of
396 creativity, FAT32 could be extended to handle at least 2^28
397 clusters There would need to be an extra field in the
398 FSInfo sector, and the old sector count could be set to
399 0xffffffff. This is non standard though, the Windows FAT
400 driver FASTFAT.SYS won't understand this. Perhaps a future
401 version of FAT32 and FASTFAT will handle this. */
403 fprintf(stderr,"[ERR] This drive is too big for FAT32 - max 2TB supported\n");
406 if ( sectors_per_cluster ) {
407 SectorsPerCluster = sectors_per_cluster;
408 } else {
409 SectorsPerCluster = get_sectors_per_cluster(ipod->pinfo[partition].size,
410 BytesPerSect );
413 TotalSectors = (uint32_t) qTotalSectors;
415 FatSize = get_fat_size_sectors(TotalSectors, ReservedSectCount,
416 SectorsPerCluster, NumFATs, BytesPerSect );
418 UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize);
420 /* First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster */
421 SystemAreaSize = (ReservedSectCount+(NumFATs*FatSize) + SectorsPerCluster);
423 /* Work out the Cluster count */
424 FatNeeded = UserAreaSize/SectorsPerCluster;
426 /* check for a cluster count of >2^28, since the upper 4 bits of
427 the cluster values in the FAT are reserved. */
428 if (FatNeeded > 0x0FFFFFFF) {
429 fprintf(stderr,"[ERR] This drive has more than 2^28 clusters, try to specify a larger cluster size\n" );
430 return -1;
433 /* Sanity check, make sure the fat is big enough.
434 Convert the cluster count into a Fat sector count, and check
435 the fat size value we calculated earlier is OK. */
437 FatNeeded *=4;
438 FatNeeded += (BytesPerSect-1);
439 FatNeeded /= BytesPerSect;
441 if ( FatNeeded > FatSize ) {
442 fprintf(stderr,"[ERR] Drive too big to format\n");
443 return -1;
447 Write boot sector, fats
448 Sector 0 Boot Sector
449 Sector 1 FSInfo
450 Sector 2 More boot code - we write zeros here
451 Sector 3 unused
452 Sector 4 unused
453 Sector 5 unused
454 Sector 6 Backup boot sector
455 Sector 7 Backup FSInfo sector
456 Sector 8 Backup 'more boot code'
457 zero'd sectors upto ReservedSectCount
458 FAT1 ReservedSectCount to ReservedSectCount + FatSize
460 FATn ReservedSectCount to ReservedSectCount + FatSize
461 RootDir - allocated to cluster2
464 fprintf(stderr,"[INFO] Heads - %d, sectors/track = %d\n",ipod->num_heads,ipod->sectors_per_track);
465 fprintf(stderr,"[INFO] Size : %lluGB %u sectors\n", ((uint64_t)ipod->pinfo[partition].size * (uint64_t)ipod->sector_size) / (1000*1000*1000), TotalSectors );
466 fprintf(stderr,"[INFO] %d Bytes Per Sector, Cluster size %d bytes\n", BytesPerSect, SectorsPerCluster*BytesPerSect );
467 fprintf(stderr,"[INFO] Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff );
468 fprintf(stderr,"[INFO] %d Reserved Sectors, %d Sectors per FAT, %d fats\n", ReservedSectCount, FatSize, NumFATs );
469 fprintf (stderr,"[INFO] %d Total clusters\n", UserAreaSize/SectorsPerCluster );
471 fprintf(stderr,"[INFO] Formatting partition %d:...\n",partition);
473 /* Once zero_sectors has run, any data on the drive is basically lost... */
474 fprintf(stderr,"[INFO] Clearing out %d sectors for Reserved sectors, fats and root cluster...\n", SystemAreaSize );
476 zero_sectors(ipod, ipod->pinfo[partition].start, SystemAreaSize);
478 fprintf(stderr,"[INFO] Initialising reserved sectors and FATs...\n" );
480 /* Create the boot sector structure */
481 create_boot_sector(sectorbuf, ipod, partition);
482 create_fsinfo(sectorbuf + 512);
484 /* Write boot sector and fsinfo at start of partition */
485 if (ipod_seek(ipod, ipod->pinfo[partition].start * ipod->sector_size) < 0) {
486 fprintf(stderr,"[ERR] Seek failed\n");
487 return -1;
489 if (ipod_write(ipod,sectorbuf,512 * 2) < 0) {
490 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
491 return -1;
494 /* Write backup copy of boot sector and fsinfo */
495 if (ipod_seek(ipod, (ipod->pinfo[partition].start + BackupBootSect) * ipod->sector_size) < 0) {
496 fprintf(stderr,"[ERR] Seek failed\n");
497 return -1;
499 if (ipod_write(ipod,sectorbuf,512 * 2) < 0) {
500 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
501 return -1;
504 /* Create the first FAT sector */
505 create_firstfatsector(sectorbuf);
507 /* Write the first fat sector in the right places */
508 for ( i=0; i<NumFATs; i++ ) {
509 int SectorStart = ReservedSectCount + (i * FatSize );
511 if (ipod_seek(ipod, (ipod->pinfo[partition].start + SectorStart) * ipod->sector_size) < 0) {
512 fprintf(stderr,"[ERR] Seek failed\n");
513 return -1;
516 if (ipod_write(ipod,sectorbuf,512) < 0) {
517 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
518 return -1;
522 fprintf(stderr,"[INFO] Format successful\n");
524 return 0;