]> git.gir.st - tmk_keyboard.git/blob - tmk_core/protocol/lufa/LUFA-git/Bootloaders/MassStorage/Lib/VirtualFAT.c
Merge commit '20b787fc1284176834cbe7ca2134e4b36bec5828'
[tmk_keyboard.git] / tmk_core / protocol / lufa / LUFA-git / Bootloaders / MassStorage / Lib / VirtualFAT.c
1 /*
2 LUFA Library
3 Copyright (C) Dean Camera, 2014.
4
5 dean [at] fourwalledcubicle [dot] com
6 www.lufa-lib.org
7 */
8
9 /*
10 Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com)
11
12 Permission to use, copy, modify, distribute, and sell this
13 software and its documentation for any purpose is hereby granted
14 without fee, provided that the above copyright notice appear in
15 all copies and that both that the copyright notice and this
16 permission notice and warranty disclaimer appear in supporting
17 documentation, and that the name of the author not be used in
18 advertising or publicity pertaining to distribution of the
19 software without specific, written prior permission.
20
21 The author disclaims all warranties with regard to this
22 software, including all implied warranties of merchantability
23 and fitness. In no event shall the author be liable for any
24 special, indirect or consequential damages or any damages
25 whatsoever resulting from loss of use, data or profits, whether
26 in an action of contract, negligence or other tortious action,
27 arising out of or in connection with the use or performance of
28 this software.
29 */
30
31 /** \file
32 *
33 * Virtualized FAT12 filesystem implementation, to perform self-programming
34 * in response to read and write requests to the virtual filesystem by the
35 * host PC.
36 */
37
38 #define INCLUDE_FROM_VIRTUAL_FAT_C
39 #include "VirtualFAT.h"
40
41 /** FAT filesystem boot sector block, must be the first sector on the physical
42 * disk so that the host can identify the presence of a FAT filesystem. This
43 * block is truncated; normally a large bootstrap section is located near the
44 * end of the block for booting purposes however as this is not meant to be a
45 * bootable disk it is omitted for space reasons.
46 *
47 * \note When returning the boot block to the host, the magic signature 0xAA55
48 * must be added to the very end of the block to identify it as a boot
49 * block.
50 */
51 static const FATBootBlock_t BootBlock =
52 {
53 .Bootstrap = {0xEB, 0x3C, 0x90},
54 .Description = "mkdosfs",
55 .SectorSize = SECTOR_SIZE_BYTES,
56 .SectorsPerCluster = SECTOR_PER_CLUSTER,
57 .ReservedSectors = 1,
58 .FATCopies = 2,
59 .RootDirectoryEntries = (SECTOR_SIZE_BYTES / sizeof(FATDirectoryEntry_t)),
60 .TotalSectors16 = LUN_MEDIA_BLOCKS,
61 .MediaDescriptor = 0xF8,
62 .SectorsPerFAT = 1,
63 .SectorsPerTrack = (LUN_MEDIA_BLOCKS % 64),
64 .Heads = (LUN_MEDIA_BLOCKS / 64),
65 .HiddenSectors = 0,
66 .TotalSectors32 = 0,
67 .PhysicalDriveNum = 0,
68 .ExtendedBootRecordSig = 0x29,
69 .VolumeSerialNumber = 0x12345678,
70 .VolumeLabel = "LUFA BOOT ",
71 .FilesystemIdentifier = "FAT12 ",
72 };
73
74 /** FAT 8.3 style directory entry, for the virtual FLASH contents file. */
75 static FATDirectoryEntry_t FirmwareFileEntries[] =
76 {
77 /* Root volume label entry; disk label is contained in the Filename and
78 * Extension fields (concatenated) with a special attribute flag - other
79 * fields are ignored. Should be the same as the label in the boot block.
80 */
81 [DISK_FILE_ENTRY_VolumeID] =
82 {
83 .MSDOS_Directory =
84 {
85 .Name = "LUFA BOOT ",
86 .Attributes = FAT_FLAG_VOLUME_NAME,
87 .Reserved = {0},
88 .CreationTime = 0,
89 .CreationDate = 0,
90 .StartingCluster = 0,
91 .Reserved2 = 0,
92 }
93 },
94
95 /* VFAT Long File Name entry for the virtual firmware file; required to
96 * prevent corruption from systems that are unable to detect the device
97 * as being a legacy MSDOS style FAT12 volume. */
98 [DISK_FILE_ENTRY_FLASH_LFN] =
99 {
100 .VFAT_LongFileName =
101 {
102 .Ordinal = 1 | FAT_ORDINAL_LAST_ENTRY,
103 .Attribute = FAT_FLAG_LONG_FILE_NAME,
104 .Reserved1 = 0,
105 .Reserved2 = 0,
106
107 .Checksum = FAT_CHECKSUM('F','L','A','S','H',' ',' ',' ','B','I','N'),
108
109 .Unicode1 = 'F',
110 .Unicode2 = 'L',
111 .Unicode3 = 'A',
112 .Unicode4 = 'S',
113 .Unicode5 = 'H',
114 .Unicode6 = '.',
115 .Unicode7 = 'B',
116 .Unicode8 = 'I',
117 .Unicode9 = 'N',
118 .Unicode10 = 0,
119 .Unicode11 = 0,
120 .Unicode12 = 0,
121 .Unicode13 = 0,
122 }
123 },
124
125 /* MSDOS file entry for the virtual Firmware image. */
126 [DISK_FILE_ENTRY_FLASH_MSDOS] =
127 {
128 .MSDOS_File =
129 {
130 .Filename = "FLASH ",
131 .Extension = "BIN",
132 .Attributes = 0,
133 .Reserved = {0},
134 .CreationTime = FAT_TIME(1, 1, 0),
135 .CreationDate = FAT_DATE(14, 2, 1989),
136 .StartingCluster = 2,
137 .FileSizeBytes = FLASH_FILE_SIZE_BYTES,
138 }
139 },
140
141 [DISK_FILE_ENTRY_EEPROM_LFN] =
142 {
143 .VFAT_LongFileName =
144 {
145 .Ordinal = 1 | FAT_ORDINAL_LAST_ENTRY,
146 .Attribute = FAT_FLAG_LONG_FILE_NAME,
147 .Reserved1 = 0,
148 .Reserved2 = 0,
149
150 .Checksum = FAT_CHECKSUM('E','E','P','R','O','M',' ',' ','B','I','N'),
151
152 .Unicode1 = 'E',
153 .Unicode2 = 'E',
154 .Unicode3 = 'P',
155 .Unicode4 = 'R',
156 .Unicode5 = 'O',
157 .Unicode6 = 'M',
158 .Unicode7 = '.',
159 .Unicode8 = 'B',
160 .Unicode9 = 'I',
161 .Unicode10 = 'N',
162 .Unicode11 = 0,
163 .Unicode12 = 0,
164 .Unicode13 = 0,
165 }
166 },
167
168 [DISK_FILE_ENTRY_EEPROM_MSDOS] =
169 {
170 .MSDOS_File =
171 {
172 .Filename = "EEPROM ",
173 .Extension = "BIN",
174 .Attributes = 0,
175 .Reserved = {0},
176 .CreationTime = FAT_TIME(1, 1, 0),
177 .CreationDate = FAT_DATE(14, 2, 1989),
178 .StartingCluster = 2 + FILE_CLUSTERS(FLASH_FILE_SIZE_BYTES),
179 .FileSizeBytes = EEPROM_FILE_SIZE_BYTES,
180 }
181 },
182 };
183
184 /** Starting cluster of the virtual FLASH.BIN file on disk, tracked so that the
185 * offset from the start of the data sector can be determined. On Windows
186 * systems files are usually replaced using the original file's disk clusters,
187 * while Linux appears to overwrite with an offset which must be compensated for.
188 */
189 static const uint16_t* FLASHFileStartCluster = &FirmwareFileEntries[DISK_FILE_ENTRY_FLASH_MSDOS].MSDOS_File.StartingCluster;
190
191 /** Starting cluster of the virtual EEPROM.BIN file on disk, tracked so that the
192 * offset from the start of the data sector can be determined. On Windows
193 * systems files are usually replaced using the original file's disk clusters,
194 * while Linux appears to overwrite with an offset which must be compensated for.
195 */
196 static const uint16_t* EEPROMFileStartCluster = &FirmwareFileEntries[DISK_FILE_ENTRY_EEPROM_MSDOS].MSDOS_File.StartingCluster;
197
198 /** Reads a byte of EEPROM out from the EEPROM memory space.
199 *
200 * \note This function is required as the avr-libc EEPROM functions do not cope
201 * with linker relaxations, and a jump longer than 4K of FLASH on the
202 * larger USB AVRs will break the linker. This function is marked as
203 * never inlinable and placed into the normal text segment so that the
204 * call to the EEPROM function will be short even if the AUX boot section
205 * is used.
206 *
207 * \param[in] Address Address of the EEPROM location to read from
208 *
209 * \return Read byte of EEPROM data.
210 */
211 static uint8_t ReadEEPROMByte(const uint8_t* const Address)
212 {
213 return eeprom_read_byte(Address);
214 }
215
216 /** Writes a byte of EEPROM out to the EEPROM memory space.
217 *
218 * \note This function is required as the avr-libc EEPROM functions do not cope
219 * with linker relaxations, and a jump longer than 4K of FLASH on the
220 * larger USB AVRs will break the linker. This function is marked as
221 * never inlinable and placed into the normal text segment so that the
222 * call to the EEPROM function will be short even if the AUX boot section
223 * is used.
224 *
225 * \param[in] Address Address of the EEPROM location to write to
226 * \param[in] Data New data to write to the EEPROM location
227 */
228 static void WriteEEPROMByte(uint8_t* const Address,
229 const uint8_t Data)
230 {
231 eeprom_update_byte(Address, Data);
232 }
233
234 /** Updates a FAT12 cluster entry in the FAT file table with the specified next
235 * chain index. If the cluster is the last in the file chain, the magic value
236 * \c 0xFFF should be used.
237 *
238 * \note FAT data cluster indexes are offset by 2, so that cluster 2 is the
239 * first file data cluster on the disk. See the FAT specification.
240 *
241 * \param[out] FATTable Pointer to the FAT12 allocation table
242 * \param[in] Index Index of the cluster entry to update
243 * \param[in] ChainEntry Next cluster index in the file chain
244 */
245 static void UpdateFAT12ClusterEntry(uint8_t* const FATTable,
246 const uint16_t Index,
247 const uint16_t ChainEntry)
248 {
249 /* Calculate the starting offset of the cluster entry in the FAT12 table */
250 uint8_t FATOffset = (Index + (Index >> 1));
251 bool UpperNibble = ((Index & 1) != 0);
252
253 /* Check if the start of the entry is at an upper nibble of the byte, fill
254 * out FAT12 entry as required */
255 if (UpperNibble)
256 {
257 FATTable[FATOffset] = (FATTable[FATOffset] & 0x0F) | ((ChainEntry & 0x0F) << 4);
258 FATTable[FATOffset + 1] = (ChainEntry >> 4);
259 }
260 else
261 {
262 FATTable[FATOffset] = ChainEntry;
263 FATTable[FATOffset + 1] = (FATTable[FATOffset] & 0xF0) | (ChainEntry >> 8);
264 }
265 }
266
267 /** Updates a FAT12 cluster chain in the FAT file table with a linear chain of
268 * the specified length.
269 *
270 * \note FAT data cluster indexes are offset by 2, so that cluster 2 is the
271 * first file data cluster on the disk. See the FAT specification.
272 *
273 * \param[out] FATTable Pointer to the FAT12 allocation table
274 * \param[in] Index Index of the start of the cluster chain to update
275 * \param[in] ChainLength Length of the chain to write, in clusters
276 */
277 static void UpdateFAT12ClusterChain(uint8_t* const FATTable,
278 const uint16_t Index,
279 const uint8_t ChainLength)
280 {
281 for (uint8_t i = 0; i < ChainLength; i++)
282 {
283 uint16_t CurrentCluster = Index + i;
284 uint16_t NextCluster = CurrentCluster + 1;
285
286 /* Mark last cluster as end of file */
287 if (i == (ChainLength - 1))
288 NextCluster = 0xFFF;
289
290 UpdateFAT12ClusterEntry(FATTable, CurrentCluster, NextCluster);
291 }
292 }
293
294 /** Reads or writes a block of data from/to the physical device FLASH using a
295 * block buffer stored in RAM, if the requested block is within the virtual
296 * firmware file's sector ranges in the emulated FAT file system.
297 *
298 * \param[in] BlockNumber Physical disk block to read from/write to
299 * \param[in,out] BlockBuffer Pointer to the start of the block buffer in RAM
300 * \param[in] Read If \c true, the requested block is read, if
301 * \c false, the requested block is written
302 */
303 static void ReadWriteFLASHFileBlock(const uint16_t BlockNumber,
304 uint8_t* BlockBuffer,
305 const bool Read)
306 {
307 uint16_t FileStartBlock = DISK_BLOCK_DataStartBlock + (*FLASHFileStartCluster - 2) * SECTOR_PER_CLUSTER;
308 uint16_t FileEndBlock = FileStartBlock + (FILE_SECTORS(FLASH_FILE_SIZE_BYTES) - 1);
309
310 /* Range check the write request - abort if requested block is not within the
311 * virtual firmware file sector range */
312 if (!((BlockNumber >= FileStartBlock) && (BlockNumber <= FileEndBlock)))
313 return;
314
315 #if (FLASHEND > 0xFFFF)
316 uint32_t FlashAddress = (uint32_t)(BlockNumber - FileStartBlock) * SECTOR_SIZE_BYTES;
317 #else
318 uint16_t FlashAddress = (uint16_t)(BlockNumber - FileStartBlock) * SECTOR_SIZE_BYTES;
319 #endif
320
321 if (Read)
322 {
323 /* Read out the mapped block of data from the device's FLASH */
324 for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++)
325 {
326 #if (FLASHEND > 0xFFFF)
327 BlockBuffer[i] = pgm_read_byte_far(FlashAddress++);
328 #else
329 BlockBuffer[i] = pgm_read_byte(FlashAddress++);
330 #endif
331 }
332 }
333 else
334 {
335 /* Write out the mapped block of data to the device's FLASH */
336 for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i += 2)
337 {
338 if ((FlashAddress % SPM_PAGESIZE) == 0)
339 {
340 /* Erase the given FLASH page, ready to be programmed */
341 BootloaderAPI_ErasePage(FlashAddress);
342 }
343
344 /* Write the next data word to the FLASH page */
345 BootloaderAPI_FillWord(FlashAddress, (BlockBuffer[i + 1] << 8) | BlockBuffer[i]);
346 FlashAddress += 2;
347
348 if ((FlashAddress % SPM_PAGESIZE) == 0)
349 {
350 /* Write the filled FLASH page to memory */
351 BootloaderAPI_WritePage(FlashAddress - SPM_PAGESIZE);
352 }
353 }
354 }
355 }
356
357 /** Reads or writes a block of data from/to the physical device EEPROM using a
358 * block buffer stored in RAM, if the requested block is within the virtual
359 * firmware file's sector ranges in the emulated FAT file system.
360 *
361 * \param[in] BlockNumber Physical disk block to read from/write to
362 * \param[in,out] BlockBuffer Pointer to the start of the block buffer in RAM
363 * \param[in] Read If \c true, the requested block is read, if
364 * \c false, the requested block is written
365 */
366 static void ReadWriteEEPROMFileBlock(const uint16_t BlockNumber,
367 uint8_t* BlockBuffer,
368 const bool Read)
369 {
370 uint16_t FileStartBlock = DISK_BLOCK_DataStartBlock + (*EEPROMFileStartCluster - 2) * SECTOR_PER_CLUSTER;
371 uint16_t FileEndBlock = FileStartBlock + (FILE_SECTORS(EEPROM_FILE_SIZE_BYTES) - 1);
372
373 /* Range check the write request - abort if requested block is not within the
374 * virtual firmware file sector range */
375 if (!((BlockNumber >= FileStartBlock) && (BlockNumber <= FileEndBlock)))
376 return;
377
378 uint16_t EEPROMAddress = (uint16_t)(BlockNumber - FileStartBlock) * SECTOR_SIZE_BYTES;
379
380 if (Read)
381 {
382 /* Read out the mapped block of data from the device's EEPROM */
383 for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++)
384 BlockBuffer[i] = ReadEEPROMByte((uint8_t*)EEPROMAddress++);
385 }
386 else
387 {
388 /* Write out the mapped block of data to the device's EEPROM */
389 for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++)
390 WriteEEPROMByte((uint8_t*)EEPROMAddress++, BlockBuffer[i]);
391 }
392 }
393
394 /** Writes a block of data to the virtual FAT filesystem, from the USB Mass
395 * Storage interface.
396 *
397 * \param[in] BlockNumber Index of the block to write.
398 */
399 void VirtualFAT_WriteBlock(const uint16_t BlockNumber)
400 {
401 uint8_t BlockBuffer[SECTOR_SIZE_BYTES];
402
403 /* Buffer the entire block to be written from the host */
404 Endpoint_Read_Stream_LE(BlockBuffer, sizeof(BlockBuffer), NULL);
405 Endpoint_ClearOUT();
406
407 switch (BlockNumber)
408 {
409 case DISK_BLOCK_BootBlock:
410 case DISK_BLOCK_FATBlock1:
411 case DISK_BLOCK_FATBlock2:
412 /* Ignore writes to the boot and FAT blocks */
413
414 break;
415
416 case DISK_BLOCK_RootFilesBlock:
417 /* Copy over the updated directory entries */
418 memcpy(FirmwareFileEntries, BlockBuffer, sizeof(FirmwareFileEntries));
419
420 break;
421
422 default:
423 ReadWriteFLASHFileBlock(BlockNumber, BlockBuffer, false);
424 ReadWriteEEPROMFileBlock(BlockNumber, BlockBuffer, false);
425
426 break;
427 }
428 }
429
430 /** Reads a block of data from the virtual FAT filesystem, and sends it to the
431 * host via the USB Mass Storage interface.
432 *
433 * \param[in] BlockNumber Index of the block to read.
434 */
435 void VirtualFAT_ReadBlock(const uint16_t BlockNumber)
436 {
437 uint8_t BlockBuffer[SECTOR_SIZE_BYTES];
438 memset(BlockBuffer, 0x00, sizeof(BlockBuffer));
439
440 switch (BlockNumber)
441 {
442 case DISK_BLOCK_BootBlock:
443 memcpy(BlockBuffer, &BootBlock, sizeof(FATBootBlock_t));
444
445 /* Add the magic signature to the end of the block */
446 BlockBuffer[SECTOR_SIZE_BYTES - 2] = 0x55;
447 BlockBuffer[SECTOR_SIZE_BYTES - 1] = 0xAA;
448
449 break;
450
451 case DISK_BLOCK_FATBlock1:
452 case DISK_BLOCK_FATBlock2:
453 /* Cluster 0: Media type/Reserved */
454 UpdateFAT12ClusterEntry(BlockBuffer, 0, 0xF00 | BootBlock.MediaDescriptor);
455
456 /* Cluster 1: Reserved */
457 UpdateFAT12ClusterEntry(BlockBuffer, 1, 0xFFF);
458
459 /* Cluster 2 onwards: Cluster chain of FLASH.BIN */
460 UpdateFAT12ClusterChain(BlockBuffer, *FLASHFileStartCluster, FILE_CLUSTERS(FLASH_FILE_SIZE_BYTES));
461
462 /* Cluster 2+n onwards: Cluster chain of EEPROM.BIN */
463 UpdateFAT12ClusterChain(BlockBuffer, *EEPROMFileStartCluster, FILE_CLUSTERS(EEPROM_FILE_SIZE_BYTES));
464
465 break;
466
467 case DISK_BLOCK_RootFilesBlock:
468 memcpy(BlockBuffer, FirmwareFileEntries, sizeof(FirmwareFileEntries));
469
470 break;
471
472 default:
473 ReadWriteFLASHFileBlock(BlockNumber, BlockBuffer, true);
474 ReadWriteEEPROMFileBlock(BlockNumber, BlockBuffer, true);
475
476 break;
477 }
478
479 /* Write the entire read block Buffer to the host */
480 Endpoint_Write_Stream_LE(BlockBuffer, sizeof(BlockBuffer), NULL);
481 Endpoint_ClearIN();
482 }
Imprint / Impressum