added dosfs code to read FAT filesystems
Stefan Schuermans

Stefan Schuermans commited on 2012-05-03 18:43:13
Showing 6 changed files, with 1751 additions and 20 deletions.

... ...
@@ -60,8 +60,8 @@ SRC = $(TARGET).c
60 60
 
61 61
 # If there is more than one source file, append them above, or modify and
62 62
 # uncomment the following:
63
-SRC += arp.c bus.c cf.c checksum.c config.c dhcp.c eeprom.c \
64
-       ethernet.c http.c icmp.c ip.c random.c rtl8019.c \
63
+SRC += arp.c bus.c cf.c checksum.c config.c dhcp.c dosfs.c dosfs_user.c \
64
+       eeprom.c ethernet.c http.c icmp.c ip.c random.c rtl8019.c \
65 65
        ser62500.c status.c tasks.c tcp.c timing.c uart.c udp.c \
66 66
        xtea.c
67 67
 
... ...
@@ -25,15 +25,17 @@
25 25
 
26 26
 // debug version of printf
27 27
 #ifdef DEBUG
28
-#define debug_printf( fmt, arg... ) \
29
-  { \
30
-    static const PROGMEM char fmt_pgm[] = fmt"\r\n"; \
28
+#define debug_printf_nolf( fmt, arg... ) \
29
+  do { \
30
+    static const PROGMEM char fmt_pgm[] = fmt; \
31 31
     char fmt_buf[sizeof( fmt_pgm )]; \
32 32
     for( int i = 0; i < sizeof( fmt_pgm ); i++ ) \
33 33
       fmt_buf[i] = (char)pgm_read_byte_near( (uint16_t)fmt_pgm + i ); \
34 34
     printf( fmt_buf, ##arg ); \
35
-  }
35
+  } while (0)
36
+#define debug_printf( fmt, arg... ) debug_printf_nolf( fmt"\r\n", ##arg )
36 37
 #else
38
+#define debug_printf_nolf( fmt, arg... ) { }
37 39
 #define debug_printf( fmt, arg... ) { }
38 40
 #endif
39 41
 
... ...
@@ -0,0 +1,1249 @@
1
+/*
2
+	DOSFS Embedded FAT-Compatible Filesystem
3
+	(C) 2005 Lewin A.R.W. Edwards (sysadm@zws.com)
4
+
5
+	You are permitted to modify and/or use this code in your own projects without
6
+	payment of royalty, regardless of the license(s) you choose for those projects.
7
+
8
+	You cannot re-copyright or restrict use of the code as released by Lewin Edwards.
9
+*/
10
+
11
+#include <string.h>
12
+#include <stdlib.h>
13
+
14
+#include "dosfs.h"
15
+
16
+/*
17
+	Get starting sector# of specified partition on drive #unit
18
+	NOTE: This code ASSUMES an MBR on the disk.
19
+	scratchsector should point to a SECTOR_SIZE scratch area
20
+	Returns 0xffffffff for any error.
21
+	If pactive is non-NULL, this function also returns the partition active flag.
22
+	If pptype is non-NULL, this function also returns the partition type.
23
+	If psize is non-NULL, this function also returns the partition size.
24
+*/
25
+uint32_t DFS_GetPtnStart(uint8_t unit, uint8_t *scratchsector, uint8_t pnum, uint8_t *pactive, uint8_t *pptype, uint32_t *psize)
26
+{
27
+	uint32_t result;
28
+	PMBR mbr = (PMBR) scratchsector;
29
+
30
+	// DOS ptable supports maximum 4 partitions
31
+	if (pnum > 3)
32
+		return DFS_ERRMISC;
33
+
34
+	// Read MBR from target media
35
+	if (DFS_ReadSector(unit,scratchsector,0,1)) {
36
+		return DFS_ERRMISC;
37
+	}
38
+
39
+	result = (uint32_t) mbr->ptable[pnum].start_0 |
40
+	  (((uint32_t) mbr->ptable[pnum].start_1) << 8) |
41
+	  (((uint32_t) mbr->ptable[pnum].start_2) << 16) |
42
+	  (((uint32_t) mbr->ptable[pnum].start_3) << 24);
43
+
44
+	if (pactive)
45
+		*pactive = mbr->ptable[pnum].active;
46
+
47
+	if (pptype)
48
+		*pptype = mbr->ptable[pnum].type;
49
+
50
+	if (psize)
51
+		*psize = (uint32_t) mbr->ptable[pnum].size_0 |
52
+		  (((uint32_t) mbr->ptable[pnum].size_1) << 8) |
53
+		  (((uint32_t) mbr->ptable[pnum].size_2) << 16) |
54
+		  (((uint32_t) mbr->ptable[pnum].size_3) << 24);
55
+
56
+	return result;
57
+}
58
+
59
+
60
+/*
61
+	Retrieve volume info from BPB and store it in a VOLINFO structure
62
+	You must provide the unit and starting sector of the filesystem, and
63
+	a pointer to a sector buffer for scratch
64
+	Attempts to read BPB and glean information about the FS from that.
65
+	Returns 0 OK, nonzero for any error.
66
+*/
67
+uint32_t DFS_GetVolInfo(uint8_t unit, uint8_t *scratchsector, uint32_t startsector, PVOLINFO volinfo)
68
+{
69
+	PLBR lbr = (PLBR) scratchsector;
70
+	volinfo->unit = unit;
71
+	volinfo->startsector = startsector;
72
+
73
+	if(DFS_ReadSector(unit,scratchsector,startsector,1))
74
+		return DFS_ERRMISC;
75
+
76
+// tag: OEMID, refer dosfs.h
77
+//	strncpy(volinfo->oemid, lbr->oemid, 8);
78
+//	volinfo->oemid[8] = 0;
79
+
80
+	volinfo->secperclus = lbr->bpb.secperclus;
81
+	volinfo->reservedsecs = (uint16_t) lbr->bpb.reserved_l |
82
+		  (((uint16_t) lbr->bpb.reserved_h) << 8);
83
+
84
+	volinfo->numsecs =  (uint16_t) lbr->bpb.sectors_s_l |
85
+		  (((uint16_t) lbr->bpb.sectors_s_h) << 8);
86
+
87
+	if (!volinfo->numsecs)
88
+		volinfo->numsecs = (uint32_t) lbr->bpb.sectors_l_0 |
89
+		  (((uint32_t) lbr->bpb.sectors_l_1) << 8) |
90
+		  (((uint32_t) lbr->bpb.sectors_l_2) << 16) |
91
+		  (((uint32_t) lbr->bpb.sectors_l_3) << 24);
92
+
93
+	// If secperfat is 0, we must be in a FAT32 volume; get secperfat
94
+	// from the FAT32 EBPB. The volume label and system ID string are also
95
+	// in different locations for FAT12/16 vs FAT32.
96
+	volinfo->secperfat =  (uint16_t) lbr->bpb.secperfat_l |
97
+		  (((uint16_t) lbr->bpb.secperfat_h) << 8);
98
+	if (!volinfo->secperfat) {
99
+		volinfo->secperfat = (uint32_t) lbr->ebpb.ebpb32.fatsize_0 |
100
+		  (((uint32_t) lbr->ebpb.ebpb32.fatsize_1) << 8) |
101
+		  (((uint32_t) lbr->ebpb.ebpb32.fatsize_2) << 16) |
102
+		  (((uint32_t) lbr->ebpb.ebpb32.fatsize_3) << 24);
103
+
104
+		memcpy(volinfo->label, lbr->ebpb.ebpb32.label, 11);
105
+		volinfo->label[11] = 0;
106
+	
107
+// tag: OEMID, refer dosfs.h
108
+//		memcpy(volinfo->system, lbr->ebpb.ebpb32.system, 8);
109
+//		volinfo->system[8] = 0; 
110
+	}
111
+	else {
112
+		memcpy(volinfo->label, lbr->ebpb.ebpb.label, 11);
113
+		volinfo->label[11] = 0;
114
+	
115
+// tag: OEMID, refer dosfs.h
116
+//		memcpy(volinfo->system, lbr->ebpb.ebpb.system, 8);
117
+//		volinfo->system[8] = 0; 
118
+	}
119
+
120
+	// note: if rootentries is 0, we must be in a FAT32 volume.
121
+	volinfo->rootentries =  (uint16_t) lbr->bpb.rootentries_l |
122
+		  (((uint16_t) lbr->bpb.rootentries_h) << 8);
123
+
124
+	// after extracting raw info we perform some useful precalculations
125
+	volinfo->fat1 = startsector + volinfo->reservedsecs;
126
+
127
+	// The calculation below is designed to round up the root directory size for FAT12/16
128
+	// and to simply ignore the root directory for FAT32, since it's a normal, expandable
129
+	// file in that situation.
130
+	if (volinfo->rootentries) {
131
+		volinfo->rootdir = volinfo->fat1 + (volinfo->secperfat * 2);
132
+		volinfo->dataarea = volinfo->rootdir + (((volinfo->rootentries * 32) + (SECTOR_SIZE - 1)) / SECTOR_SIZE);
133
+	}
134
+	else {
135
+		volinfo->dataarea = volinfo->fat1 + (volinfo->secperfat * 2);
136
+		volinfo->rootdir = (uint32_t) lbr->ebpb.ebpb32.root_0 |
137
+		  (((uint32_t) lbr->ebpb.ebpb32.root_1) << 8) |
138
+		  (((uint32_t) lbr->ebpb.ebpb32.root_2) << 16) |
139
+		  (((uint32_t) lbr->ebpb.ebpb32.root_3) << 24);
140
+	}
141
+
142
+	// Calculate number of clusters in data area and infer FAT type from this information.
143
+	volinfo->numclusters = (volinfo->numsecs - volinfo->dataarea) / volinfo->secperclus;
144
+	if (volinfo->numclusters < 4085)
145
+		volinfo->filesystem = FAT12;
146
+	else if (volinfo->numclusters < 65525)
147
+		volinfo->filesystem = FAT16;
148
+	else
149
+		volinfo->filesystem = FAT32;
150
+
151
+	return DFS_OK;
152
+}
153
+
154
+/*
155
+	Fetch FAT entry for specified cluster number
156
+	You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
157
+	Returns a FAT32 BAD_CLUSTER value for any error, otherwise the contents of the desired
158
+	FAT entry.
159
+	scratchcache should point to a UINT32. This variable caches the physical sector number
160
+	last read into the scratch buffer for performance enhancement reasons.
161
+*/
162
+uint32_t DFS_GetFAT(PVOLINFO volinfo, uint8_t *scratch, uint32_t *scratchcache, uint32_t cluster)
163
+{
164
+	uint32_t offset, sector, result;
165
+
166
+	if (volinfo->filesystem == FAT12) {
167
+		offset = cluster + (cluster / 2);
168
+	}
169
+	else if (volinfo->filesystem == FAT16) {
170
+		offset = cluster * 2;
171
+	}
172
+	else if (volinfo->filesystem == FAT32) {
173
+		offset = cluster * 4;
174
+	}
175
+	else
176
+		return 0x0ffffff7;	// FAT32 bad cluster	
177
+
178
+	// at this point, offset is the BYTE offset of the desired sector from the start
179
+	// of the FAT. Calculate the physical sector containing this FAT entry.
180
+	sector = ldiv(offset, SECTOR_SIZE).quot + volinfo->fat1;
181
+
182
+	// If this is not the same sector we last read, then read it into RAM
183
+	if (sector != *scratchcache) {
184
+		if(DFS_ReadSector(volinfo->unit, scratch, sector, 1)) {
185
+			// avoid anyone assuming that this cache value is still valid, which
186
+			// might cause disk corruption
187
+			*scratchcache = 0;
188
+			return 0x0ffffff7;	// FAT32 bad cluster	
189
+		}
190
+		*scratchcache = sector;
191
+	}
192
+
193
+	// At this point, we "merely" need to extract the relevant entry.
194
+	// This is easy for FAT16 and FAT32, but a royal PITA for FAT12 as a single entry
195
+	// may span a sector boundary. The normal way around this is always to read two
196
+	// FAT sectors, but that luxury is (by design intent) unavailable to DOSFS.
197
+	offset = ldiv(offset, SECTOR_SIZE).rem;
198
+
199
+	if (volinfo->filesystem == FAT12) {
200
+		// Special case for sector boundary - Store last byte of current sector.
201
+		// Then read in the next sector and put the first byte of that sector into
202
+		// the high byte of result.
203
+		if (offset == SECTOR_SIZE - 1) {
204
+			result = (uint32_t) scratch[offset];
205
+			sector++;
206
+			if(DFS_ReadSector(volinfo->unit, scratch, sector, 1)) {
207
+				// avoid anyone assuming that this cache value is still valid, which
208
+				// might cause disk corruption
209
+				*scratchcache = 0;
210
+				return 0x0ffffff7;	// FAT32 bad cluster	
211
+			}
212
+			*scratchcache = sector;
213
+			// Thanks to Claudio Leonel for pointing out this missing line.
214
+			result |= ((uint32_t) scratch[0]) << 8;
215
+		}
216
+		else {
217
+			result = (uint32_t) scratch[offset] |
218
+			  ((uint32_t) scratch[offset+1]) << 8;
219
+		}
220
+		if (cluster & 1)
221
+			result = result >> 4;
222
+		else
223
+			result = result & 0xfff;
224
+	}
225
+	else if (volinfo->filesystem == FAT16) {
226
+		result = (uint32_t) scratch[offset] |
227
+		  ((uint32_t) scratch[offset+1]) << 8;
228
+	}
229
+	else if (volinfo->filesystem == FAT32) {
230
+		result = ((uint32_t) scratch[offset] |
231
+		  ((uint32_t) scratch[offset+1]) << 8 |
232
+		  ((uint32_t) scratch[offset+2]) << 16 |
233
+		  ((uint32_t) scratch[offset+3]) << 24) & 0x0fffffff;
234
+	}
235
+	else
236
+		result = 0x0ffffff7;	// FAT32 bad cluster	
237
+	return result;
238
+}
239
+
240
+
241
+/*
242
+	Set FAT entry for specified cluster number
243
+	You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
244
+	Returns DFS_ERRMISC for any error, otherwise DFS_OK
245
+	scratchcache should point to a UINT32. This variable caches the physical sector number
246
+	last read into the scratch buffer for performance enhancement reasons.
247
+
248
+	NOTE: This code is HIGHLY WRITE-INEFFICIENT, particularly for flash media. Considerable
249
+	performance gains can be realized by caching the sector. However this is difficult to
250
+	achieve on FAT12 without requiring 2 sector buffers of scratch space, and it is a design
251
+	requirement of this code to operate on a single 512-byte scratch.
252
+
253
+	If you are operating DOSFS over flash, you are strongly advised to implement a writeback
254
+	cache in your physical I/O driver. This will speed up your code significantly and will
255
+	also conserve power and flash write life.
256
+*/
257
+uint32_t DFS_SetFAT(PVOLINFO volinfo, uint8_t *scratch, uint32_t *scratchcache, uint32_t cluster, uint32_t new_contents)
258
+{
259
+	uint32_t offset, sector, result;
260
+	if (volinfo->filesystem == FAT12) {
261
+		offset = cluster + (cluster / 2);
262
+		new_contents &=0xfff;
263
+	}
264
+	else if (volinfo->filesystem == FAT16) {
265
+		offset = cluster * 2;
266
+		new_contents &=0xffff;
267
+	}
268
+	else if (volinfo->filesystem == FAT32) {
269
+		offset = cluster * 4;
270
+		new_contents &=0x0fffffff;	// FAT32 is really "FAT28"
271
+	}
272
+	else
273
+		return DFS_ERRMISC;	
274
+
275
+	// at this point, offset is the BYTE offset of the desired sector from the start
276
+	// of the FAT. Calculate the physical sector containing this FAT entry.
277
+	sector = ldiv(offset, SECTOR_SIZE).quot + volinfo->fat1;
278
+
279
+	// If this is not the same sector we last read, then read it into RAM
280
+	if (sector != *scratchcache) {
281
+		if(DFS_ReadSector(volinfo->unit, scratch, sector, 1)) {
282
+			// avoid anyone assuming that this cache value is still valid, which
283
+			// might cause disk corruption
284
+			*scratchcache = 0;
285
+			return DFS_ERRMISC;
286
+		}
287
+		*scratchcache = sector;
288
+	}
289
+
290
+	// At this point, we "merely" need to extract the relevant entry.
291
+	// This is easy for FAT16 and FAT32, but a royal PITA for FAT12 as a single entry
292
+	// may span a sector boundary. The normal way around this is always to read two
293
+	// FAT sectors, but that luxury is (by design intent) unavailable to DOSFS.
294
+	offset = ldiv(offset, SECTOR_SIZE).rem;
295
+
296
+	if (volinfo->filesystem == FAT12) {
297
+
298
+		// If this is an odd cluster, pre-shift the desired new contents 4 bits to
299
+		// make the calculations below simpler
300
+		if (cluster & 1)
301
+			new_contents = new_contents << 4;
302
+
303
+		// Special case for sector boundary
304
+		if (offset == SECTOR_SIZE - 1) {
305
+
306
+			// Odd cluster: High 12 bits being set
307
+			if (cluster & 1) {
308
+				scratch[offset] = (scratch[offset] & 0x0f) | (new_contents & 0xf0);
309
+			}
310
+			// Even cluster: Low 12 bits being set
311
+			else {
312
+				scratch[offset] = new_contents & 0xff;
313
+			}
314
+			result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
315
+			// mirror the FAT into copy 2
316
+			if (DFS_OK == result)
317
+				result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
318
+
319
+			// If we wrote that sector OK, then read in the subsequent sector
320
+			// and poke the first byte with the remainder of this FAT entry.
321
+			if (DFS_OK == result) {
322
+				scratchcache++;
323
+				result = DFS_ReadSector(volinfo->unit, scratch, *scratchcache, 1);
324
+				if (DFS_OK == result) {
325
+					// Odd cluster: High 12 bits being set
326
+					if (cluster & 1) {
327
+						scratch[0] = new_contents & 0xff00;
328
+					}
329
+					// Even cluster: Low 12 bits being set
330
+					else {
331
+						scratch[0] = (scratch[0] & 0xf0) | (new_contents & 0x0f);
332
+					}
333
+					result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
334
+					// mirror the FAT into copy 2
335
+					if (DFS_OK == result)
336
+						result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
337
+				}
338
+				else {
339
+					// avoid anyone assuming that this cache value is still valid, which
340
+					// might cause disk corruption
341
+					*scratchcache = 0;
342
+				}
343
+			}
344
+		} // if (offset == SECTOR_SIZE - 1)
345
+
346
+		// Not a sector boundary. But we still have to worry about if it's an odd
347
+		// or even cluster number.
348
+		else {
349
+			// Odd cluster: High 12 bits being set
350
+			if (cluster & 1) {
351
+				scratch[offset] = (scratch[offset] & 0x0f) | (new_contents & 0xf0);
352
+				scratch[offset+1] = new_contents & 0xff00;
353
+			}
354
+			// Even cluster: Low 12 bits being set
355
+			else {
356
+				scratch[offset] = new_contents & 0xff;
357
+				scratch[offset+1] = (scratch[offset+1] & 0xf0) | (new_contents & 0x0f);
358
+			}
359
+			result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
360
+			// mirror the FAT into copy 2
361
+			if (DFS_OK == result)
362
+				result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
363
+		}
364
+	}
365
+	else if (volinfo->filesystem == FAT16) {
366
+		scratch[offset] = (new_contents & 0xff);
367
+		scratch[offset+1] = (new_contents & 0xff00) >> 8;
368
+		result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
369
+		// mirror the FAT into copy 2
370
+		if (DFS_OK == result)
371
+			result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
372
+	}
373
+	else if (volinfo->filesystem == FAT32) {
374
+		scratch[offset] = (new_contents & 0xff);
375
+		scratch[offset+1] = (new_contents & 0xff00) >> 8;
376
+		scratch[offset+2] = (new_contents & 0xff0000) >> 16;
377
+		scratch[offset+3] = (scratch[offset+3] & 0xf0) | ((new_contents & 0x0f000000) >> 24);
378
+		// Note well from the above: Per Microsoft's guidelines we preserve the upper
379
+		// 4 bits of the FAT32 cluster value. It's unclear what these bits will be used
380
+		// for; in every example I've encountered they are always zero.
381
+		result = DFS_WriteSector(volinfo->unit, scratch, *scratchcache, 1);
382
+		// mirror the FAT into copy 2
383
+		if (DFS_OK == result)
384
+			result = DFS_WriteSector(volinfo->unit, scratch, (*scratchcache)+volinfo->secperfat, 1);
385
+	}
386
+	else
387
+		result = DFS_ERRMISC;
388
+
389
+	return result;
390
+}
391
+
392
+/*
393
+	Convert a filename element from canonical (8.3) to directory entry (11) form
394
+	src must point to the first non-separator character.
395
+	dest must point to a 12-byte buffer.
396
+*/
397
+uint8_t *DFS_CanonicalToDir(uint8_t *dest, uint8_t *src)
398
+{
399
+	uint8_t *destptr = dest;
400
+
401
+	memset(dest, ' ', 11);
402
+	dest[11] = 0;
403
+
404
+	while (*src && (*src != DIR_SEPARATOR) && (destptr - dest < 11)) {
405
+		if (*src >= 'a' && *src <='z') {
406
+			*destptr++ = (*src - 'a') + 'A';
407
+			src++;
408
+		}
409
+		else if (*src == '.') {
410
+			src++;
411
+			destptr = dest + 8;
412
+		}
413
+		else {
414
+			*destptr++ = *src++;
415
+		}
416
+	}
417
+
418
+	return dest;
419
+}
420
+
421
+/*
422
+	Find the first unused FAT entry
423
+	You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
424
+	Returns a FAT32 BAD_CLUSTER value for any error, otherwise the contents of the desired
425
+	FAT entry.
426
+	Returns FAT32 bad_sector (0x0ffffff7) if there is no free cluster available
427
+*/
428
+uint32_t DFS_GetFreeFAT(PVOLINFO volinfo, uint8_t *scratch)
429
+{
430
+	uint32_t i, result = 0xffffffff, scratchcache = 0;
431
+	
432
+	// Search starts at cluster 2, which is the first usable cluster
433
+	// NOTE: This search can't terminate at a bad cluster, because there might
434
+	// legitimately be bad clusters on the disk.
435
+	for (i=2; i < volinfo->numclusters; i++) {
436
+		result = DFS_GetFAT(volinfo, scratch, &scratchcache, i);
437
+		if (!result) {
438
+			return i;
439
+		}
440
+	}
441
+	return 0x0ffffff7;		// Can't find a free cluster
442
+}
443
+
444
+
445
+/*
446
+	Open a directory for enumeration by DFS_GetNextDirEnt
447
+	You must supply a populated VOLINFO (see DFS_GetVolInfo)
448
+	The empty string or a string containing only the directory separator are
449
+	considered to be the root directory.
450
+	Returns 0 OK, nonzero for any error.
451
+*/
452
+uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo)
453
+{
454
+	// Default behavior is a regular search for existing entries
455
+	dirinfo->flags = 0;
456
+
457
+	if (!strlen((char *) dirname) || (strlen((char *) dirname) == 1 && dirname[0] == DIR_SEPARATOR)) {
458
+		if (volinfo->filesystem == FAT32) {
459
+			dirinfo->currentcluster = volinfo->rootdir;
460
+			dirinfo->currentsector = 0;
461
+			dirinfo->currententry = 0;
462
+
463
+			// read first sector of directory
464
+			return DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((volinfo->rootdir - 2) * volinfo->secperclus), 1);
465
+		}
466
+		else {
467
+			dirinfo->currentcluster = 0;
468
+			dirinfo->currentsector = 0;
469
+			dirinfo->currententry = 0;
470
+
471
+			// read first sector of directory
472
+			return DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir, 1);
473
+		}
474
+	}
475
+
476
+	// This is not the root directory. We need to find the start of this subdirectory.
477
+	// We do this by devious means, using our own companion function DFS_GetNext.
478
+	else {
479
+		uint8_t tmpfn[12];
480
+		uint8_t *ptr = dirname;
481
+		uint32_t result;
482
+		DIRENT de;
483
+
484
+		if (volinfo->filesystem == FAT32) {
485
+			dirinfo->currentcluster = volinfo->rootdir;
486
+			dirinfo->currentsector = 0;
487
+			dirinfo->currententry = 0;
488
+
489
+			// read first sector of directory
490
+			if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((volinfo->rootdir - 2) * volinfo->secperclus), 1))
491
+				return DFS_ERRMISC;
492
+		}
493
+		else {
494
+			dirinfo->currentcluster = 0;
495
+			dirinfo->currentsector = 0;
496
+			dirinfo->currententry = 0;
497
+
498
+			// read first sector of directory
499
+			if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir, 1))
500
+				return DFS_ERRMISC;
501
+		}
502
+
503
+		// skip leading path separators
504
+		while (*ptr == DIR_SEPARATOR && *ptr)
505
+			ptr++;
506
+
507
+		// Scan the path from left to right, finding the start cluster of each entry
508
+		// Observe that this code is inelegant, but obviates the need for recursion.
509
+		while (*ptr) {
510
+			DFS_CanonicalToDir(tmpfn, ptr);
511
+
512
+			de.name[0] = 0;
513
+
514
+			do {
515
+				result = DFS_GetNext(volinfo, dirinfo, &de);
516
+			} while (!result && memcmp(de.name, tmpfn, 11));
517
+
518
+			if (!memcmp(de.name, tmpfn, 11) && ((de.attr & ATTR_DIRECTORY) == ATTR_DIRECTORY)) {
519
+				if (volinfo->filesystem == FAT32) {
520
+					dirinfo->currentcluster = (uint32_t) de.startclus_l_l |
521
+					  ((uint32_t) de.startclus_l_h) << 8 |
522
+					  ((uint32_t) de.startclus_h_l) << 16 |
523
+					  ((uint32_t) de.startclus_h_h) << 24;
524
+				}
525
+				else {
526
+					dirinfo->currentcluster = (uint32_t) de.startclus_l_l |
527
+					  ((uint32_t) de.startclus_l_h) << 8;
528
+				}
529
+				dirinfo->currentsector = 0;
530
+				dirinfo->currententry = 0;
531
+
532
+				if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((dirinfo->currentcluster - 2) * volinfo->secperclus), 1))
533
+					return DFS_ERRMISC;
534
+			}
535
+			else if (!memcmp(de.name, tmpfn, 11) && !(de.attr & ATTR_DIRECTORY))
536
+				return DFS_NOTFOUND;
537
+
538
+			// seek to next item in list
539
+			while (*ptr != DIR_SEPARATOR && *ptr)
540
+				ptr++;
541
+			if (*ptr == DIR_SEPARATOR)
542
+				ptr++;
543
+		}
544
+
545
+		if (!dirinfo->currentcluster)
546
+			return DFS_NOTFOUND;
547
+	}
548
+	return DFS_OK;
549
+}
550
+
551
+/*
552
+	Get next entry in opened directory structure. Copies fields into the dirent
553
+	structure, updates dirinfo. Note that it is the _caller's_ responsibility to
554
+	handle the '.' and '..' entries.
555
+	A deleted file will be returned as a NULL entry (first char of filename=0)
556
+	by this code. Filenames beginning with 0x05 will be translated to 0xE5
557
+	automatically. Long file name entries will be returned as NULL.
558
+	returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid,
559
+	or DFS_ERRMISC for a media error
560
+*/
561
+uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent)
562
+{
563
+	uint32_t tempint;	// required by DFS_GetFAT
564
+
565
+	// Do we need to read the next sector of the directory?
566
+	if (dirinfo->currententry >= SECTOR_SIZE / sizeof(DIRENT)) {
567
+		dirinfo->currententry = 0;
568
+		dirinfo->currentsector++;
569
+
570
+		// Root directory; special case handling 
571
+		// Note that currentcluster will only ever be zero if both:
572
+		// (a) this is the root directory, and
573
+		// (b) we are on a FAT12/16 volume, where the root dir can't be expanded
574
+		if (dirinfo->currentcluster == 0) {
575
+			// Trying to read past end of root directory?
576
+			if (dirinfo->currentsector * (SECTOR_SIZE / sizeof(DIRENT)) >= volinfo->rootentries)
577
+				return DFS_EOF;
578
+
579
+			// Otherwise try to read the next sector
580
+			if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir + dirinfo->currentsector, 1))
581
+				return DFS_ERRMISC;
582
+		}
583
+
584
+		// Normal handling
585
+		else {
586
+			if (dirinfo->currentsector >= volinfo->secperclus) {
587
+				dirinfo->currentsector = 0;
588
+				if ((dirinfo->currentcluster >= 0xff7 &&  volinfo->filesystem == FAT12) ||
589
+				  (dirinfo->currentcluster >= 0xfff7 &&  volinfo->filesystem == FAT16) ||
590
+				  (dirinfo->currentcluster >= 0x0ffffff7 &&  volinfo->filesystem == FAT32)) {
591
+				  
592
+				  	// We are at the end of the directory chain. If this is a normal
593
+				  	// find operation, we should indicate that there is nothing more
594
+				  	// to see.
595
+				  	if (!(dirinfo->flags & DFS_DI_BLANKENT))
596
+						return DFS_EOF;
597
+					
598
+					// On the other hand, if this is a "find free entry" search,
599
+					// we need to tell the caller to allocate a new cluster
600
+					else
601
+						return DFS_ALLOCNEW;
602
+				}
603
+				dirinfo->currentcluster = DFS_GetFAT(volinfo, dirinfo->scratch, &tempint, dirinfo->currentcluster);
604
+			}
605
+			if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((dirinfo->currentcluster - 2) * volinfo->secperclus) + dirinfo->currentsector, 1))
606
+				return DFS_ERRMISC;
607
+		}
608
+	}
609
+
610
+	memcpy(dirent, &(((PDIRENT) dirinfo->scratch)[dirinfo->currententry]), sizeof(DIRENT));
611
+
612
+	if (dirent->name[0] == 0) {		// no more files in this directory
613
+		// If this is a "find blank" then we can reuse this name.
614
+		if (dirinfo->flags & DFS_DI_BLANKENT)
615
+			return DFS_OK;
616
+		else
617
+			return DFS_EOF;
618
+	}
619
+
620
+	if (dirent->name[0] == 0xe5)	// handle deleted file entries
621
+		dirent->name[0] = 0;
622
+	else if ((dirent->attr & ATTR_LONG_NAME) == ATTR_LONG_NAME)
623
+		dirent->name[0] = 0;
624
+	else if (dirent->name[0] == 0x05)	// handle kanji filenames beginning with 0xE5
625
+		dirent->name[0] = 0xe5;
626
+
627
+	dirinfo->currententry++;
628
+
629
+	return DFS_OK;
630
+}
631
+
632
+/*
633
+	INTERNAL
634
+	Find a free directory entry in the directory specified by path
635
+	This function MAY cause a disk write if it is necessary to extend the directory
636
+	size.
637
+	Note - di.scratch must be preinitialized to point to a sector scratch buffer
638
+	de is a scratch structure
639
+	Returns DFS_ERRMISC if a new entry could not be located or created
640
+	de is updated with the same return information you would expect from DFS_GetNext
641
+*/
642
+uint32_t DFS_GetFreeDirEnt(PVOLINFO volinfo, uint8_t *path, PDIRINFO di, PDIRENT de)
643
+{
644
+	uint32_t tempclus,i;
645
+
646
+	if (DFS_OpenDir(volinfo, path, di))
647
+		return DFS_NOTFOUND;
648
+
649
+	// Set "search for empty" flag so DFS_GetNext knows what we're doing
650
+	di->flags |= DFS_DI_BLANKENT;
651
+
652
+	// We seek through the directory looking for an empty entry
653
+	// Note we are reusing tempclus as a temporary result holder.
654
+	tempclus = 0;	
655
+	do {
656
+		tempclus = DFS_GetNext(volinfo, di, de);
657
+
658
+		// Empty entry found
659
+		if (tempclus == DFS_OK && (!de->name[0])) {
660
+			return DFS_OK;
661
+		}
662
+
663
+		// End of root directory reached
664
+		else if (tempclus == DFS_EOF)
665
+			return DFS_ERRMISC;
666
+			
667
+		else if (tempclus == DFS_ALLOCNEW) {
668
+			tempclus = DFS_GetFreeFAT(volinfo, di->scratch);
669
+			if (tempclus == 0x0ffffff7)
670
+				return DFS_ERRMISC;
671
+
672
+			// write out zeroed sectors to the new cluster
673
+			memset(di->scratch, 0, SECTOR_SIZE);
674
+			for (i=0;i<volinfo->secperclus;i++) {
675
+				if (DFS_WriteSector(volinfo->unit, di->scratch, volinfo->dataarea + ((tempclus - 2) * volinfo->secperclus) + i, 1))
676
+					return DFS_ERRMISC;
677
+			}
678
+			// Point old end cluster to newly allocated cluster
679
+			i = 0;
680
+			DFS_SetFAT(volinfo, di->scratch, &i, di->currentcluster, tempclus);
681
+
682
+			// Update DIRINFO so caller knows where to place the new file			
683
+			di->currentcluster = tempclus;
684
+			di->currentsector = 0;
685
+			di->currententry = 1;	// since the code coming after this expects to subtract 1
686
+			
687
+			// Mark newly allocated cluster as end of chain			
688
+			switch(volinfo->filesystem) {
689
+				case FAT12:		tempclus = 0xff8;	break;
690
+				case FAT16:		tempclus = 0xfff8;	break;
691
+				case FAT32:		tempclus = 0x0ffffff8;	break;
692
+				default:		return DFS_ERRMISC;
693
+			}
694
+			DFS_SetFAT(volinfo, di->scratch, &i, di->currentcluster, tempclus);
695
+		}
696
+	} while (!tempclus);
697
+
698
+	// We shouldn't get here
699
+	return DFS_ERRMISC;
700
+}
701
+
702
+/*
703
+	Open a file for reading or writing. You supply populated VOLINFO, a path to the file,
704
+	mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to
705
+	provide a pointer to a sector-sized scratch buffer.
706
+	Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used
707
+	to access the file from this point on.
708
+*/
709
+uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo)
710
+{
711
+	uint8_t tmppath[MAX_PATH];
712
+	uint8_t filename[12];
713
+	uint8_t *p;
714
+	DIRINFO di;
715
+	DIRENT de;
716
+
717
+	// larwe 2006-09-16 +1 zero out file structure
718
+	memset(fileinfo, 0, sizeof(FILEINFO));
719
+
720
+	// save access mode
721
+	fileinfo->mode = mode;
722
+
723
+	// Get a local copy of the path. If it's longer than MAX_PATH, abort.
724
+	strncpy((char *) tmppath, (char *) path, MAX_PATH);
725
+	tmppath[MAX_PATH - 1] = 0;
726
+	if (strcmp((char *) path,(char *) tmppath)) {
727
+		return DFS_PATHLEN;
728
+	}
729
+
730
+	// strip leading path separators
731
+	while (tmppath[0] == DIR_SEPARATOR)
732
+		strcpy((char *) tmppath, (char *) tmppath + 1);
733
+
734
+	// Parse filename off the end of the supplied path
735
+	p = tmppath;
736
+	while (*(p++));
737
+
738
+	p--;
739
+	while (p > tmppath && *p != DIR_SEPARATOR) // larwe 9/16/06 ">=" to ">" bugfix
740
+		p--;
741
+	if (*p == DIR_SEPARATOR)
742
+		p++;
743
+
744
+	DFS_CanonicalToDir(filename, p);
745
+
746
+	if (p > tmppath)
747
+		p--;
748
+	if (*p == DIR_SEPARATOR || p == tmppath) // larwe 9/16/06 +"|| p == tmppath" bugfix
749
+		*p = 0;
750
+
751
+	// At this point, if our path was MYDIR/MYDIR2/FILE.EXT, filename = "FILE    EXT" and
752
+	// tmppath = "MYDIR/MYDIR2".
753
+	di.scratch = scratch;
754
+	if (DFS_OpenDir(volinfo, tmppath, &di))
755
+		return DFS_NOTFOUND;
756
+
757
+	while (!DFS_GetNext(volinfo, &di, &de)) {
758
+		if (!memcmp(de.name, filename, 11)) {
759
+			// You can't use this function call to open a directory.
760
+			if (de.attr & ATTR_DIRECTORY)
761
+				return DFS_NOTFOUND;
762
+
763
+			fileinfo->volinfo = volinfo;
764
+			fileinfo->pointer = 0;
765
+			// The reason we store this extra info about the file is so that we can
766
+			// speedily update the file size, modification date, etc. on a file that is
767
+			// opened for writing.
768
+			if (di.currentcluster == 0)
769
+				fileinfo->dirsector = volinfo->rootdir + di.currentsector;
770
+			else
771
+				fileinfo->dirsector = volinfo->dataarea + ((di.currentcluster - 2) * volinfo->secperclus) + di.currentsector;
772
+			fileinfo->diroffset = di.currententry - 1;
773
+			if (volinfo->filesystem == FAT32) {
774
+				fileinfo->cluster = (uint32_t) de.startclus_l_l |
775
+				  ((uint32_t) de.startclus_l_h) << 8 |
776
+				  ((uint32_t) de.startclus_h_l) << 16 |
777
+				  ((uint32_t) de.startclus_h_h) << 24;
778
+			}
779
+			else {
780
+				fileinfo->cluster = (uint32_t) de.startclus_l_l |
781
+				  ((uint32_t) de.startclus_l_h) << 8;
782
+			}
783
+			fileinfo->firstcluster = fileinfo->cluster;
784
+			fileinfo->filelen = (uint32_t) de.filesize_0 |
785
+			  ((uint32_t) de.filesize_1) << 8 |
786
+			  ((uint32_t) de.filesize_2) << 16 |
787
+			  ((uint32_t) de.filesize_3) << 24;
788
+
789
+			return DFS_OK;
790
+		}
791
+	}
792
+
793
+	// At this point, we KNOW the file does not exist. If the file was opened
794
+	// with write access, we can create it.
795
+	if (mode & DFS_WRITE) {
796
+		uint32_t cluster, temp;
797
+
798
+		// Locate or create a directory entry for this file
799
+		if (DFS_OK != DFS_GetFreeDirEnt(volinfo, tmppath, &di, &de))
800
+			return DFS_ERRMISC;
801
+
802
+		// put sane values in the directory entry
803
+		memset(&de, 0, sizeof(de));
804
+		memcpy(de.name, filename, 11);
805
+		de.crttime_l = 0x20;	// 01:01:00am, Jan 1, 2006.
806
+		de.crttime_h = 0x08;
807
+		de.crtdate_l = 0x11;
808
+		de.crtdate_h = 0x34;
809
+		de.lstaccdate_l = 0x11;
810
+		de.lstaccdate_h = 0x34;
811
+		de.wrttime_l = 0x20;
812
+		de.wrttime_h = 0x08;
813
+		de.wrtdate_l = 0x11;
814
+		de.wrtdate_h = 0x34;
815
+
816
+		// allocate a starting cluster for the directory entry
817
+		cluster = DFS_GetFreeFAT(volinfo, scratch);
818
+
819
+		de.startclus_l_l = cluster & 0xff;
820
+		de.startclus_l_h = (cluster & 0xff00) >> 8;
821
+		de.startclus_h_l = (cluster & 0xff0000) >> 16;
822
+		de.startclus_h_h = (cluster & 0xff000000) >> 24;
823
+
824
+		// update FILEINFO for our caller's sake
825
+		fileinfo->volinfo = volinfo;
826
+		fileinfo->pointer = 0;
827
+		// The reason we store this extra info about the file is so that we can
828
+		// speedily update the file size, modification date, etc. on a file that is
829
+		// opened for writing.
830
+		if (di.currentcluster == 0)
831
+			fileinfo->dirsector = volinfo->rootdir + di.currentsector;
832
+		else
833
+			fileinfo->dirsector = volinfo->dataarea + ((di.currentcluster - 2) * volinfo->secperclus) + di.currentsector;
834
+		fileinfo->diroffset = di.currententry - 1;
835
+		fileinfo->cluster = cluster;
836
+		fileinfo->firstcluster = cluster;
837
+		fileinfo->filelen = 0;
838
+		
839
+		// write the directory entry
840
+		// note that we no longer have the sector containing the directory entry,
841
+		// tragically, so we have to re-read it
842
+		if (DFS_ReadSector(volinfo->unit, scratch, fileinfo->dirsector, 1))
843
+			return DFS_ERRMISC;
844
+		memcpy(&(((PDIRENT) scratch)[di.currententry-1]), &de, sizeof(DIRENT));
845
+		if (DFS_WriteSector(volinfo->unit, scratch, fileinfo->dirsector, 1))
846
+			return DFS_ERRMISC;
847
+
848
+		// Mark newly allocated cluster as end of chain			
849
+		switch(volinfo->filesystem) {
850
+			case FAT12:		cluster = 0xff8;	break;
851
+			case FAT16:		cluster = 0xfff8;	break;
852
+			case FAT32:		cluster = 0x0ffffff8;	break;
853
+			default:		return DFS_ERRMISC;
854
+		}
855
+		temp = 0;
856
+		DFS_SetFAT(volinfo, scratch, &temp, fileinfo->cluster, cluster);
857
+
858
+		return DFS_OK;
859
+	}
860
+
861
+	return DFS_NOTFOUND;
862
+}
863
+
864
+/*
865
+	Read an open file
866
+	You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
867
+	pointer to a SECTOR_SIZE scratch buffer.
868
+	Note that returning DFS_EOF is not an error condition. This function updates the
869
+	successcount field with the number of bytes actually read.
870
+*/
871
+uint32_t DFS_ReadFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len)
872
+{
873
+	uint32_t remain;
874
+	uint32_t result = DFS_OK;
875
+	uint32_t sector;
876
+	uint32_t bytesread;
877
+
878
+	// Don't try to read past EOF
879
+	if (len > fileinfo->filelen - fileinfo->pointer)
880
+		len = fileinfo->filelen - fileinfo->pointer;
881
+
882
+	remain = len;
883
+	*successcount = 0;
884
+
885
+	while (remain && result == DFS_OK) {
886
+		// This is a bit complicated. The sector we want to read is addressed at a cluster
887
+		// granularity by the fileinfo->cluster member. The file pointer tells us how many
888
+		// extra sectors to add to that number.
889
+		sector = fileinfo->volinfo->dataarea +
890
+		  ((fileinfo->cluster - 2) * fileinfo->volinfo->secperclus) +
891
+		  div(div(fileinfo->pointer,fileinfo->volinfo->secperclus * SECTOR_SIZE).rem, SECTOR_SIZE).quot;
892
+
893
+		// Case 1 - File pointer is not on a sector boundary
894
+		if (div(fileinfo->pointer, SECTOR_SIZE).rem) {
895
+			uint16_t tempreadsize;
896
+
897
+			// We always have to go through scratch in this case
898
+			result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
899
+
900
+			// This is the number of bytes that we actually care about in the sector
901
+			// just read.
902
+			tempreadsize = SECTOR_SIZE - (div(fileinfo->pointer, SECTOR_SIZE).rem);
903
+					
904
+			// Case 1A - We want the entire remainder of the sector. After this
905
+			// point, all passes through the read loop will be aligned on a sector
906
+			// boundary, which allows us to go through the optimal path 2A below.
907
+		   	if (remain >= tempreadsize) {
908
+				memcpy(buffer, scratch + (SECTOR_SIZE - tempreadsize), tempreadsize);
909
+				bytesread = tempreadsize;
910
+				buffer += tempreadsize;
911
+				fileinfo->pointer += tempreadsize;
912
+				remain -= tempreadsize;
913
+			}
914
+			// Case 1B - This read concludes the file read operation
915
+			else {
916
+				memcpy(buffer, scratch + (SECTOR_SIZE - tempreadsize), remain);
917
+
918
+				buffer += remain;
919
+				fileinfo->pointer += remain;
920
+				bytesread = remain;
921
+				remain = 0;
922
+			}
923
+		}
924
+		// Case 2 - File pointer is on sector boundary
925
+		else {
926
+			// Case 2A - We have at least one more full sector to read and don't have
927
+			// to go through the scratch buffer. You could insert optimizations here to
928
+			// read multiple sectors at a time, if you were thus inclined (note that
929
+			// the maximum multi-read you could perform is a single cluster, so it would
930
+			// be advantageous to have code similar to case 1A above that would round the
931
+			// pointer to a cluster boundary the first pass through, so all subsequent
932
+			// [large] read requests would be able to go a cluster at a time).
933
+			if (remain >= SECTOR_SIZE) {
934
+				result = DFS_ReadSector(fileinfo->volinfo->unit, buffer, sector, 1);
935
+				remain -= SECTOR_SIZE;
936
+				buffer += SECTOR_SIZE;
937
+				fileinfo->pointer += SECTOR_SIZE;
938
+				bytesread = SECTOR_SIZE;
939
+			}
940
+			// Case 2B - We are only reading a partial sector
941
+			else {
942
+				result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
943
+				memcpy(buffer, scratch, remain);
944
+				buffer += remain;
945
+				fileinfo->pointer += remain;
946
+				bytesread = remain;
947
+				remain = 0;
948
+			}
949
+		}
950
+
951
+		*successcount += bytesread;
952
+
953
+		// check to see if we stepped over a cluster boundary
954
+		if (div(fileinfo->pointer - bytesread, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot !=
955
+		  div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
956
+			// An act of minor evil - we use bytesread as a scratch integer, knowing that
957
+			// its value is not used after updating *successcount above
958
+			bytesread = 0;
959
+			if (((fileinfo->volinfo->filesystem == FAT12) && (fileinfo->cluster >= 0xff8)) ||
960
+			  ((fileinfo->volinfo->filesystem == FAT16) && (fileinfo->cluster >= 0xfff8)) ||
961
+			  ((fileinfo->volinfo->filesystem == FAT32) && (fileinfo->cluster >= 0x0ffffff8)))
962
+				result = DFS_EOF;
963
+			else
964
+				fileinfo->cluster = DFS_GetFAT(fileinfo->volinfo, scratch, &bytesread, fileinfo->cluster);
965
+		}
966
+	}
967
+	
968
+	return result;
969
+}
970
+
971
+/*
972
+	Seek file pointer to a given position
973
+	This function does not return status - refer to the fileinfo->pointer value
974
+	to see where the pointer wound up.
975
+	Requires a SECTOR_SIZE scratch buffer
976
+*/
977
+void DFS_Seek(PFILEINFO fileinfo, uint32_t offset, uint8_t *scratch)
978
+{
979
+	uint32_t tempint;
980
+
981
+	// larwe 9/16/06 bugfix split case 0a/0b and changed fallthrough handling
982
+	// Case 0a - Return immediately for degenerate case
983
+	if (offset == fileinfo->pointer) {
984
+		return;
985
+	}
986
+	
987
+	// Case 0b - Don't allow the user to seek past the end of the file
988
+	if (offset > fileinfo->filelen) {
989
+		offset = fileinfo->filelen;
990
+		// NOTE NO RETURN HERE!
991
+	}
992
+
993
+	// Case 1 - Simple rewind to start
994
+	// Note _intentional_ fallthrough from Case 0b above
995
+	if (offset == 0) {
996
+		fileinfo->cluster = fileinfo->firstcluster;
997
+		fileinfo->pointer = 0;
998
+		return;		// larwe 9/16/06 +1 bugfix
999
+	}
1000
+	// Case 2 - Seeking backwards. Need to reset and seek forwards
1001
+	else if (offset < fileinfo->pointer) {
1002
+		fileinfo->cluster = fileinfo->firstcluster;
1003
+		fileinfo->pointer = 0;
1004
+		// NOTE NO RETURN HERE!
1005
+	}
1006
+
1007
+	// Case 3 - Seeking forwards
1008
+	// Note _intentional_ fallthrough from Case 2 above
1009
+
1010
+	// Case 3a - Seek size does not cross cluster boundary - 
1011
+	// very simple case
1012
+	// larwe 9/16/06 changed .rem to .quot in both div calls, bugfix
1013
+	if (div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot ==
1014
+	  div(fileinfo->pointer + offset, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
1015
+		fileinfo->pointer = offset;
1016
+	}
1017
+	// Case 3b - Seeking across cluster boundary(ies)
1018
+	else {
1019
+		// round file pointer down to cluster boundary
1020
+		fileinfo->pointer = div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot *
1021
+		  fileinfo->volinfo->secperclus * SECTOR_SIZE;
1022
+
1023
+		// seek by clusters
1024
+		// larwe 9/30/06 bugfix changed .rem to .quot in both div calls
1025
+		while (div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot !=
1026
+		  div(fileinfo->pointer + offset, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
1027
+
1028
+			fileinfo->cluster = DFS_GetFAT(fileinfo->volinfo, scratch, &tempint, fileinfo->cluster);
1029
+			// Abort if there was an error
1030
+			if (fileinfo->cluster == 0x0ffffff7) {
1031
+				fileinfo->pointer = 0;
1032
+				fileinfo->cluster = fileinfo->firstcluster;
1033
+				return;
1034
+			}
1035
+			fileinfo->pointer += SECTOR_SIZE * fileinfo->volinfo->secperclus;
1036
+		}
1037
+
1038
+		// since we know the cluster is right, we have no more work to do
1039
+		fileinfo->pointer = offset;
1040
+	}
1041
+}
1042
+
1043
+/*
1044
+	Delete a file
1045
+	scratch must point to a sector-sized buffer
1046
+*/
1047
+uint32_t DFS_UnlinkFile(PVOLINFO volinfo, uint8_t *path, uint8_t *scratch)
1048
+{
1049
+	FILEINFO fi;
1050
+	uint32_t cache = 0;
1051
+	uint32_t tempclus;
1052
+
1053
+	// DFS_OpenFile gives us all the information we need to delete it
1054
+	if (DFS_OK != DFS_OpenFile(volinfo, path, DFS_READ, scratch, &fi))
1055
+		return DFS_NOTFOUND;
1056
+
1057
+	// First, read the directory sector and delete that entry
1058
+	if (DFS_ReadSector(volinfo->unit, scratch, fi.dirsector, 1))
1059
+		return DFS_ERRMISC;
1060
+	((PDIRENT) scratch)[fi.diroffset].name[0] = 0xe5;
1061
+	if (DFS_WriteSector(volinfo->unit, scratch, fi.dirsector, 1))
1062
+		return DFS_ERRMISC;
1063
+
1064
+	// Now follow the cluster chain to free the file space
1065
+	while (!((volinfo->filesystem == FAT12 && fi.firstcluster >= 0x0ff7) ||
1066
+	  (volinfo->filesystem == FAT16 && fi.firstcluster >= 0xfff7) ||
1067
+	  (volinfo->filesystem == FAT32 && fi.firstcluster >= 0x0ffffff7))) {
1068
+		tempclus = fi.firstcluster;
1069
+
1070
+		fi.firstcluster = DFS_GetFAT(volinfo, scratch, &cache, fi.firstcluster);
1071
+		DFS_SetFAT(volinfo, scratch, &cache, tempclus, 0);
1072
+
1073
+	}
1074
+	return DFS_OK;
1075
+}
1076
+
1077
+
1078
+/*
1079
+	Write an open file
1080
+	You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
1081
+	pointer to a SECTOR_SIZE scratch buffer.
1082
+	This function updates the successcount field with the number of bytes actually written.
1083
+*/
1084
+uint32_t DFS_WriteFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len)
1085
+{
1086
+	uint32_t remain;
1087
+	uint32_t result = DFS_OK;
1088
+	uint32_t sector;
1089
+	uint32_t byteswritten;
1090
+
1091
+	// Don't allow writes to a file that's open as readonly
1092
+	if (!(fileinfo->mode & DFS_WRITE))
1093
+		return DFS_ERRMISC;
1094
+
1095
+	remain = len;
1096
+	*successcount = 0;
1097
+
1098
+	while (remain && result == DFS_OK) {
1099
+		// This is a bit complicated. The sector we want to read is addressed at a cluster
1100
+		// granularity by the fileinfo->cluster member. The file pointer tells us how many
1101
+		// extra sectors to add to that number.
1102
+		sector = fileinfo->volinfo->dataarea +
1103
+		  ((fileinfo->cluster - 2) * fileinfo->volinfo->secperclus) +
1104
+		  div(div(fileinfo->pointer,fileinfo->volinfo->secperclus * SECTOR_SIZE).rem, SECTOR_SIZE).quot;
1105
+
1106
+		// Case 1 - File pointer is not on a sector boundary
1107
+		if (div(fileinfo->pointer, SECTOR_SIZE).rem) {
1108
+			uint16_t tempsize;
1109
+
1110
+			// We always have to go through scratch in this case
1111
+			result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
1112
+
1113
+			// This is the number of bytes that we don't want to molest in the
1114
+			// scratch sector just read.
1115
+			tempsize = div(fileinfo->pointer, SECTOR_SIZE).rem;
1116
+					
1117
+			// Case 1A - We are writing the entire remainder of the sector. After
1118
+			// this point, all passes through the read loop will be aligned on a
1119
+			// sector boundary, which allows us to go through the optimal path
1120
+			// 2A below.
1121
+		   	if (remain >= SECTOR_SIZE - tempsize) {
1122
+				memcpy(scratch + tempsize, buffer, SECTOR_SIZE - tempsize);
1123
+				if (!result)
1124
+					result = DFS_WriteSector(fileinfo->volinfo->unit, scratch, sector, 1);
1125
+
1126
+				byteswritten = SECTOR_SIZE - tempsize;
1127
+				buffer += SECTOR_SIZE - tempsize;
1128
+				fileinfo->pointer += SECTOR_SIZE - tempsize;
1129
+				if (fileinfo->filelen < fileinfo->pointer) {
1130
+					fileinfo->filelen = fileinfo->pointer;
1131
+				}
1132
+				remain -= SECTOR_SIZE - tempsize;
1133
+			}
1134
+			// Case 1B - This concludes the file write operation
1135
+			else {
1136
+				memcpy(scratch + tempsize, buffer, remain);
1137
+				if (!result)
1138
+					result = DFS_WriteSector(fileinfo->volinfo->unit, scratch, sector, 1);
1139
+
1140
+				buffer += remain;
1141
+				fileinfo->pointer += remain;
1142
+				if (fileinfo->filelen < fileinfo->pointer) {
1143
+					fileinfo->filelen = fileinfo->pointer;
1144
+				}
1145
+				byteswritten = remain;
1146
+				remain = 0;
1147
+			}
1148
+		} // case 1
1149
+		// Case 2 - File pointer is on sector boundary
1150
+		else {
1151
+			// Case 2A - We have at least one more full sector to write and don't have
1152
+			// to go through the scratch buffer. You could insert optimizations here to
1153
+			// write multiple sectors at a time, if you were thus inclined. Refer to
1154
+			// similar notes in DFS_ReadFile.
1155
+			if (remain >= SECTOR_SIZE) {
1156
+				result = DFS_WriteSector(fileinfo->volinfo->unit, buffer, sector, 1);
1157
+				remain -= SECTOR_SIZE;
1158
+				buffer += SECTOR_SIZE;
1159
+				fileinfo->pointer += SECTOR_SIZE;
1160
+				if (fileinfo->filelen < fileinfo->pointer) {
1161
+					fileinfo->filelen = fileinfo->pointer;
1162
+				}
1163
+				byteswritten = SECTOR_SIZE;
1164
+			}
1165
+			// Case 2B - We are only writing a partial sector and potentially need to
1166
+			// go through the scratch buffer.
1167
+			else {
1168
+				// If the current file pointer is not yet at or beyond the file
1169
+				// length, we are writing somewhere in the middle of the file and
1170
+				// need to load the original sector to do a read-modify-write.
1171
+				if (fileinfo->pointer < fileinfo->filelen) {
1172
+					result = DFS_ReadSector(fileinfo->volinfo->unit, scratch, sector, 1);
1173
+					if (!result) {
1174
+						memcpy(scratch, buffer, remain);
1175
+						result = DFS_WriteSector(fileinfo->volinfo->unit, scratch, sector, 1);
1176
+					}
1177
+				}
1178
+				else {
1179
+					result = DFS_WriteSector(fileinfo->volinfo->unit, buffer, sector, 1);
1180
+				}
1181
+
1182
+				buffer += remain;
1183
+				fileinfo->pointer += remain;
1184
+				if (fileinfo->filelen < fileinfo->pointer) {
1185
+					fileinfo->filelen = fileinfo->pointer;
1186
+				}
1187
+				byteswritten = remain;
1188
+				remain = 0;
1189
+			}
1190
+		}
1191
+
1192
+		*successcount += byteswritten;
1193
+
1194
+		// check to see if we stepped over a cluster boundary
1195
+		if (div(fileinfo->pointer - byteswritten, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot !=
1196
+		  div(fileinfo->pointer, fileinfo->volinfo->secperclus * SECTOR_SIZE).quot) {
1197
+		  	uint32_t lastcluster;
1198
+
1199
+		  	// We've transgressed into another cluster. If we were already at EOF,
1200
+		  	// we need to allocate a new cluster.
1201
+			// An act of minor evil - we use byteswritten as a scratch integer, knowing
1202
+			// that its value is not used after updating *successcount above
1203
+			byteswritten = 0;
1204
+
1205
+			lastcluster = fileinfo->cluster;
1206
+			fileinfo->cluster = DFS_GetFAT(fileinfo->volinfo, scratch, &byteswritten, fileinfo->cluster);
1207
+			
1208
+			// Allocate a new cluster?
1209
+			if (((fileinfo->volinfo->filesystem == FAT12) && (fileinfo->cluster >= 0xff8)) ||
1210
+			  ((fileinfo->volinfo->filesystem == FAT16) && (fileinfo->cluster >= 0xfff8)) ||
1211
+			  ((fileinfo->volinfo->filesystem == FAT32) && (fileinfo->cluster >= 0x0ffffff8))) {
1212
+			  	uint32_t tempclus;
1213
+
1214
+				tempclus = DFS_GetFreeFAT(fileinfo->volinfo, scratch);
1215
+				byteswritten = 0; // invalidate cache
1216
+				if (tempclus == 0x0ffffff7)
1217
+					return DFS_ERRMISC;
1218
+
1219
+				// Link new cluster onto file
1220
+				DFS_SetFAT(fileinfo->volinfo, scratch, &byteswritten, lastcluster, tempclus);
1221
+				fileinfo->cluster = tempclus;
1222
+
1223
+				// Mark newly allocated cluster as end of chain			
1224
+				switch(fileinfo->volinfo->filesystem) {
1225
+					case FAT12:		tempclus = 0xff8;	break;
1226
+					case FAT16:		tempclus = 0xfff8;	break;
1227
+					case FAT32:		tempclus = 0x0ffffff8;	break;
1228
+					default:		return DFS_ERRMISC;
1229
+				}
1230
+				DFS_SetFAT(fileinfo->volinfo, scratch, &byteswritten, fileinfo->cluster, tempclus);
1231
+
1232
+				result = DFS_OK;
1233
+			}
1234
+			// No else clause is required.
1235
+		}
1236
+	}
1237
+	
1238
+	// Update directory entry
1239
+		if (DFS_ReadSector(fileinfo->volinfo->unit, scratch, fileinfo->dirsector, 1))
1240
+			return DFS_ERRMISC;
1241
+		((PDIRENT) scratch)[fileinfo->diroffset].filesize_0 = fileinfo->filelen & 0xff;
1242
+		((PDIRENT) scratch)[fileinfo->diroffset].filesize_1 = (fileinfo->filelen & 0xff00) >> 8;
1243
+		((PDIRENT) scratch)[fileinfo->diroffset].filesize_2 = (fileinfo->filelen & 0xff0000) >> 16;
1244
+		((PDIRENT) scratch)[fileinfo->diroffset].filesize_3 = (fileinfo->filelen & 0xff000000) >> 24;
1245
+		if (DFS_WriteSector(fileinfo->volinfo->unit, scratch, fileinfo->dirsector, 1))
1246
+			return DFS_ERRMISC;
1247
+	return result;
1248
+}
1249
+
... ...
@@ -0,0 +1,369 @@
1
+/*
2
+	DOSFS Embedded FAT-Compatible Filesystem
3
+	(C) 2005 Lewin A.R.W. Edwards (sysadm@zws.com)
4
+*/
5
+
6
+#ifndef _DOSFS_H
7
+#define _DOSFS_H
8
+
9
+#include <stdint.h>
10
+
11
+//===================================================================
12
+// User-supplied functions
13
+uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
14
+uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
15
+
16
+
17
+//===================================================================
18
+// Configurable items
19
+#define MAX_PATH		64		// Maximum path length (increasing this will
20
+								// GREATLY increase stack requirements!)
21
+#define DIR_SEPARATOR	'/'		// character separating directory components
22
+
23
+// End of configurable items
24
+//===================================================================
25
+
26
+//===================================================================
27
+// 32-bit error codes
28
+#define DFS_OK			0			// no error
29
+#define DFS_EOF			1			// end of file (not an error)
30
+#define DFS_WRITEPROT	2			// volume is write protected
31
+#define DFS_NOTFOUND	3			// path or file not found
32
+#define DFS_PATHLEN		4			// path too long
33
+#define DFS_ALLOCNEW	5			// must allocate new directory cluster
34
+#define DFS_ERRMISC		0xffffffff	// generic error
35
+
36
+//===================================================================
37
+// File access modes
38
+#define DFS_READ		1			// read-only
39
+#define DFS_WRITE		2			// write-only
40
+
41
+//===================================================================
42
+// Miscellaneous constants
43
+#define SECTOR_SIZE		512		// sector size in bytes
44
+
45
+//===================================================================
46
+// Internal subformat identifiers
47
+#define FAT12			0
48
+#define FAT16			1
49
+#define FAT32			2
50
+
51
+//===================================================================
52
+// DOS attribute bits
53
+#define ATTR_READ_ONLY	0x01
54
+#define ATTR_HIDDEN		0x02
55
+#define ATTR_SYSTEM		0x04
56
+#define ATTR_VOLUME_ID	0x08
57
+#define ATTR_DIRECTORY	0x10
58
+#define ATTR_ARCHIVE	0x20
59
+#define ATTR_LONG_NAME	(ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)
60
+
61
+
62
+/*
63
+	Directory entry structure
64
+	note: if name[0] == 0xe5, this is a free dir entry
65
+	      if name[0] == 0x00, this is a free entry and all subsequent entries are free
66
+		  if name[0] == 0x05, the first character of the name is 0xe5 [a kanji nicety]
67
+
68
+	Date format: bit 0-4  = day of month (1-31)
69
+	             bit 5-8  = month, 1=Jan..12=Dec
70
+				 bit 9-15 =	count of years since 1980 (0-127)
71
+	Time format: bit 0-4  = 2-second count, (0-29)
72
+	             bit 5-10 = minutes (0-59)
73
+				 bit 11-15= hours (0-23)
74
+*/
75
+typedef struct _tagDIRENT {
76
+	uint8_t name[11];			// filename
77
+	uint8_t attr;				// attributes (see ATTR_* constant definitions)
78
+	uint8_t reserved;			// reserved, must be 0
79
+	uint8_t crttimetenth;		// create time, 10ths of a second (0-199 are valid)
80
+	uint8_t crttime_l;			// creation time low byte
81
+	uint8_t crttime_h;			// creation time high byte
82
+	uint8_t crtdate_l;			// creation date low byte
83
+	uint8_t crtdate_h;			// creation date high byte
84
+	uint8_t lstaccdate_l;		// last access date low byte
85
+	uint8_t lstaccdate_h;		// last access date high byte
86
+	uint8_t startclus_h_l;		// high word of first cluster, low byte (FAT32)
87
+	uint8_t startclus_h_h;		// high word of first cluster, high byte (FAT32)
88
+	uint8_t wrttime_l;			// last write time low byte
89
+	uint8_t wrttime_h;			// last write time high byte
90
+	uint8_t wrtdate_l;			// last write date low byte
91
+	uint8_t wrtdate_h;			// last write date high byte
92
+	uint8_t startclus_l_l;		// low word of first cluster, low byte
93
+	uint8_t startclus_l_h;		// low word of first cluster, high byte
94
+	uint8_t filesize_0;			// file size, low byte
95
+	uint8_t filesize_1;			//
96
+	uint8_t filesize_2;			//
97
+	uint8_t filesize_3;			// file size, high byte
98
+} DIRENT, *PDIRENT;
99
+
100
+/*
101
+	Partition table entry structure
102
+*/
103
+typedef struct _tagPTINFO {
104
+	uint8_t		active;			// 0x80 if partition active
105
+	uint8_t		start_h;		// starting head
106
+	uint8_t		start_cs_l;		// starting cylinder and sector (low byte)
107
+	uint8_t		start_cs_h;		// starting cylinder and sector (high byte)
108
+	uint8_t		type;			// type ID byte
109
+	uint8_t		end_h;			// ending head
110
+	uint8_t		end_cs_l;		// ending cylinder and sector (low byte)
111
+	uint8_t		end_cs_h;		// ending cylinder and sector (high byte)
112
+	uint8_t		start_0;		// starting sector# (low byte)
113
+	uint8_t		start_1;		//
114
+	uint8_t		start_2;		//
115
+	uint8_t		start_3;		// starting sector# (high byte)
116
+	uint8_t		size_0;			// size of partition (low byte)
117
+	uint8_t		size_1;			//
118
+	uint8_t		size_2;			//
119
+	uint8_t		size_3;			// size of partition (high byte)
120
+} PTINFO, *PPTINFO;
121
+
122
+/*
123
+	Master Boot Record structure
124
+*/
125
+typedef struct _tagMBR {
126
+	uint8_t bootcode[0x1be];	// boot sector
127
+	PTINFO ptable[4];			// four partition table structures
128
+	uint8_t sig_55;				// 0x55 signature byte
129
+	uint8_t sig_aa;				// 0xaa signature byte
130
+} MBR, *PMBR;
131
+
132
+/*
133
+	BIOS Parameter Block structure (FAT12/16)
134
+*/
135
+typedef struct _tagBPB {
136
+	uint8_t bytepersec_l;		// bytes per sector low byte (0x00)
137
+	uint8_t bytepersec_h;		// bytes per sector high byte (0x02)
138
+	uint8_t	secperclus;			// sectors per cluster (1,2,4,8,16,32,64,128 are valid)
139
+	uint8_t reserved_l;			// reserved sectors low byte
140
+	uint8_t reserved_h;			// reserved sectors high byte
141
+	uint8_t numfats;			// number of FAT copies (2)
142
+	uint8_t rootentries_l;		// number of root dir entries low byte (0x00 normally)
143
+	uint8_t rootentries_h;		// number of root dir entries high byte (0x02 normally)
144
+	uint8_t sectors_s_l;		// small num sectors low byte
145
+	uint8_t sectors_s_h;		// small num sectors high byte
146
+	uint8_t mediatype;			// media descriptor byte
147
+	uint8_t secperfat_l;		// sectors per FAT low byte
148
+	uint8_t secperfat_h;		// sectors per FAT high byte
149
+	uint8_t secpertrk_l;		// sectors per track low byte
150
+	uint8_t secpertrk_h;		// sectors per track high byte
151
+	uint8_t heads_l;			// heads low byte
152
+	uint8_t heads_h;			// heads high byte
153
+	uint8_t hidden_0;			// hidden sectors low byte
154
+	uint8_t hidden_1;			// (note - this is the number of MEDIA sectors before
155
+	uint8_t hidden_2;			// first sector of VOLUME - we rely on the MBR instead)
156
+	uint8_t hidden_3;			// hidden sectors high byte
157
+	uint8_t sectors_l_0;		// large num sectors low byte
158
+	uint8_t sectors_l_1;		//
159
+	uint8_t sectors_l_2;		//
160
+	uint8_t sectors_l_3;		// large num sectors high byte
161
+} BPB, *PBPB;
162
+
163
+/*
164
+	Extended BIOS Parameter Block structure (FAT12/16)
165
+*/
166
+typedef struct _tagEBPB {
167
+	uint8_t unit;				// int 13h drive#
168
+	uint8_t head;				// archaic, used by Windows NT-class OSes for flags
169
+	uint8_t signature;			// 0x28 or 0x29
170
+	uint8_t serial_0;			// serial#
171
+	uint8_t serial_1;			// serial#
172
+	uint8_t serial_2;			// serial#
173
+	uint8_t serial_3;			// serial#
174
+	uint8_t label[11];			// volume label
175
+	uint8_t system[8];			// filesystem ID
176
+} EBPB, *PEBPB;
177
+
178
+/*
179
+	Extended BIOS Parameter Block structure (FAT32)
180
+*/
181
+typedef struct _tagEBPB32 {
182
+	uint8_t fatsize_0;			// big FAT size in sectors low byte
183
+	uint8_t fatsize_1;			//
184
+	uint8_t fatsize_2;			//
185
+	uint8_t fatsize_3;			// big FAT size in sectors high byte
186
+	uint8_t extflags_l;			// extended flags low byte
187
+	uint8_t extflags_h;			// extended flags high byte
188
+	uint8_t fsver_l;			// filesystem version (0x00) low byte
189
+	uint8_t fsver_h;			// filesystem version (0x00) high byte
190
+	uint8_t root_0;				// cluster of root dir, low byte
191
+	uint8_t root_1;				//
192
+	uint8_t root_2;				//
193
+	uint8_t root_3;				// cluster of root dir, high byte
194
+	uint8_t fsinfo_l;			// sector pointer to FSINFO within reserved area, low byte (2)
195
+	uint8_t fsinfo_h;			// sector pointer to FSINFO within reserved area, high byte (0)
196
+	uint8_t bkboot_l;			// sector pointer to backup boot sector within reserved area, low byte (6)
197
+	uint8_t bkboot_h;			// sector pointer to backup boot sector within reserved area, high byte (0)
198
+	uint8_t reserved[12];		// reserved, should be 0
199
+
200
+	uint8_t unit;				// int 13h drive#
201
+	uint8_t head;				// archaic, used by Windows NT-class OSes for flags
202
+	uint8_t signature;			// 0x28 or 0x29
203
+	uint8_t serial_0;			// serial#
204
+	uint8_t serial_1;			// serial#
205
+	uint8_t serial_2;			// serial#
206
+	uint8_t serial_3;			// serial#
207
+	uint8_t label[11];			// volume label
208
+	uint8_t system[8];			// filesystem ID
209
+} EBPB32, *PEBPB32;
210
+
211
+/*
212
+	Logical Boot Record structure (volume boot sector)
213
+*/
214
+typedef struct _tagLBR {
215
+	uint8_t jump[3];			// JMP instruction
216
+	uint8_t oemid[8];			// OEM ID, space-padded
217
+	BPB bpb;					// BIOS Parameter Block
218
+	union {
219
+		EBPB ebpb;				// FAT12/16 Extended BIOS Parameter Block
220
+		EBPB32 ebpb32;			// FAT32 Extended BIOS Parameter Block
221
+	} ebpb;
222
+	uint8_t code[420];			// boot sector code
223
+	uint8_t sig_55;				// 0x55 signature byte
224
+	uint8_t sig_aa;				// 0xaa signature byte
225
+} LBR, *PLBR;
226
+
227
+/*
228
+	Volume information structure (Internal to DOSFS)
229
+*/
230
+typedef struct _tagVOLINFO {
231
+	uint8_t unit;				// unit on which this volume resides
232
+	uint8_t filesystem;			// formatted filesystem
233
+
234
+// These two fields aren't very useful, so support for them has been commented out to
235
+// save memory. (Note that the "system" tag is not actually used by DOS to determine
236
+// filesystem type - that decision is made entirely on the basis of how many clusters
237
+// the drive contains. DOSFS works the same way).
238
+// See tag: OEMID in dosfs.c
239
+//	uint8_t oemid[9];			// OEM ID ASCIIZ
240
+//	uint8_t system[9];			// system ID ASCIIZ
241
+	uint8_t label[12];			// volume label ASCIIZ
242
+	uint32_t startsector;		// starting sector of filesystem
243
+	uint8_t secperclus;			// sectors per cluster
244
+	uint16_t reservedsecs;		// reserved sectors
245
+	uint32_t numsecs;			// number of sectors in volume
246
+	uint32_t secperfat;			// sectors per FAT
247
+	uint16_t rootentries;		// number of root dir entries
248
+
249
+	uint32_t numclusters;		// number of clusters on drive
250
+
251
+	// The fields below are PHYSICAL SECTOR NUMBERS.
252
+	uint32_t fat1;				// starting sector# of FAT copy 1
253
+	uint32_t rootdir;			// starting sector# of root directory (FAT12/FAT16) or cluster (FAT32)
254
+	uint32_t dataarea;			// starting sector# of data area (cluster #2)
255
+} VOLINFO, *PVOLINFO;
256
+
257
+/*
258
+	Flags in DIRINFO.flags
259
+*/
260
+#define DFS_DI_BLANKENT		0x01	// Searching for blank entry
261
+
262
+/*
263
+	Directory search structure (Internal to DOSFS)
264
+*/
265
+typedef struct _tagDIRINFO {
266
+	uint32_t currentcluster;	// current cluster in dir
267
+	uint8_t currentsector;		// current sector in cluster
268
+	uint8_t currententry;		// current dir entry in sector
269
+	uint8_t *scratch;			// ptr to user-supplied scratch buffer (one sector)
270
+	uint8_t flags;				// internal DOSFS flags
271
+} DIRINFO, *PDIRINFO;
272
+
273
+/*
274
+	File handle structure (Internal to DOSFS)
275
+*/
276
+typedef struct _tagFILEINFO {
277
+	PVOLINFO volinfo;			// VOLINFO used to open this file
278
+	uint32_t dirsector;			// physical sector containing dir entry of this file
279
+	uint8_t diroffset;			// # of this entry within the dir sector
280
+	uint8_t mode;				// mode in which this file was opened
281
+	uint32_t firstcluster;		// first cluster of file
282
+	uint32_t filelen;			// byte length of file
283
+
284
+	uint32_t cluster;			// current cluster
285
+	uint32_t pointer;			// current (BYTE) pointer
286
+} FILEINFO, *PFILEINFO;
287
+
288
+/*
289
+	Get starting sector# of specified partition on drive #unit
290
+	NOTE: This code ASSUMES an MBR on the disk.
291
+	scratchsector should point to a SECTOR_SIZE scratch area
292
+	Returns 0xffffffff for any error.
293
+	If pactive is non-NULL, this function also returns the partition active flag.
294
+	If pptype is non-NULL, this function also returns the partition type.
295
+	If psize is non-NULL, this function also returns the partition size.
296
+*/
297
+uint32_t DFS_GetPtnStart(uint8_t unit, uint8_t *scratchsector, uint8_t pnum, uint8_t *pactive, uint8_t *pptype, uint32_t *psize);
298
+
299
+/*
300
+	Retrieve volume info from BPB and store it in a VOLINFO structure
301
+	You must provide the unit and starting sector of the filesystem, and
302
+	a pointer to a sector buffer for scratch
303
+	Attempts to read BPB and glean information about the FS from that.
304
+	Returns 0 OK, nonzero for any error.
305
+*/
306
+uint32_t DFS_GetVolInfo(uint8_t unit, uint8_t *scratchsector, uint32_t startsector, PVOLINFO volinfo);
307
+
308
+/*
309
+	Open a directory for enumeration by DFS_GetNextDirEnt
310
+	You must supply a populated VOLINFO (see DFS_GetVolInfo)
311
+	The empty string or a string containing only the directory separator are
312
+	considered to be the root directory.
313
+	Returns 0 OK, nonzero for any error.
314
+*/
315
+uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo);
316
+
317
+/*
318
+	Get next entry in opened directory structure. Copies fields into the dirent
319
+	structure, updates dirinfo. Note that it is the _caller's_ responsibility to
320
+	handle the '.' and '..' entries.
321
+	A deleted file will be returned as a NULL entry (first char of filename=0)
322
+	by this code. Filenames beginning with 0x05 will be translated to 0xE5
323
+	automatically. Long file name entries will be returned as NULL.
324
+	returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid,
325
+	or DFS_ERRMISC for a media error
326
+*/
327
+uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent);
328
+
329
+/*
330
+	Open a file for reading or writing. You supply populated VOLINFO, a path to the file,
331
+	mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to
332
+	provide a pointer to a sector-sized scratch buffer.
333
+	Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used
334
+	to access the file from this point on.
335
+*/
336
+uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo);
337
+
338
+/*
339
+	Read an open file
340
+	You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
341
+	pointer to a SECTOR_SIZE scratch buffer.
342
+	Note that returning DFS_EOF is not an error condition. This function updates the
343
+	successcount field with the number of bytes actually read.
344
+*/
345
+uint32_t DFS_ReadFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
346
+
347
+/*
348
+	Write an open file
349
+	You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
350
+	pointer to a SECTOR_SIZE scratch buffer.
351
+	This function updates the successcount field with the number of bytes actually written.
352
+*/
353
+uint32_t DFS_WriteFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
354
+
355
+/*
356
+	Seek file pointer to a given position
357
+	This function does not return status - refer to the fileinfo->pointer value
358
+	to see where the pointer wound up.
359
+	Requires a SECTOR_SIZE scratch buffer
360
+*/
361
+void DFS_Seek(PFILEINFO fileinfo, uint32_t offset, uint8_t *scratch);
362
+
363
+/*
364
+	Delete a file
365
+	scratch must point to a sector-sized buffer
366
+*/
367
+uint32_t DFS_UnlinkFile(PVOLINFO volinfo, uint8_t *path, uint8_t *scratch);
368
+
369
+#endif // _DOSFS_H
... ...
@@ -0,0 +1,46 @@
1
+/* flaneth - flash and ethernet
2
+   Copyright (C) 2007-2012 Stefan Schuermans <stefan@schuermans.info>
3
+   Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html
4
+   a BlinkenArea project - http://www.blinkenarea.org/ */
5
+
6
+#include "cf.h"
7
+#include "dosfs.h"
8
+
9
+/**
10
+ * @brief read sector(s) from disk
11
+ * @param[in] unit identifies the disk
12
+ * @param[in] buffer to put data into
13
+ * @param[in] sector number of sector
14
+ * @param[in] count number of sectors to read
15
+ * @return 0 on success, -1 on error
16
+ */
17
+uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector,
18
+                        uint32_t count)
19
+{
20
+  // read sectros from CF card
21
+  for ( ; count > 0; count--, buffer += SECTOR_SIZE)
22
+    if (CfRead(sector, buffer) != 0)
23
+      return -1;
24
+  return 0;
25
+  (void)unit;
26
+}
27
+
28
+/**
29
+ * @brief write sector(s) to disk
30
+ * @param[in] unit identifies the disk
31
+ * @param[in] buffer to get data from
32
+ * @param[in] sector number of sector
33
+ * @param[in] count number of sectors to write
34
+ * @return 0 on success, -1 on error
35
+ */
36
+uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector,
37
+                         uint32_t count)
38
+{
39
+  // writing not supported
40
+  return -1;
41
+  (void)unit;
42
+  (void)buffer;
43
+  (void)sector;
44
+  (void)count;
45
+}
46
+
... ...
@@ -5,11 +5,13 @@
5 5
 
6 6
 #include <avr/interrupt.h>
7 7
 #include <avr/wdt.h>
8
+#include <string.h>
8 9
 
9 10
 #include "arp.h"
10 11
 #include "bus.h"
11 12
 #include "cf.h"
12 13
 #include "debug.h"
14
+#include "dosfs.h"
13 15
 #include "eeprom.h"
14 16
 #include "http.h"
15 17
 #include "random.h"
... ...
@@ -23,8 +25,18 @@
23 25
 // try to work with CF card
24 26
 static void mainWorkCf(void)
25 27
 {
26
-  unsigned long sectors, s;
27
-  unsigned char buf[CF_SECTOR_SIZE];
28
+  unsigned long sectors;
29
+  uint8_t sectorBuf[SECTOR_SIZE];
30
+  uint32_t partStart, partSize;
31
+  uint8_t partAct, partType;
32
+  VOLINFO vi;
33
+  const char *sec_clus, *fstype;
34
+  DIRINFO di;
35
+  DIRENT de;
36
+  uint8_t filename[12];
37
+  FILEINFO fi;
38
+  uint8_t buf[16];
39
+  uint32_t len, i;
28 40
 
29 41
   // reset card
30 42
   if (CfReset() != 0)
... ...
@@ -34,20 +46,73 @@ static void mainWorkCf(void)
34 46
   if (CfIdentify(&sectors) != 0)
35 47
     return;
36 48
 
37
-  // dump sectors
38
-  for (s = 0; s < sectors; ++s) {
39
-    if (CfRead(s, buf) != 0)
49
+  // get start of first partition
50
+  partStart = DFS_GetPtnStart(0, sectorBuf, 0, &partAct, &partType, &partSize);
51
+  if (partStart == 0xFFFFFFFF) {
52
+    debug_printf("cannot find first partition");
40 53
     return;
41
-#if 0
42
-    unsigned int i;
43
-    for (i = 0; i < CF_SECTOR_SIZE; ++i) {
44
-      printf(" %02X", buf[i]);
45
-      if ((i & 15) == 15)
46
-        printf("\r\n");
47 54
   }
48
-#endif
49
-    Tasks();
50
-  } // for s
55
+  debug_printf("partition: start %lu size %lu", partStart, partSize);
56
+
57
+  // get volume info
58
+  if (DFS_GetVolInfo(0, sectorBuf, partStart, &vi) != 0) {
59
+    debug_printf("cannot get volume info");
60
+    return;
61
+  }
62
+  switch (vi.filesystem) {
63
+    case FAT12: sec_clus = "sector: "; fstype = "FAT12";   break;
64
+    case FAT16: sec_clus = "sector: "; fstype = "FAT16";   break;
65
+    case FAT32: sec_clus = "cluster:"; fstype = "FAT32";   break;
66
+    default:    sec_clus = "sector: "; fstype = "unknown"; break;
67
+  }
68
+  debug_printf("filesystem:                    %s", fstype);
69
+  debug_printf("volume label:                  %-11.11s", vi.label);
70
+  debug_printf("sector/s per cluster:          %hu", vi.secperclus);
71
+  debug_printf("reserved sector/s:             %hu", vi.reservedsecs);
72
+  debug_printf("volume total sectors:          %lu", vi.numsecs);
73
+  debug_printf("sectors per FAT:               %lu", vi.secperfat);
74
+  debug_printf("first FAT at sector:           %lu", vi.fat1);
75
+  debug_printf("root dir at %s           %lu", sec_clus, vi.rootdir);
76
+  debug_printf("root dir entries:              %hu", vi.rootentries);
77
+  debug_printf("data area commences at sector: %lu", vi.dataarea);
78
+  debug_printf("clusters in data area:         %lu", vi.numclusters);
79
+  debug_printf("sectors per cluster:           %hu", vi.secperclus);
80
+
81
+  // list files in root directory
82
+  di.scratch = sectorBuf;
83
+  if (DFS_OpenDir(&vi, (uint8_t *)"", &di) != 0) {
84
+    debug_printf("cannot open root directory");
85
+    return;
86
+  }
87
+  debug_printf("files in root directory:");
88
+  while (DFS_GetNext(&vi, &di, &de) == 0) {
89
+    if (de.attr == ATTR_LONG_NAME) {
90
+      // ignore long names
91
+    } else if (de.attr & ATTR_VOLUME_ID) {
92
+      debug_printf("volume ID: %-11.11s", de.name);
93
+    } else if (de.attr & ATTR_DIRECTORY) {
94
+      debug_printf("directory: %-11.11s", de.name);
95
+    } else {
96
+      debug_printf("file: %-11.11s", de.name);
97
+      memcpy(filename, de.name, 11); // remember last filename
98
+      filename[11] = 0;
99
+    }
100
+  }
101
+
102
+  // dump last file
103
+  if (DFS_OpenFile(&vi, filename, DFS_READ, sectorBuf, &fi) != 0) {
104
+    debug_printf("cannot open file %s", filename);
105
+    return;
106
+  }
107
+  debug_printf("dumping file %s:", filename);
108
+  while (fi.pointer < fi.filelen) {
109
+    len = 0;
110
+    DFS_ReadFile(&fi, sectorBuf, buf, &len, sizeof(buf));
111
+    for (i = 0; i < len; ++i)
112
+      debug_printf_nolf(" %02X", buf[i]);
113
+    debug_printf("");
114
+  }
115
+  debug_printf("done", filename);
51 116
 }
52 117
 
53 118
 // wait for CF card and work with it
54 119