diff --git a/LUFA.pnproj b/LUFA.pnproj index 1115f17bb1..2e485bc89d 100644 --- a/LUFA.pnproj +++ b/LUFA.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/Projects/TemperatureDataLogger/makefile b/Projects/TemperatureDataLogger/makefile index 990156f26a..a3da35ff3f 100644 --- a/Projects/TemperatureDataLogger/makefile +++ b/Projects/TemperatureDataLogger/makefile @@ -128,9 +128,9 @@ SRC = $(TARGET).c \ Descriptors.c \ Lib/DataflashManager.c \ Lib/SCSI.c \ + Lib/DS1307.c \ Lib/FATFs/diskio.c \ Lib/FATFs/ff.c \ - Lib/DS1307.c \ $(LUFA_PATH)/LUFA/Drivers/Board/Temperature.c \ $(LUFA_PATH)/LUFA/Drivers/Peripheral/TWI.c \ $(LUFA_PATH)/LUFA/Drivers/USB/LowLevel/DevChapter9.c \ diff --git a/Projects/Webserver/Descriptors.c b/Projects/Webserver/Descriptors.c new file mode 100644 index 0000000000..40ce620a64 --- /dev/null +++ b/Projects/Webserver/Descriptors.c @@ -0,0 +1,217 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * USB Device Descriptors, for library use when in USB device mode. Descriptors are special + * computer-readable structures which the host requests upon device enumeration, to determine + * the device's capabilities and functions. + */ + +#include "Descriptors.h" + +/* On some devices, there is a factory set internal serial number which can be automatically sent to the host as + * the device's serial number when the Device Descriptor's .SerialNumStrIndex entry is set to USE_INTERNAL_SERIAL. + * This allows the host to track a device across insertions on different ports, allowing them to retain allocated + * resources like COM port numbers and drivers. On demos using this feature, give a warning on unsupported devices + * so that the user can supply their own serial number descriptor instead or remove the USE_INTERNAL_SERIAL value + * from the Device Descriptor (forcing the host to generate a serial number for each device from the VID, PID and + * port location). + */ +#if (USE_INTERNAL_SERIAL == NO_DESCRIPTOR) + #warning USE_INTERNAL_SERIAL is not available on this AVR - please manually construct a device serial descriptor. +#endif + +/** Device descriptor structure. This descriptor, located in FLASH memory, describes the overall + * device characteristics, including the supported USB version, control endpoint size and the + * number of device configurations. The descriptor is read out by the USB host when the enumeration + * process begins. + */ +USB_Descriptor_Device_t PROGMEM DeviceDescriptor = +{ + .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device}, + + .USBSpecification = VERSION_BCD(01.10), + .Class = 0x00, + .SubClass = 0x00, + .Protocol = 0x00, + + .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, + + .VendorID = 0x03EB, + .ProductID = 0x2045, + .ReleaseNumber = 0x0000, + + .ManufacturerStrIndex = 0x01, + .ProductStrIndex = 0x02, + .SerialNumStrIndex = USE_INTERNAL_SERIAL, + + .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS +}; + +/** Configuration descriptor structure. This descriptor, located in FLASH memory, describes the usage + * of the device in one of its supported configurations, including information about any device interfaces + * and endpoints. The descriptor is read out by the USB host during the enumeration process when selecting + * a configuration so that the host may correctly communicate with the USB device. + */ +USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = +{ + .Config = + { + .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, + + .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t), + .TotalInterfaces = 1, + + .ConfigurationNumber = 1, + .ConfigurationStrIndex = NO_DESCRIPTOR, + + .ConfigAttributes = USB_CONFIG_ATTR_BUSPOWERED, + + .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) + }, + + .Interface = + { + .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, + + .InterfaceNumber = 0, + .AlternateSetting = 0, + + .TotalEndpoints = 2, + + .Class = 0x08, + .SubClass = 0x06, + .Protocol = 0x50, + + .InterfaceStrIndex = NO_DESCRIPTOR + }, + + .DataInEndpoint = + { + .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, + + .EndpointAddress = (ENDPOINT_DESCRIPTOR_DIR_IN | MASS_STORAGE_IN_EPNUM), + .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = MASS_STORAGE_IO_EPSIZE, + .PollingIntervalMS = 0x00 + }, + + .DataOutEndpoint = + { + .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, + + .EndpointAddress = (ENDPOINT_DESCRIPTOR_DIR_OUT | MASS_STORAGE_OUT_EPNUM), + .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = MASS_STORAGE_IO_EPSIZE, + .PollingIntervalMS = 0x00 + } +}; + +/** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests + * the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate + * via the language ID table available at USB.org what languages the device supports for its string descriptors. + */ +USB_Descriptor_String_t PROGMEM LanguageString = +{ + .Header = {.Size = USB_STRING_LEN(1), .Type = DTYPE_String}, + + .UnicodeString = {LANGUAGE_ID_ENG} +}; + +/** Manufacturer descriptor string. This is a Unicode string containing the manufacturer's details in human readable + * form, and is read out upon request by the host when the appropriate string ID is requested, listed in the Device + * Descriptor. + */ +USB_Descriptor_String_t PROGMEM ManufacturerString = +{ + .Header = {.Size = USB_STRING_LEN(11), .Type = DTYPE_String}, + + .UnicodeString = L"Dean Camera" +}; + +/** Product descriptor string. This is a Unicode string containing the product's details in human readable form, + * and is read out upon request by the host when the appropriate string ID is requested, listed in the Device + * Descriptor. + */ +USB_Descriptor_String_t PROGMEM ProductString = +{ + .Header = {.Size = USB_STRING_LEN(22), .Type = DTYPE_String}, + + .UnicodeString = L"LUFA Mass Storage Demo" +}; + +/** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors" + * documentation) by the application code so that the address and size of a requested descriptor can be given + * to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function + * is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the + * USB host. + */ +uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, const uint8_t wIndex, void** const DescriptorAddress) +{ + const uint8_t DescriptorType = (wValue >> 8); + const uint8_t DescriptorNumber = (wValue & 0xFF); + + void* Address = NULL; + uint16_t Size = NO_DESCRIPTOR; + + switch (DescriptorType) + { + case DTYPE_Device: + Address = (void*)&DeviceDescriptor; + Size = sizeof(USB_Descriptor_Device_t); + break; + case DTYPE_Configuration: + Address = (void*)&ConfigurationDescriptor; + Size = sizeof(USB_Descriptor_Configuration_t); + break; + case DTYPE_String: + switch (DescriptorNumber) + { + case 0x00: + Address = (void*)&LanguageString; + Size = pgm_read_byte(&LanguageString.Header.Size); + break; + case 0x01: + Address = (void*)&ManufacturerString; + Size = pgm_read_byte(&ManufacturerString.Header.Size); + break; + case 0x02: + Address = (void*)&ProductString; + Size = pgm_read_byte(&ProductString.Header.Size); + break; + } + + break; + } + + *DescriptorAddress = Address; + return Size; +} diff --git a/Projects/Webserver/Descriptors.h b/Projects/Webserver/Descriptors.h new file mode 100644 index 0000000000..dcaaddaf7d --- /dev/null +++ b/Projects/Webserver/Descriptors.h @@ -0,0 +1,72 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Header file for Descriptors.c. + */ + +#ifndef _DESCRIPTORS_H_ +#define _DESCRIPTORS_H_ + + /* Includes: */ + #include + + #include + #include + + /* Macros: */ + /** Endpoint number of the Mass Storage device-to-host data IN endpoint. */ + #define MASS_STORAGE_IN_EPNUM 3 + + /** Endpoint number of the Mass Storage host-to-device data OUT endpoint. */ + #define MASS_STORAGE_OUT_EPNUM 4 + + /** Size in bytes of the Mass Storage data endpoints. */ + #define MASS_STORAGE_IO_EPSIZE 64 + + /* Type Defines: */ + /** Type define for the device configuration descriptor structure. This must be defined in the + * application code, as the configuration descriptor contains several sub-descriptors which + * vary between devices, and which describe the device's usage to the host. + */ + typedef struct + { + USB_Descriptor_Configuration_Header_t Config; + USB_Descriptor_Interface_t Interface; + USB_Descriptor_Endpoint_t DataInEndpoint; + USB_Descriptor_Endpoint_t DataOutEndpoint; + } USB_Descriptor_Configuration_t; + + /* Function Prototypes: */ + uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, const uint8_t wIndex, void** const DescriptorAddress) + ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); + +#endif diff --git a/Projects/Webserver/Lib/DataflashManager.c b/Projects/Webserver/Lib/DataflashManager.c new file mode 100644 index 0000000000..1de866edec --- /dev/null +++ b/Projects/Webserver/Lib/DataflashManager.c @@ -0,0 +1,525 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Functions to manage the physical dataflash media, including reading and writing of + * blocks of data. These functions are called by the SCSI layer when data must be stored + * or retrieved to/from the physical storage media. If a different media is used (such + * as a SD card or EEPROM), functions similar to these will need to be generated. + */ + +#define INCLUDE_FROM_DATAFLASHMANAGER_C +#include "DataflashManager.h" + +/** Writes blocks (OS blocks, not Dataflash pages) to the storage medium, the board dataflash IC(s), from + * the pre-selected data OUT endpoint. This routine reads in OS sized blocks from the endpoint and writes + * them to the dataflash in Dataflash page sized blocks. + * + * \param[in] MSInterfaceInfo Pointer to a structure containing a Mass Storage Class configuration and state + * \param[in] BlockAddress Data block starting address for the write sequence + * \param[in] TotalBlocks Number of blocks of data to write + */ +void DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t* MSInterfaceInfo, const uint32_t BlockAddress, uint16_t TotalBlocks) +{ + uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); + uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); + uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); + bool UsingSecondBuffer = false; + + /* Select the correct starting Dataflash IC for the block requested */ + Dataflash_SelectChipFromPage(CurrDFPage); + +#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) + /* Copy selected dataflash's current page contents to the dataflash buffer */ + Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_WaitWhileBusy(); +#endif + + /* Send the dataflash buffer write command */ + Dataflash_SendByte(DF_CMD_BUFF1WRITE); + Dataflash_SendAddressBytes(0, CurrDFPageByte); + + /* Wait until endpoint is ready before continuing */ + if (Endpoint_WaitUntilReady()) + return; + + while (TotalBlocks) + { + uint8_t BytesInBlockDiv16 = 0; + + /* Write an endpoint packet sized data block to the dataflash */ + while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) + { + /* Check if the endpoint is currently empty */ + if (!(Endpoint_IsReadWriteAllowed())) + { + /* Clear the current endpoint bank */ + Endpoint_ClearOUT(); + + /* Wait until the host has sent another packet */ + if (Endpoint_WaitUntilReady()) + return; + } + + /* Check if end of dataflash page reached */ + if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Write the dataflash buffer contents back to the dataflash page */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); + Dataflash_SendAddressBytes(CurrDFPage, 0); + + /* Reset the dataflash buffer counter, increment the page counter */ + CurrDFPageByteDiv16 = 0; + CurrDFPage++; + + /* Once all the dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */ + if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) + UsingSecondBuffer = !(UsingSecondBuffer); + + /* Select the next dataflash chip based on the new dataflash page index */ + Dataflash_SelectChipFromPage(CurrDFPage); + +#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) + /* If less than one dataflash page remaining, copy over the existing page to preserve trailing data */ + if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Copy selected dataflash's current page contents to the dataflash buffer */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_WaitWhileBusy(); + } +#endif + + /* Send the dataflash buffer write command */ + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2WRITE : DF_CMD_BUFF1WRITE); + Dataflash_SendAddressBytes(0, 0); + } + + /* Write one 16-byte chunk of data to the dataflash */ + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + Dataflash_SendByte(Endpoint_Read_Byte()); + + /* Increment the dataflash page 16 byte block counter */ + CurrDFPageByteDiv16++; + + /* Increment the block 16 byte block counter */ + BytesInBlockDiv16++; + + /* Check if the current command is being aborted by the host */ + if (MSInterfaceInfo->State.IsMassStoreReset) + return; + } + + /* Decrement the blocks remaining counter and reset the sub block counter */ + TotalBlocks--; + } + + /* Write the dataflash buffer contents back to the dataflash page */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); + Dataflash_SendAddressBytes(CurrDFPage, 0x00); + Dataflash_WaitWhileBusy(); + + /* If the endpoint is empty, clear it ready for the next packet from the host */ + if (!(Endpoint_IsReadWriteAllowed())) + Endpoint_ClearOUT(); + + /* Deselect all dataflash chips */ + Dataflash_DeselectChip(); +} + +/** Reads blocks (OS blocks, not Dataflash pages) from the storage medium, the board dataflash IC(s), into + * the pre-selected data IN endpoint. This routine reads in Dataflash page sized blocks from the Dataflash + * and writes them in OS sized blocks to the endpoint. + * + * \param[in] MSInterfaceInfo Pointer to a structure containing a Mass Storage Class configuration and state + * \param[in] BlockAddress Data block starting address for the read sequence + * \param[in] TotalBlocks Number of blocks of data to read + */ +void DataflashManager_ReadBlocks(USB_ClassInfo_MS_Device_t* MSInterfaceInfo, const uint32_t BlockAddress, uint16_t TotalBlocks) +{ + uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); + uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); + uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); + + /* Select the correct starting Dataflash IC for the block requested */ + Dataflash_SelectChipFromPage(CurrDFPage); + + /* Send the dataflash main memory page read command */ + Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); + Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + + /* Wait until endpoint is ready before continuing */ + if (Endpoint_WaitUntilReady()) + return; + + while (TotalBlocks) + { + uint8_t BytesInBlockDiv16 = 0; + + /* Write an endpoint packet sized data block to the dataflash */ + while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) + { + /* Check if the endpoint is currently full */ + if (!(Endpoint_IsReadWriteAllowed())) + { + /* Clear the endpoint bank to send its contents to the host */ + Endpoint_ClearIN(); + + /* Wait until the endpoint is ready for more data */ + if (Endpoint_WaitUntilReady()) + return; + } + + /* Check if end of dataflash page reached */ + if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Reset the dataflash buffer counter, increment the page counter */ + CurrDFPageByteDiv16 = 0; + CurrDFPage++; + + /* Select the next dataflash chip based on the new dataflash page index */ + Dataflash_SelectChipFromPage(CurrDFPage); + + /* Send the dataflash main memory page read command */ + Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + } + + /* Read one 16-byte chunk of data from the dataflash */ + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + Endpoint_Write_Byte(Dataflash_ReceiveByte()); + + /* Increment the dataflash page 16 byte block counter */ + CurrDFPageByteDiv16++; + + /* Increment the block 16 byte block counter */ + BytesInBlockDiv16++; + + /* Check if the current command is being aborted by the host */ + if (MSInterfaceInfo->State.IsMassStoreReset) + return; + } + + /* Decrement the blocks remaining counter */ + TotalBlocks--; + } + + /* If the endpoint is full, send its contents to the host */ + if (!(Endpoint_IsReadWriteAllowed())) + Endpoint_ClearIN(); + + /* Deselect all dataflash chips */ + Dataflash_DeselectChip(); +} + +/** Writes blocks (OS blocks, not Dataflash pages) to the storage medium, the board dataflash IC(s), from + * the a given RAM buffer. This routine reads in OS sized blocks from the buffer and writes them to the + * dataflash in Dataflash page sized blocks. This can be linked to FAT libraries to write files to the + * dataflash. + * + * \param[in] BlockAddress Data block starting address for the write sequence + * \param[in] TotalBlocks Number of blocks of data to write + * \param[in] BufferPtr Pointer to the data source RAM buffer + */ +void DataflashManager_WriteBlocks_RAM(const uint32_t BlockAddress, uint16_t TotalBlocks, const uint8_t* BufferPtr) +{ + uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); + uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); + uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); + bool UsingSecondBuffer = false; + + /* Select the correct starting Dataflash IC for the block requested */ + Dataflash_SelectChipFromPage(CurrDFPage); + +#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) + /* Copy selected dataflash's current page contents to the dataflash buffer */ + Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_WaitWhileBusy(); +#endif + + /* Send the dataflash buffer write command */ + Dataflash_SendByte(DF_CMD_BUFF1WRITE); + Dataflash_SendAddressBytes(0, CurrDFPageByte); + + while (TotalBlocks) + { + uint8_t BytesInBlockDiv16 = 0; + + /* Write an endpoint packet sized data block to the dataflash */ + while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) + { + /* Check if end of dataflash page reached */ + if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Write the dataflash buffer contents back to the dataflash page */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); + Dataflash_SendAddressBytes(CurrDFPage, 0); + + /* Reset the dataflash buffer counter, increment the page counter */ + CurrDFPageByteDiv16 = 0; + CurrDFPage++; + + /* Once all the dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */ + if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) + UsingSecondBuffer = !(UsingSecondBuffer); + + /* Select the next dataflash chip based on the new dataflash page index */ + Dataflash_SelectChipFromPage(CurrDFPage); + +#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) + /* If less than one dataflash page remaining, copy over the existing page to preserve trailing data */ + if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Copy selected dataflash's current page contents to the dataflash buffer */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_WaitWhileBusy(); + } +#endif + + /* Send the dataflash buffer write command */ + Dataflash_ToggleSelectedChipCS(); + Dataflash_SendByte(DF_CMD_BUFF1WRITE); + Dataflash_SendAddressBytes(0, 0); + } + + /* Write one 16-byte chunk of data to the dataflash */ + for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) + Dataflash_SendByte(*(BufferPtr++)); + + /* Increment the dataflash page 16 byte block counter */ + CurrDFPageByteDiv16++; + + /* Increment the block 16 byte block counter */ + BytesInBlockDiv16++; + } + + /* Decrement the blocks remaining counter and reset the sub block counter */ + TotalBlocks--; + } + + /* Write the dataflash buffer contents back to the dataflash page */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); + Dataflash_SendAddressBytes(CurrDFPage, 0x00); + Dataflash_WaitWhileBusy(); + + /* Deselect all dataflash chips */ + Dataflash_DeselectChip(); +} + +/** Reads blocks (OS blocks, not Dataflash pages) from the storage medium, the board dataflash IC(s), into + * the a preallocated RAM buffer. This routine reads in Dataflash page sized blocks from the Dataflash + * and writes them in OS sized blocks to the given buffer. This can be linked to FAT libraries to read + * the files stored on the dataflash. + * + * \param[in] BlockAddress Data block starting address for the read sequence + * \param[in] TotalBlocks Number of blocks of data to read + * \param[out] BufferPtr Pointer to the data destination RAM buffer + */ +void DataflashManager_ReadBlocks_RAM(const uint32_t BlockAddress, uint16_t TotalBlocks, uint8_t* BufferPtr) +{ + uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); + uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); + uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); + + /* Select the correct starting Dataflash IC for the block requested */ + Dataflash_SelectChipFromPage(CurrDFPage); + + /* Send the dataflash main memory page read command */ + Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); + Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + + while (TotalBlocks) + { + uint8_t BytesInBlockDiv16 = 0; + + /* Write an endpoint packet sized data block to the dataflash */ + while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) + { + /* Check if end of dataflash page reached */ + if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Reset the dataflash buffer counter, increment the page counter */ + CurrDFPageByteDiv16 = 0; + CurrDFPage++; + + /* Select the next dataflash chip based on the new dataflash page index */ + Dataflash_SelectChipFromPage(CurrDFPage); + + /* Send the dataflash main memory page read command */ + Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + } + + /* Read one 16-byte chunk of data from the dataflash */ + for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) + *(BufferPtr++) = Dataflash_ReceiveByte(); + + /* Increment the dataflash page 16 byte block counter */ + CurrDFPageByteDiv16++; + + /* Increment the block 16 byte block counter */ + BytesInBlockDiv16++; + } + + /* Decrement the blocks remaining counter */ + TotalBlocks--; + } + + /* Deselect all dataflash chips */ + Dataflash_DeselectChip(); +} + +/** Disables the dataflash memory write protection bits on the board Dataflash ICs, if enabled. */ +void DataflashManager_ResetDataflashProtections(void) +{ + /* Select first dataflash chip, send the read status register command */ + Dataflash_SelectChip(DATAFLASH_CHIP1); + Dataflash_SendByte(DF_CMD_GETSTATUS); + + /* Check if sector protection is enabled */ + if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) + { + Dataflash_ToggleSelectedChipCS(); + + /* Send the commands to disable sector protection */ + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]); + } + + /* Select second dataflash chip (if present on selected board), send read status register command */ + #if (DATAFLASH_TOTALCHIPS == 2) + Dataflash_SelectChip(DATAFLASH_CHIP2); + Dataflash_SendByte(DF_CMD_GETSTATUS); + + /* Check if sector protection is enabled */ + if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) + { + Dataflash_ToggleSelectedChipCS(); + + /* Send the commands to disable sector protection */ + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]); + } + #endif + + /* Deselect current dataflash chip */ + Dataflash_DeselectChip(); +} + +/** Performs a simple test on the attached Dataflash IC(s) to ensure that they are working. + * + * \return Boolean true if all media chips are working, false otherwise + */ +bool DataflashManager_CheckDataflashOperation(void) +{ + uint8_t ReturnByte; + + /* Test first Dataflash IC is present and responding to commands */ + Dataflash_SelectChip(DATAFLASH_CHIP1); + Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO); + ReturnByte = Dataflash_ReceiveByte(); + Dataflash_DeselectChip(); + + /* If returned data is invalid, fail the command */ + if (ReturnByte != DF_MANUFACTURER_ATMEL) + return false; + + #if (DATAFLASH_TOTALCHIPS == 2) + /* Test second Dataflash IC is present and responding to commands */ + Dataflash_SelectChip(DATAFLASH_CHIP2); + Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO); + ReturnByte = Dataflash_ReceiveByte(); + Dataflash_DeselectChip(); + + /* If returned data is invalid, fail the command */ + if (ReturnByte != DF_MANUFACTURER_ATMEL) + return false; + #endif + + return true; +} diff --git a/Projects/Webserver/Lib/DataflashManager.h b/Projects/Webserver/Lib/DataflashManager.h new file mode 100644 index 0000000000..d3ea0ff50f --- /dev/null +++ b/Projects/Webserver/Lib/DataflashManager.h @@ -0,0 +1,80 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Header file for DataflashManager.c. + */ + +#ifndef _DATAFLASH_MANAGER_H +#define _DATAFLASH_MANAGER_H + + /* Includes: */ + #include + + #include "Descriptors.h" + + #include + #include + #include + #include + + /* Preprocessor Checks: */ + #if (DATAFLASH_PAGE_SIZE % 16) + #error Dataflash page size must be a multiple of 16 bytes. + #endif + + /* Defines: */ + /** Total number of bytes of the storage medium, comprised of one or more dataflash ICs. */ + #define VIRTUAL_MEMORY_BYTES ((uint32_t)DATAFLASH_PAGES * DATAFLASH_PAGE_SIZE * DATAFLASH_TOTALCHIPS) + + /** Block size of the device. This is kept at 512 to remain compatible with the OS despite the underlying + * storage media (Dataflash) using a different native block size. Do not change this value. + */ + #define VIRTUAL_MEMORY_BLOCK_SIZE 512 + + /** Total number of blocks of the virtual memory for reporting to the host as the device's total capacity. Do not + * change this value; change VIRTUAL_MEMORY_BYTES instead to alter the media size. + */ + #define VIRTUAL_MEMORY_BLOCKS (VIRTUAL_MEMORY_BYTES / VIRTUAL_MEMORY_BLOCK_SIZE) + + /* Function Prototypes: */ + void DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t* MSInterfaceInfo, const uint32_t BlockAddress, + uint16_t TotalBlocks); + void DataflashManager_ReadBlocks(USB_ClassInfo_MS_Device_t* MSInterfaceInfo, const uint32_t BlockAddress, + uint16_t TotalBlocks); + void DataflashManager_WriteBlocks_RAM(const uint32_t BlockAddress, uint16_t TotalBlocks, + const uint8_t* BufferPtr) ATTR_NON_NULL_PTR_ARG(3); + void DataflashManager_ReadBlocks_RAM(const uint32_t BlockAddress, uint16_t TotalBlocks, + uint8_t* BufferPtr) ATTR_NON_NULL_PTR_ARG(3); + void DataflashManager_ResetDataflashProtections(void); + bool DataflashManager_CheckDataflashOperation(void); + +#endif diff --git a/Projects/Webserver/Lib/FATFs/00readme.txt b/Projects/Webserver/Lib/FATFs/00readme.txt new file mode 100644 index 0000000000..295be3b9a8 --- /dev/null +++ b/Projects/Webserver/Lib/FATFs/00readme.txt @@ -0,0 +1,110 @@ +FatFs Module Source Files R0.07e (C)ChaN, 2010 + + +FILES + + ffconf.h Configuration file for FatFs module. + ff.h Common include file for FatFs and application module. + ff.c FatFs module. + diskio.h Common include file for FatFs and disk I/O module. + diskio.c Skeleton of low level disk I/O module. + integer.h Alternative type definitions for integer variables. + option Optional external functions. + + Low level disk I/O module is not included in this archive because the FatFs + module is only a generic file system layer and not depend on any specific + storage device. You have to provide a low level disk I/O module that written + to control your storage device. + + + +AGREEMENTS + + FatFs module is an open source software to implement FAT file system to + small embedded systems. This is a free software and is opened for education, + research and commercial developments under license policy of following trems. + + Copyright (C) 2010, ChaN, all right reserved. + + * The FatFs module is a free software and there is NO WARRANTY. + * No restriction on use. You can use, modify and redistribute it for + personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY. + * Redistributions of source code must retain the above copyright notice. + + + +REVISION HISTORY + + Feb 26, 2006 R0.00 Prototype + + Apr 29, 2006 R0.01 First release. + + Jun 01, 2006 R0.02 Added FAT12. + Removed unbuffered mode. + Fixed a problem on small (<32M) patition. + + Jun 10, 2006 R0.02a Added a configuration option _FS_MINIMUM. + + Sep 22, 2006 R0.03 Added f_rename. + Changed option _FS_MINIMUM to _FS_MINIMIZE. + + Dec 11, 2006 R0.03a Improved cluster scan algolithm to write files fast. + Fixed f_mkdir creates incorrect directory on FAT32. + + Feb 04, 2007 R0.04 Supported multiple drive system. (FatFs) + Changed some APIs for multiple drive system. + Added f_mkfs. (FatFs) + Added _USE_FAT32 option. (Tiny-FatFs) + + Apr 01, 2007 R0.04a Supported multiple partitions on a plysical drive. (FatFs) + Fixed an endian sensitive code in f_mkfs. (FatFs) + Added a capability of extending the file size to f_lseek. + Added minimization level 3. + Fixed a problem that can collapse a sector when recreate an + existing file in any sub-directory at non FAT32 cfg. (Tiny-FatFs) + + May 05, 2007 R0.04b Added _USE_NTFLAG option. + Added FSInfo support. + Fixed some problems corresponds to FAT32. (Tiny-FatFs) + Fixed DBCS name can result FR_INVALID_NAME. + Fixed short seek (0 < ofs <= csize) collapses the file object. + + Aug 25, 2007 R0.05 Changed arguments of f_read, f_write. + Changed arguments of f_mkfs. (FatFs) + Fixed f_mkfs on FAT32 creates incorrect FSInfo. (FatFs) + Fixed f_mkdir on FAT32 creates incorrect directory. (FatFs) + + Feb 03, 2008 R0.05a Added f_truncate(). + Added f_utime(). + Fixed off by one error at FAT sub-type determination. + Fixed btr in f_read() can be mistruncated. + Fixed cached sector is not flushed when create and close without write. + + Apr 01, 2008 R0.06 Added f_forward(). (Tiny-FatFs) + Added string functions: fputc(), fputs(), fprintf() and fgets(). + Improved performance of f_lseek() on move to the same or following cluster. + + Apr 01, 2010, R0.07 Merged Tiny-FatFs as a buffer configuration option. + Added long file name support. + Added multiple code page support. + Added re-entrancy for multitask operation. + Added auto cluster size selection to f_mkfs(). + Added rewind option to f_readdir(). + Changed result code of critical errors. + Renamed string functions to avoid name collision. + + Apr 14, 2010, R0.07a Separated out OS dependent code on reentrant cfg. + Added multiple sector size support. + + Jun 21, 2010, R0.07c Fixed f_unlink() may return FR_OK on error. + Fixed wrong cache control in f_lseek(). + Added relative path feature. + Added f_chdir(). + Added f_chdrive(). + Added proper case conversion for extended characters. + + Nov 03,'2010 R0.07e Separated out configuration options from ff.h to ffconf.h. + Added a configuration option, _LFN_UNICODE. + Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. + Fixed name matching error on the 13 char boundary. + Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. diff --git a/Projects/Webserver/Lib/FATFs/diskio.c b/Projects/Webserver/Lib/FATFs/diskio.c new file mode 100644 index 0000000000..c7c837ba4f --- /dev/null +++ b/Projects/Webserver/Lib/FATFs/diskio.c @@ -0,0 +1,92 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* This is a stub disk I/O module that acts as front end of the existing */ +/* disk I/O modules and attach it to FatFs module with common interface. */ +/*-----------------------------------------------------------------------*/ + +#include "diskio.h" + +/*-----------------------------------------------------------------------*/ +/* Inidialize a Drive */ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + return FR_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Return Disk Status */ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + return FR_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Sector address (LBA) */ + BYTE count /* Number of sectors to read (1..255) */ +) +{ + DataflashManager_ReadBlocks_RAM(sector, count, buff); + return RES_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0..) */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Sector address (LBA) */ + BYTE count /* Number of sectors to write (1..255) */ +) +{ + DataflashManager_WriteBlocks_RAM(sector, count, buff); + return RES_OK; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + if (ctrl == CTRL_SYNC) + return RES_OK; + else + return RES_PARERR; +} + + +DWORD get_fattime (void) +{ + return ((DWORD)1 << 25) | + ((DWORD)1 << 21) | + ((DWORD)1 << 16) | + ((DWORD)1 << 11) | + ((DWORD)1 << 5) | + ((DWORD)1 << 0); +} diff --git a/Projects/Webserver/Lib/FATFs/diskio.h b/Projects/Webserver/Lib/FATFs/diskio.h new file mode 100644 index 0000000000..3df93ae7ad --- /dev/null +++ b/Projects/Webserver/Lib/FATFs/diskio.h @@ -0,0 +1,72 @@ +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file R0.07 (C)ChaN, 2010 +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO + +#define _READONLY 0 /* 1: Read-only mode */ +#define _USE_IOCTL 1 + +#include "integer.h" +#include "ff.h" + +#include "../DataflashManager.h" + + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + +BOOL assign_drives (int argc, char *argv[]); +DSTATUS disk_initialize (BYTE); +DSTATUS disk_status (BYTE); +DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); +#if _READONLY == 0 +DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); +#endif +DRESULT disk_ioctl (BYTE, BYTE, void*); + + +/* Disk Status Bits (DSTATUS) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + +/* Command code for disk_ioctrl() */ + +/* Generic command */ +#define CTRL_SYNC 0 /* Mandatory for write functions */ +#define GET_SECTOR_COUNT 1 /* Mandatory for only f_mkfs() */ +#define GET_SECTOR_SIZE 2 /* Mandatory for multiple sector size cfg */ +#define GET_BLOCK_SIZE 3 /* Mandatory for only f_mkfs() */ +#define CTRL_POWER 4 +#define CTRL_LOCK 5 +#define CTRL_EJECT 6 +/* MMC/SDC command */ +#define MMC_GET_TYPE 10 +#define MMC_GET_CSD 11 +#define MMC_GET_CID 12 +#define MMC_GET_OCR 13 +#define MMC_GET_SDSTAT 14 +/* ATA/CF command */ +#define ATA_GET_REV 20 +#define ATA_GET_MODEL 21 +#define ATA_GET_SN 22 + + +#define _DISKIO +#endif diff --git a/Projects/Webserver/Lib/FATFs/diskio.lst b/Projects/Webserver/Lib/FATFs/diskio.lst new file mode 100644 index 0000000000..d3e78054bf --- /dev/null +++ b/Projects/Webserver/Lib/FATFs/diskio.lst @@ -0,0 +1,149 @@ + 1 .file "diskio.c" + 2 __SREG__ = 0x3f + 3 __SP_H__ = 0x3e + 4 __SP_L__ = 0x3d + 5 __CCP__ = 0x34 + 6 __tmp_reg__ = 0 + 7 __zero_reg__ = 1 + 15 .Ltext0: + 16 .section .text.disk_initialize,"ax",@progbits + 17 .global disk_initialize + 19 disk_initialize: + 20 .LFB54: + 21 .LSM0: + 22 .LVL0: + 23 /* prologue: function */ + 24 /* frame size = 0 */ + 25 .LSM1: + 26 0000 80E0 ldi r24,lo8(0) + 27 .LVL1: + 28 /* epilogue start */ + 29 0002 0895 ret + 30 .LFE54: + 32 .section .text.disk_status,"ax",@progbits + 33 .global disk_status + 35 disk_status: + 36 .LFB55: + 37 .LSM2: + 38 .LVL2: + 39 /* prologue: function */ + 40 /* frame size = 0 */ + 41 .LSM3: + 42 0000 80E0 ldi r24,lo8(0) + 43 .LVL3: + 44 /* epilogue start */ + 45 0002 0895 ret + 46 .LFE55: + 48 .section .text.disk_ioctl,"ax",@progbits + 49 .global disk_ioctl + 51 disk_ioctl: + 52 .LFB58: + 53 .LSM4: + 54 .LVL4: + 55 /* prologue: function */ + 56 /* frame size = 0 */ + 57 .LSM5: + 58 0000 6623 tst r22 + 59 0002 01F0 breq .L6 + 60 0004 84E0 ldi r24,lo8(4) + 61 .LVL5: + 62 0006 0895 ret + 63 .LVL6: + 64 .L6: + 65 0008 80E0 ldi r24,lo8(0) + 66 .LVL7: + 67 .LSM6: + 68 000a 0895 ret + 69 .LFE58: + 71 .section .text.get_fattime,"ax",@progbits + 72 .global get_fattime + 74 get_fattime: + 75 .LFB59: + 76 .LSM7: + 77 /* prologue: function */ + 78 /* frame size = 0 */ + 79 .LSM8: + 80 0000 61E2 ldi r22,lo8(35719201) + 81 0002 78E0 ldi r23,hi8(35719201) + 82 0004 81E2 ldi r24,hlo8(35719201) + 83 0006 92E0 ldi r25,hhi8(35719201) + 84 /* epilogue start */ + 85 0008 0895 ret + 86 .LFE59: + 88 .section .text.disk_write,"ax",@progbits + 89 .global disk_write + 91 disk_write: + 92 .LFB57: + 93 .LSM9: + 94 .LVL8: + 95 0000 0F93 push r16 + 96 .LVL9: + 97 /* prologue: function */ + 98 /* frame size = 0 */ + 99 0002 FB01 movw r30,r22 + 100 .LSM10: + 101 0004 CA01 movw r24,r20 + 102 0006 B901 movw r22,r18 + 103 .LVL10: + 104 0008 402F mov r20,r16 + 105 .LVL11: + 106 000a 50E0 ldi r21,lo8(0) + 107 000c 9F01 movw r18,r30 + 108 .LVL12: + 109 000e 0E94 0000 call DataflashManager_WriteBlocks_RAM + 110 .LVL13: + 111 .LSM11: + 112 0012 80E0 ldi r24,lo8(0) + 113 /* epilogue start */ + 114 0014 0F91 pop r16 + 115 .LVL14: + 116 0016 0895 ret + 117 .LFE57: + 119 .section .text.disk_read,"ax",@progbits + 120 .global disk_read + 122 disk_read: + 123 .LFB56: + 124 .LSM12: + 125 .LVL15: + 126 0000 0F93 push r16 + 127 .LVL16: + 128 /* prologue: function */ + 129 /* frame size = 0 */ + 130 0002 FB01 movw r30,r22 + 131 .LSM13: + 132 0004 CA01 movw r24,r20 + 133 0006 B901 movw r22,r18 + 134 .LVL17: + 135 0008 402F mov r20,r16 + 136 .LVL18: + 137 000a 50E0 ldi r21,lo8(0) + 138 000c 9F01 movw r18,r30 + 139 .LVL19: + 140 000e 0E94 0000 call DataflashManager_ReadBlocks_RAM + 141 .LVL20: + 142 .LSM14: + 143 0012 80E0 ldi r24,lo8(0) + 144 /* epilogue start */ + 145 0014 0F91 pop r16 + 146 .LVL21: + 147 0016 0895 ret + 148 .LFE56: + 214 .Letext0: +DEFINED SYMBOLS + *ABS*:00000000 diskio.c +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:2 *ABS*:0000003f __SREG__ +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:3 *ABS*:0000003e __SP_H__ +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:4 *ABS*:0000003d __SP_L__ +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:5 *ABS*:00000034 __CCP__ +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:6 *ABS*:00000000 __tmp_reg__ +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:7 *ABS*:00000001 __zero_reg__ +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:19 .text.disk_initialize:00000000 disk_initialize +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:35 .text.disk_status:00000000 disk_status +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:51 .text.disk_ioctl:00000000 disk_ioctl +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:74 .text.get_fattime:00000000 get_fattime +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:91 .text.disk_write:00000000 disk_write +C:\Users\Dean\AppData\Local\Temp/cc5TUdwu.s:122 .text.disk_read:00000000 disk_read + +UNDEFINED SYMBOLS +DataflashManager_WriteBlocks_RAM +DataflashManager_ReadBlocks_RAM diff --git a/Projects/Webserver/Lib/FATFs/ff.c b/Projects/Webserver/Lib/FATFs/ff.c new file mode 100644 index 0000000000..6382fea1fe --- /dev/null +++ b/Projects/Webserver/Lib/FATFs/ff.c @@ -0,0 +1,3153 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.07e (C)ChaN, 2010 +/-----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following trems. +/ +/ Copyright (C) 2010, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Feb 26,'06 R0.00 Prototype. +/ +/ Apr 29,'06 R0.01 First stable version. +/ +/ Jun 01,'06 R0.02 Added FAT12 support. +/ Removed unbuffered mode. +/ Fixed a problem on small (<32M) patition. +/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). +/ +/ Sep 22,'06 R0.03 Added f_rename(). +/ Changed option _FS_MINIMUM to _FS_MINIMIZE. +/ Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast. +/ Fixed f_mkdir() creates incorrect directory on FAT32. +/ +/ Feb 04,'07 R0.04 Supported multiple drive system. +/ Changed some interfaces for multiple drive system. +/ Changed f_mountdrv() to f_mount(). +/ Added f_mkfs(). +/ Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive. +/ Added a capability of extending file size to f_lseek(). +/ Added minimization level 3. +/ Fixed an endian sensitive code in f_mkfs(). +/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. +/ Added FSInfo support. +/ Fixed DBCS name can result FR_INVALID_NAME. +/ Fixed short seek (<= csize) collapses the file object. +/ +/ Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). +/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo. +/ Fixed f_mkdir() on FAT32 creates incorrect directory. +/ Feb 03,'08 R0.05a Added f_truncate() and f_utime(). +/ Fixed off by one error at FAT sub-type determination. +/ Fixed btr in f_read() can be mistruncated. +/ Fixed cached sector is not flushed when create and close +/ without write. +/ +/ Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets(). +/ Improved performance of f_lseek() on moving to the same +/ or following cluster. +/ +/ Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option. +/ Added long file name support. +/ Added multiple code page support. +/ Added re-entrancy for multitask operation. +/ Added auto cluster size selection to f_mkfs(). +/ Added rewind option to f_readdir(). +/ Changed result code of critical errors. +/ Renamed string functions to avoid name collision. +/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. +/ Added multiple sector size support. +/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error. +/ Fixed wrong cache control in f_lseek(). +/ Added relative path feature. +/ Added f_chdir() and f_chdrive(). +/ Added proper case conversion to extended char. +/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. +/ Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. +/ Fixed name matching error on the 13 char boundary. +/ Added a configuration option, _LFN_UNICODE. +/ Changed f_readdir() to return the SFN with always upper +/ case on non-LFN cfg. +/---------------------------------------------------------------------------*/ + +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 0x007E +#error Wrong include file (ff.h). +#endif + +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area must not be used in re-entrant configuration. +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } + +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res + +#endif + +#define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } + +#ifndef NULL +#define NULL 0 +#endif + +/* Name status flags */ +#define NS 11 /* Offset of name status byte */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ + + + + +/*-------------------------------------------------------------------------- + + Private Work Area + +---------------------------------------------------------------------------*/ + +#if _DRIVES < 1 || _DRIVES > 9 +#error Number of drives must be 1-9. +#endif +static +FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */ + +static +WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH +static +BYTE Drive; /* Current drive */ +#endif + + +#if _USE_LFN == 1 /* LFN with static LFN working buffer */ +static +WCHAR LfnBuf[_MAX_LFN + 1]; +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR *lp = LfnBuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#elif _USE_LFN > 1 /* LFN with dynamic LFN working buffer */ +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#else /* No LFN */ +#define NAMEBUF(sp,lp) BYTE sp[12] +#define INITBUF(dj,sp,lp) dj.fn = sp + +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, int cnt) { + char *d = (char*)dst; + const char *s = (const char *)src; + while (cnt--) *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, int cnt) { + char *d = (char*)dst; + while (cnt--) *d++ = (char)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, int cnt) { + const char *d = (const char *)dst, *s = (const char *)src; + int r = 0; + while (cnt-- && (r = *d++ - *s++) == 0) ; + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { + while (*str && *str != chr) str++; + return *str; +} + + + +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +#if _FS_REENTRANT + +static +BOOL lock_fs ( + FATFS *fs /* File system object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS *fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (res != FR_NOT_ENABLED && + res != FR_INVALID_DRIVE && + res != FR_INVALID_OBJECT && + res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT move_window ( + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make apperance in the fs->win[] */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; + + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#if !_FS_READONLY + if (fs->wflag) { /* Write back dirty window if needed */ + if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK) + return FR_DISK_ERR; + fs->wflag = 0; + if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ + BYTE nf; + for (nf = fs->n_fats; nf > 1; nf--) { /* Refrect the change to all FAT copies */ + wsect += fs->sects_fat; + disk_write(fs->drive, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK) + return FR_DISK_ERR; + fs->winsect = sector; + } + } + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Clean-up cached data */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ + FATFS *fs /* File system object */ +) +{ + FRESULT res; + + + res = move_window(fs, 0); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { + fs->winsect = 0; + mem_set(fs->win, 0, 512); + ST_WORD(fs->win+BS_55AA, 0xAA55); + ST_DWORD(fs->win+FSI_LeadSig, 0x41615252); + ST_DWORD(fs->win+FSI_StrucSig, 0x61417272); + ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust); + ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust); + disk_write(fs->drive, fs->win, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to get the link information */ +) +{ + UINT wc, bc; + DWORD fsect; + + + if (clst < 2 || clst >= fs->max_clust) /* Range check */ + return 1; + + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc = fs->win[bc & (SS(fs) - 1)]; bc++; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break; + return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]); + + case FS_FAT32 : + if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break; + return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF; + } + + return 0xFFFFFFFF; /* An error occured at the disk I/O layer */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY + +FRESULT put_fat ( + FATFS *fs, /* File system object */ + DWORD clst, /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */ + DWORD val /* New value to mark the cluster */ +) +{ + UINT bc; + BYTE *p; + DWORD fsect; + FRESULT res; + + + if (clst < 2 || clst >= fs->max_clust) { /* Range check */ + res = FR_INT_ERR; + + } else { + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + res = move_window(fs, fsect + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val); + break; + + case FS_FAT32 : + res = move_window(fs, fsect + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val); + break; + + default : + res = FR_INT_ERR; + } + fs->wflag = 1; + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + FRESULT res; + DWORD nxt; + + + if (clst < 2 || clst >= fs->max_clust) { /* Check the range of cluster# */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->max_clust) { /* Not a last link? */ + nxt = get_fat(fs, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ + if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ + res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ + if (res != FR_OK) break; + if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */ + fs->free_clust++; + fs->fsi_flag = 1; + } + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch or Create a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ +) +{ + DWORD cs, ncl, scl, mcl; + + + mcl = fs->max_clust; + if (clst == 0) { /* Create new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (scl == 0 || scl >= mcl) scl = 1; + } + else { /* Stretch existing chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* It is an invalid cluster */ + if (cs < mcl) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= mcl) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free custer */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */ + return cs; + if (ncl == scl) return 0; /* No free custer */ + } + + if (put_fat(fs, ncl, 0x0FFFFFFF)) /* Mark the new cluster "in use" */ + return 0xFFFFFFFF; + if (clst != 0) { /* Link it to the previous one if needed */ + if (put_fat(fs, clst, ncl)) + return 0xFFFFFFFF; + } + + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag = 1; + } + + return ncl; /* Return new cluster number */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + + +DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Seek directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_seek ( + DIR *dj, /* Pointer to directory object */ + WORD idx /* Directory index number */ +) +{ + DWORD clst; + WORD ic; + + + dj->index = idx; + clst = dj->sclust; + if (clst == 1 || clst >= dj->fs->max_clust) /* Check start cluster range */ + return FR_INT_ERR; + if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dj->fs->dirbase; + + if (clst == 0) { /* Static table */ + dj->clust = clst; + if (idx >= dj->fs->n_rootdir) /* Index is out of range */ + return FR_INT_ERR; + dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32); /* Sector# */ + } + else { /* Dynamic table */ + ic = SS(dj->fs) / 32 * dj->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dj->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dj->fs->max_clust) /* Reached to end of table or int error */ + return FR_INT_ERR; + idx -= ic; + } + dj->clust = clst; + dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32); /* Sector# */ + } + + dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32; /* Ptr to the entry in the sector */ + + return FR_OK; /* Seek succeeded */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */ + DIR *dj, /* Pointer to directory object */ + BOOL streach /* FALSE: Do not streach table, TRUE: Streach table if needed */ +) +{ + DWORD clst; + WORD i; + + + i = dj->index + 1; + if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % (SS(dj->fs) / 32))) { /* Sector changed? */ + dj->sect++; /* Next sector */ + + if (dj->clust == 0) { /* Static table */ + if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dj->fs, dj->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + if (clst >= dj->fs->max_clust) { /* When it reached end of dynamic table */ +#if !_FS_READONLY + BYTE c; + if (!streach) return FR_NO_FILE; /* When do not streach, report EOT */ + clst = create_chain(dj->fs, dj->clust); /* Streach cluster chain */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + /* Clean-up streached table */ + if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */ + mem_set(dj->fs->win, 0, SS(dj->fs)); /* Clear window buffer */ + dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */ + for (c = 0; c < dj->fs->csize; c++) { /* Fill the new cluster with 0 */ + dj->fs->wflag = 1; + if (move_window(dj->fs, 0)) return FR_DISK_ERR; + dj->fs->winsect++; + } + dj->fs->winsect -= c; /* Rewind window address */ +#else + return FR_NO_FILE; /* Report EOT */ +#endif + } + dj->clust = clst; /* Initialize data for new cluster */ + dj->sect = clust2sect(dj->fs, clst); + } + } + } + + dj->index = i; + dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */ + + +static +BOOL cmp_lfn ( /* TRUE:Matched, FALSE:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + int i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Get offset in the LFN buffer */ + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + wc = ff_wtoupper(uc); /* Convert it to upper case */ + if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return FALSE; /* Not matched */ + } else { + if (uc != 0xFFFF) return FALSE; /* Check filler */ + } + } while (++s < 13); /* Repeat until all chars in the entry are checked */ + + if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i]) /* Last segment matched but different length */ + return FALSE; + + return TRUE; /* The part of LFN matched */ +} + + + +static +BOOL pick_lfn ( /* TRUE:Succeeded, FALSE:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + int i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + if (i >= _MAX_LFN) return FALSE; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return FALSE; /* Check filler */ + } + } while (++s < 13); /* Read all character in the entry */ + + if (dir[LDIR_Ord] & 0x40) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return FALSE; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return TRUE; +} + + +#if !_FS_READONLY +static +void fit_lfn ( + const WCHAR *lfnbuf, /* Pointer to the LFN buffer */ + BYTE *dir, /* Pointer to the directory entry */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* SFN sum */ +) +{ + int i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + ST_WORD(dir+LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */ + ST_WORD(dir+LfnOfs[s], wc); /* Put it */ + if (!wc) wc = 0xFFFF; /* Padding chars following last char */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40; /* Bottom LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Create numbered name */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +void gen_numname ( + BYTE *dst, /* Pointer to genartated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD num /* Sequense number */ +) +{ + char ns[8]; + int i, j; + + + mem_cpy(dst, src, 11); + + if (num > 5) { /* On many collisions, generate a hash number instead of sequencial number */ + do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn); + } + + /* itoa */ + i = 7; + do { + ns[i--] = (num % 10) + '0'; + num /= 10; + } while (num); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Calculate sum of an SFN */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +BYTE sum_sfn ( + const BYTE *dir /* Ptr to directory entry */ +) +{ + BYTE sum = 0; + int n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( + DIR *dj /* Pointer to the directory object linked to the file name */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_seek(dj, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + +#if _USE_LFN + ord = sum = 0xFF; +#endif + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == 0xE5 || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (dj->lfn) { + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; /* LFN start order */ + dj->lfn_idx = dj->index; + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ + ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break; /* SFN matched? */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +FRESULT dir_read ( + DIR *dj /* Pointer to the directory object that pointing the entry to be read */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord = 0xFF, sum = 0xFF; +#endif + + res = FR_NO_FILE; + while (dj->sect) { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; + dj->lfn_idx = dj->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ + dj->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dj->sect = 0; + + return res; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ + DIR *dj /* Target directory with object name to be created */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN /* LFN configuration */ + WORD n, ne, is; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + + fn = dj->fn; lfn = dj->lfn; + mem_cpy(sn, fn, 12); + + if (_FS_RPATH && (sn[NS] & NS_DOT)) return FR_INVALID_NAME; /* Cannot create dot entry */ + + if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NS] = 0; dj->lfn = NULL; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_find(dj); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + fn[NS] = sn[NS]; dj->lfn = lfn; + } + + if (sn[NS] & NS_LFN) { /* When LFN is to be created, reserve reserve an SFN + LFN entries. */ + for (ne = 0; lfn[ne]; ne++) ; + ne = (ne + 25) / 13; + } else { /* Otherwise reserve only an SFN entry. */ + ne = 1; + } + + /* Reserve contiguous entries */ + res = dir_seek(dj, 0); + if (res != FR_OK) return res; + n = is = 0; + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; /* Check the entry status */ + if (c == 0xE5 || c == 0) { /* Is it a blank entry? */ + if (n == 0) is = dj->index; /* First index of the contigulus entry */ + if (++n == ne) break; /* A contiguous entry that requiered count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, TRUE); /* Next entry with table streach */ + } while (res == FR_OK); + + if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ + res = dir_seek(dj, is); + if (res == FR_OK) { + sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */ + ne--; + do { /* Store LFN entries in bottom first */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum); + dj->fs->wflag = 1; + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_seek(dj, 0); + if (res == FR_OK) { + do { /* Find a blank entry for the SFN */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; + if (c == 0xE5 || c == 0) break; /* Is it a blank entry? */ + res = dir_next(dj, TRUE); /* Next entry with table streach */ + } while (res == FR_OK); + } +#endif + + if (res == FR_OK) { /* Initialize the SFN entry */ + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + dir = dj->dir; + mem_set(dir, 0, 32); /* Clean the entry */ + mem_cpy(dir, dj->fn, 11); /* Put SFN */ + dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */ + dj->fs->wflag = 1; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY && !_FS_MINIMIZE +static +FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ + DIR *dj /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + WORD i; + + i = dj->index; /* SFN index */ + res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); /* Goto the SFN or top of the LFN entries */ + if (res == FR_OK) { + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + if (dj->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_seek(dj, dj->index); + if (res == FR_OK) { + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( + DIR *dj, /* Pointer to the directory object */ + const XCHAR **path /* Pointer to pointer to the segment in the path string */ +) +{ +#ifdef _EXCVT + static const BYTE cvt[] = _EXCVT; +#endif + +#if _USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + int i, ni, si, di; + const XCHAR *p; + + /* Create LFN in Unicode */ + si = di = 0; + p = *path; + lfn = dj->lfn; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (di >= _MAX_LFN) /* Reject too long name */ + return FR_INVALID_NAME; +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* If it is a DBC 1st byte */ + b = p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(b)) /* Reject invalid code for DBC */ + return FR_INVALID_NAME; + w = (w << 8) + b; + } + w = ff_convert(w, 1); /* Convert OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */ + return FR_INVALID_NAME; + lfn[di++] = w; /* Store the Unicode char */ + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ +#if _FS_RPATH + if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */ + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { + lfn[di] = 0; + for (i = 0; i < 11; i++) + dj->fn[i] = (i < di) ? '.' : ' '; + dj->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject null string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + mem_set(dj->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + b = i = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN char */ + if (!w) break; /* Break on enf of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII char */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = cvt[w - 0x80]; /* Convert extended char to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Double byte char */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dj->fn[i++] = (BYTE)(w >> 8); + } else { /* Single byte char */ + if (!w || chk_chr("+,;[=]", w)) { /* Replace illegal chars for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN; /* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dj->fn[i++] = (BYTE)w; + } + + if (dj->fn[0] == 0xE5) dj->fn[0] = 0x05; /* If the first char collides with deleted mark, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ + cf |= NS_LFN; + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dj->fn[NS] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* Non-LFN configuration */ + BYTE b, c, d, *sfn; + int ni, si, i; + const char *p; + + /* Create file name in directory form */ + sfn = dj->fn; + mem_set(sfn, ' ', 11); + si = i = b = 0; ni = 8; + p = *path; +#if _FS_RPATH + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = &p[si]; /* Rerurn pointer to the next segment */ + sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + return FR_OK; + } +#endif + for (;;) { + c = p[si++]; + if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') return FR_INVALID_NAME; + i = 8; ni = 11; + b <<= 2; continue; + } + if (c >= 0x80) { /* Extended char */ +#ifdef _EXCVT + c = cvt[c - 0x80]; /* Convert extend char (SBCS) */ +#else + b |= 3; /* Eliminate NT flag if ext char is exist */ +#if !_DF1S /* ASCII only cfg */ + return FR_INVALID_NAME; +#endif +#endif + } + if (IsDBCS1(c)) { /* DBC 1st byte? */ + d = p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ + return FR_INVALID_NAME; + sfn[i++] = c; + sfn[i++] = d; + } else { /* Single byte code */ + if (chk_chr(" \"*+,[=]|\x7F", c)) /* Reject illegal chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { /* ASCII large capital? */ + b |= 2; + } else { + if (IsLower(c)) { /* ASCII small capital? */ + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject null string */ + if (sfn[0] == 0xE5) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Filename has only small capital) */ + + sfn[NS] = c; /* Store NT flag, File name is created */ + + return FR_OK; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +void get_fileinfo ( /* No return code */ + DIR *dj, /* Pointer to the directory object */ + FILINFO *fno /* Pointer to the file information to be filled */ +) +{ + int i; + BYTE c, nt, *dir; + char *p; + + + p = fno->fname; + if (dj->sect) { + dir = dj->dir; + nt = dir[DIR_NTres]; /* NT flag */ + for (i = 0; i < 8; i++) { /* Copy name body */ + c = dir[i]; + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20; + *p++ = c; + } + if (dir[8] != ' ') { /* Copy name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') break; + if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20; + *p++ = c; + } + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ + } + *p = 0; + +#if _USE_LFN + if (fno->lfname) { + XCHAR *tp = fno->lfname; + WCHAR w, *lfn; + + i = 0; + if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ + lfn = dj->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN char */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM conversion */ + if (!w) { i = 0; break; } /* Could not convert, no LFN */ + if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC */ + tp[i++] = (XCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overrun, no LFN */ + tp[i++] = (XCHAR)w; + } + } + tp[i] = 0; /* Terminator */ + } +#endif +} +#endif /* _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR *dj, /* Directory object to return last directory and found object */ + const XCHAR *path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, last; + + + while (!_USE_LFN && *path == ' ') path++; /* Skip leading spaces */ +#if _FS_RPATH + if (*path == '/' || *path == '\\') { /* There is a heading separator */ + path++; dj->sclust = 0; /* Strip it and start from the root dir */ + } else { /* No heading saparator */ + dj->sclust = dj->fs->cdir; /* Start from the current dir */ + } +#else + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dj->sclust = 0; /* Start from the root dir */ +#endif + + if ((UINT)*path < ' ') { /* Null path means the start directory itself */ + res = dir_seek(dj, 0); + dj->dir = NULL; + + } else { /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj); /* Find it */ + last = *(dj->fn+NS) & NS_LAST; + if (res != FR_OK) { /* Could not find the object */ + if (res == FR_NO_FILE && !last) + res = FR_NO_PATH; + break; + } + if (last) break; /* Last segment match. Function completed. */ + dir = dj->dir; /* There is next segment. Follow the sub directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */ + res = FR_NO_PATH; break; + } + dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is an FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */ + FATFS *fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK) /* Load boot record */ + return 3; + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */ + return 2; + + if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Make sure that the file system is valid */ +/*-----------------------------------------------------------------------*/ + + +FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occured */ + const XCHAR **path, /* Pointer to pointer to the path name (drive number) */ + FATFS **rfs, /* Pointer to pointer to the found file system object */ + BYTE chk_wp /* !=0: Check media write protection for write access */ +) +{ + BYTE fmt, *tbl; + UINT vol; + DSTATUS stat; + DWORD bsect, fsize, tsect, mclst; + const XCHAR *p = *path; + FATFS *fs; + + /* Get logical drive number from the path name */ + vol = p[0] - '0'; /* Is there a drive number? */ + if (vol <= 9 && p[1] == ':') { /* Found a drive number, get and strip it */ + p += 2; *path = p; /* Return pointer to the path name */ + } else { /* No drive number is given */ +#if _FS_RPATH + vol = Drive; /* Use current drive */ +#else + vol = 0; /* Use drive 0 */ +#endif + } + + /* Check if the logical drive is valid or not */ + if (vol >= _DRIVES) /* Is the drive number valid? */ + return FR_INVALID_DRIVE; + *rfs = fs = FatFs[vol]; /* Returen pointer to the corresponding file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock file system */ + + if (fs->fs_type) { /* If the logical drive has been mounted */ + stat = disk_status(fs->drive); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized (has not been changed), */ +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + return FR_OK; /* The file system object is valid */ + } + } + + /* The logical drive must be mounted. Following code attempts to mount the volume */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drive = (BYTE)LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drive); /* Initialize low level disk I/O layer */ + if (stat & STA_NOINIT) /* Check if the drive is ready */ + return FR_NOT_READY; +#if _MAX_SS != 512 /* Get disk sector size if needed */ + if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS) + return FR_NO_FILESYSTEM; +#endif +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + /* Search FAT partition on the drive */ + fmt = check_fs(fs, bsect = 0); /* Check sector 0 as an SFD format */ + if (fmt == 1) { /* Not an FAT boot record, it may be patitioned */ + /* Check a partition listed in top of the partition table */ + tbl = &fs->win[MBR_Table + LD2PT(vol) * 16]; /* Partition table */ + if (tbl[4]) { /* Is the partition existing? */ + bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ + fmt = check_fs(fs, bsect); /* Check the partition */ + } + } + if (fmt == 3) return FR_DISK_ERR; + if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* No valid FAT patition is found */ + return FR_NO_FILESYSTEM; + + /* Initialize the file system object */ + fsize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ + if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32); + fs->sects_fat = fsize; + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + fsize *= fs->n_fats; /* (Number of sectors in FAT area) */ + fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */ + fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Nmuber of root directory entries */ + tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */ + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + fs->max_clust = mclst = (tsect /* Last cluster# + 1 (Number of clusters + 2) */ + - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32) + ) / fs->csize + 2; + + fmt = FS_FAT12; /* Determine the FAT sub type */ + if (mclst >= 0xFF7) fmt = FS_FAT16; /* Number of clusters >= 0xFF5 */ + if (mclst >= 0xFFF7) fmt = FS_FAT32; /* Number of clusters >= 0xFFF5 */ + + if (fmt == FS_FAT32) + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ + else + fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */ + fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32); /* Data start sector (lba) */ + +#if !_FS_READONLY + /* Initialize allocation information */ + fs->free_clust = 0xFFFFFFFF; + fs->wflag = 0; + /* Get fsinfo if needed */ + if (fmt == FS_FAT32) { + fs->fsi_flag = 0; + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK && + LD_WORD(fs->win+BS_55AA) == 0xAA55 && + LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 && + LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) { + fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free); + fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count); + } + } +#endif + fs->fs_type = fmt; /* FAT sub-type */ + fs->winsect = 0; /* Invalidate sector cache */ +#if _FS_RPATH + fs->cdir = 0; /* Current directory (root dir) */ +#endif + fs->id = ++Fsid; /* File system mount ID */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/dir object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + FATFS *fs, /* Pointer to the file system object */ + WORD id /* Member id of the target object to be checked */ +) +{ + if (!fs || !fs->fs_type || fs->id != id) + return FR_INVALID_OBJECT; + + ENTER_FF(fs); /* Lock file system */ + + if (disk_status(fs->drive) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Locical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + BYTE vol, /* Logical drive number to be mounted/unmounted */ + FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ +) +{ + FATFS *rfs; + + + if (vol >= _DRIVES) /* Check if the drive number is valid */ + return FR_INVALID_DRIVE; + rfs = FatFs[vol]; /* Get current fs object */ + + if (rfs) { +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR; +#endif + rfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL *fp, /* Pointer to the blank file object */ + const XCHAR *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + fp->fs = NULL; /* Clear file object */ +#if !_FS_READONLY + mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW); + res = chk_mounted(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW))); +#else + mode &= FA_READ; + res = chk_mounted(&path, &dj.fs, 0); +#endif + if (res != FR_OK) LEAVE_FF(dj.fs, res); + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + +#if !_FS_READONLY + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD ps, cl; + + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ + res = dir_register(&dj); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + mode |= FA_CREATE_ALWAYS; + dir = dj.dir; /* Created entry (SFN entry) */ + } + else { /* Any object is already existing */ + if (mode & FA_CREATE_NEW) /* Cannot create new */ + LEAVE_FF(dj.fs, FR_EXIST); + dir = dj.dir; + if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR))) /* Cannot overwrite it (R/O or DIR) */ + LEAVE_FF(dj.fs, FR_DENIED); + if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero on over write mode */ + cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); /* Get start cluster */ + ST_WORD(dir+DIR_FstClusHI, 0); /* cluster = 0 */ + ST_WORD(dir+DIR_FstClusLO, 0); + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + dj.fs->wflag = 1; + ps = dj.fs->winsect; /* Remove the cluster chain */ + if (cl) { + res = remove_chain(dj.fs, cl); + if (res) LEAVE_FF(dj.fs, res); + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + } + res = move_window(dj.fs, ps); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + } + } + if (mode & FA_CREATE_ALWAYS) { + dir[DIR_Attr] = 0; /* Reset attribute */ + ps = get_fattime(); + ST_DWORD(dir+DIR_CrtTime, ps); /* Created time */ + dj.fs->wflag = 1; + mode |= FA__WRITTEN; /* Set file changed flag */ + } + } + /* Open an existing file */ + else { +#endif /* !_FS_READONLY */ + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + dir = dj.dir; + if (!dir || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ + LEAVE_FF(dj.fs, FR_NO_FILE); +#if !_FS_READONLY + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + LEAVE_FF(dj.fs, FR_DENIED); + } + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#endif + fp->flag = mode; /* File access mode */ + fp->org_clust = /* File start cluster */ + ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fp->fptr = 0; fp->csect = 255; /* File pointer */ + fp->dsect = 0; + fp->fs = dj.fs; fp->id = dj.fs->id; /* Owner file system object of the file */ + + LEAVE_FF(dj.fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + void *buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT *br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + DWORD clst, sect, remain; + UINT rcnt, cc; + BYTE *rbuff = buff; + + + *br = 0; /* Initialize bytes read */ + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data transferred */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_fat(fp->fs, fp->curr_clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector offset in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 +#if _FS_TINY + if (fp->fs->wflag && fp->fs->winsect - sect < cc) /* Replace one of the read sectors with cached data if it contains a dirty sector */ + mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); +#else + if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) /* Replace one of the read sectors with cached data if it contains a dirty sector */ + mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); +#endif +#endif + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write sector I/O buffer if needed */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + fp->csect++; /* Next sector address in the cluster */ + } + rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ + if (rcnt > btr) rcnt = btr; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#else + mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#endif + } + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT *bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + DWORD clst, sect; + UINT wcnt, cc; + const BYTE *wbuff = buff; + + + *bw = 0; /* Initialize bytes written */ + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if (fp->fsize + btw < fp->fsize) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data transferred */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->org_clust; /* Follow from the origin */ + if (clst == 0) /* When there is no cluster chain, */ + fp->org_clust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of the file */ + clst = create_chain(fp->fs, fp->curr_clust); /* Follow or streach cluster chain */ + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector address in the cluster */ + } +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write back data buffer prior to following direct transfer */ + ABORT(fp->fs, FR_DISK_ERR); +#else + if (fp->flag & FA__DIRTY) { /* Write back data buffer prior to following direct transfer */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if _FS_TINY + if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */ + mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->fs->wflag = 0; + } +#else + if (fp->dsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->flag &= ~FA__DIRTY; + } +#endif + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly buffer filling at growing edge */ + if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR); + fp->fs->winsect = sect; + } +#else + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + fp->csect++; /* Next sector address in the cluster */ + } + wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Put partial sector into file I/O buffer */ + if (wcnt > btw) wcnt = btw; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->fs->wflag = 1; +#else + mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->flag |= FA__DIRTY; +#endif + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file changed flag */ + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tim; + BYTE *dir; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ +#if !_FS_TINY /* Write-back dirty buffer */ + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + /* Update the directory entry */ + res = move_window(fp->fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */ + ST_WORD(dir+DIR_FstClusLO, fp->org_clust); /* Update start cluster */ + ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16); + tim = get_fattime(); /* Updated time */ + ST_DWORD(dir+DIR_WrtTime, tim); + fp->flag &= ~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync(fp->fs); + } + } + } + + LEAVE_FF(fp->fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#if _FS_READONLY + res = validate(fp->fs, fp->id); + if (res == FR_OK) fp->fs = NULL; + LEAVE_FF(fp->fs, res); +#else + res = f_sync(fp); + if (res == FR_OK) fp->fs = NULL; + return res; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Current Drive/Directory */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH + +FRESULT f_chdrive ( + BYTE drv /* Drive number */ +) +{ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; + + Drive = drv; + + return FR_OK; +} + + + + +FRESULT f_chdir ( + const XCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + dir = dj.dir; /* Pointer to the entry */ + if (!dir) { + dj.fs->cdir = 0; /* No entry (root dir) */ + } else { + if (dir[DIR_Attr] & AM_DIR) /* Reached to the dir */ + dj.fs->cdir = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + else + res = FR_NO_PATH; /* Could not reach the dir (it is a file) */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj.fs, res); +} + +#endif + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + FRESULT res; + DWORD clst, bcs, nsect, ifptr; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#if !_FS_READONLY + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + ifptr = fp->fptr; + fp->fptr = nsect = 0; fp->csect = 255; + if (ofs > 0) { + bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->curr_clust; + } else { /* When seek to back cluster, */ + clst = fp->org_clust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(fp->fs, 0); + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->org_clust = clst; + } +#endif + fp->curr_clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + clst = create_chain(fp->fs, clst); /* Force streached if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; + } + } else +#endif + clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR); + fp->curr_clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + fp->csect = (BYTE)(ofs / SS(fp->fs)); /* Sector offset in the cluster */ + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += fp->csect; + fp->csect++; + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = nsect; + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set changed flag if the file size is extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + + LEAVE_FF(fp->fs, res); +} + + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directroy Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR *dj, /* Pointer to directory object to create */ + const XCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj->fs, 0); + if (res == FR_OK) { + INITBUF((*dj), sfn, lfn); + res = follow_path(dj, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + dir = dj->dir; + if (dir) { /* It is not the root dir */ + if (dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ + dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + } else { /* The object is not a directory */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dj->id = dj->fs->id; + res = dir_seek(dj, 0); /* Rewind dir */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entry in Sequense */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR *dj, /* Pointer to the open directory object */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + NAMEBUF(sfn, lfn); + + + res = validate(dj->fs, dj->id); /* Check validity of the object */ + if (res == FR_OK) { + INITBUF((*dj), sfn, lfn); + if (!fno) { + res = dir_seek(dj, 0); + } else { + res = dir_read(dj); + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dj, fno); /* Get the object information */ + res = dir_next(dj, FALSE); /* Increment index for next */ + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + } + } + } + + LEAVE_FF(dj->fs, res); +} + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const XCHAR *path, /* Pointer to the file path */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follwo completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const XCHAR *path, /* Pointer to the logical drive number (root dir) */ + DWORD *nclst, /* Pointer to the variable to return number of free clusters */ + FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ +) +{ + FRESULT res; + DWORD n, clst, sect, stat; + UINT i; + BYTE fat, *p; + + + /* Get drive number */ + res = chk_mounted(&path, fatfs, 0); + if (res != FR_OK) LEAVE_FF(*fatfs, res); + + /* If number of free cluster is valid, return it without cluster scan. */ + if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) { + *nclst = (*fatfs)->free_clust; + LEAVE_FF(*fatfs, FR_OK); + } + + /* Get number of free clusters */ + fat = (*fatfs)->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + stat = get_fat(*fatfs, clst); + if (stat == 0xFFFFFFFF) LEAVE_FF(*fatfs, FR_DISK_ERR); + if (stat == 1) LEAVE_FF(*fatfs, FR_INT_ERR); + if (stat == 0) n++; + } while (++clst < (*fatfs)->max_clust); + } else { + clst = (*fatfs)->max_clust; + sect = (*fatfs)->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(*fatfs, sect++); + if (res != FR_OK) + LEAVE_FF(*fatfs, res); + p = (*fatfs)->win; + i = SS(*fatfs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; + p += 4; i -= 4; + } + } while (--clst); + } + (*fatfs)->free_clust = n; + if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; + *nclst = n; + + LEAVE_FF(*fatfs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + if (fp->fsize > fp->fptr) { + fp->fsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA__WRITTEN; + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(fp->fs, fp->org_clust); + fp->org_clust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(fp->fs, fp->curr_clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fp->fs->max_clust) { + res = put_fat(fp->fs, fp->curr_clust, 0x0FFFFFFF); + if (res == FR_OK) res = remove_chain(fp->fs, ncl); + } + } + } + if (res != FR_OK) fp->flag |= FA__ERROR; + + LEAVE_FF(fp->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const XCHAR *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + NAMEBUF(sfn, lfn); + BYTE *dir; + DWORD dclst; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + + dir = dj.dir; + if (!dir) /* Is it the root directory? */ + LEAVE_FF(dj.fs, FR_INVALID_NAME); + if (dir[DIR_Attr] & AM_RDO) /* Is it a R/O object? */ + LEAVE_FF(dj.fs, FR_DENIED); + dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + + if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */ + if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR); + mem_cpy(&sdj, &dj, sizeof(DIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_seek(&sdj, 2); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + res = dir_read(&sdj); + if (res == FR_OK) res = FR_DENIED; /* Not empty sub-dir */ + if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res); + } + + res = dir_remove(&dj); /* Remove directory entry */ + if (res == FR_OK) { + if (dclst) + res = remove_chain(dj.fs, dclst); /* Remove the cluster chain */ + if (res == FR_OK) res = sync(dj.fs); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const XCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir, n; + DWORD dsect, dclst, pclst, tim; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any file or directory is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res != FR_NO_FILE) /* Any error occured */ + LEAVE_FF(dj.fs, res); + + dclst = create_chain(dj.fs, 0); /* Allocate a new cluster for new directory table */ + res = FR_OK; + if (dclst == 0) res = FR_DENIED; + if (dclst == 1) res = FR_INT_ERR; + if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) + res = move_window(dj.fs, 0); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + dsect = clust2sect(dj.fs, dclst); + + dir = dj.fs->win; /* Initialize the new directory table */ + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + tim = get_fattime(); + ST_DWORD(dir+DIR_WrtTime, tim); + ST_WORD(dir+DIR_FstClusLO, dclst); + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + mem_cpy(dir+32, dir, 32); /* Create ".." entry */ + dir[33] = '.'; + pclst = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase) + pclst = 0; + ST_WORD(dir+32+DIR_FstClusLO, pclst); + ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16); + for (n = 0; n < dj.fs->csize; n++) { /* Write dot entries and clear left sectors */ + dj.fs->winsect = dsect++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res) LEAVE_FF(dj.fs, res); + mem_set(dir, 0, SS(dj.fs)); + } + + res = dir_register(&dj); + if (res != FR_OK) { + remove_chain(dj.fs, dclst); + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Crated time */ + ST_WORD(dir+DIR_FstClusLO, dclst); /* Table start cluster */ + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change File Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const XCHAR *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Is it a root directory? */ + res = FR_INVALID_NAME; + } else { /* File or sub directory */ + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const XCHAR *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the timestamp to be set */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Root directory */ + res = FR_INVALID_NAME; + } else { /* File or sub-directory */ + ST_WORD(dir+DIR_WrtTime, fno->ftime); + ST_WORD(dir+DIR_WrtDate, fno->fdate); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const XCHAR *path_old, /* Pointer to the old name */ + const XCHAR *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR dj_old, dj_new; + NAMEBUF(sfn, lfn); + BYTE buf[21], *dir; + DWORD dw; + + + INITBUF(dj_old, sfn, lfn); + res = chk_mounted(&path_old, &dj_old.fs, 1); + if (res == FR_OK) { + dj_new.fs = dj_old.fs; + res = follow_path(&dj_old, path_old); /* Check old object */ + if (_FS_RPATH && res == FR_OK && (dj_old.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + } + if (res != FR_OK) LEAVE_FF(dj_old.fs, res); /* The old object is not found */ + + if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE); /* Is root dir? */ + mem_cpy(buf, dj_old.dir+DIR_Attr, 21); /* Save the object information */ + + mem_cpy(&dj_new, &dj_old, sizeof(DIR)); + res = follow_path(&dj_new, path_new); /* Check new object */ + if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ + if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */ + res = dir_register(&dj_new); /* Register the new object */ + if (res == FR_OK) { + dir = dj_new.dir; /* Copy object information into new entry */ + mem_cpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + dj_old.fs->wflag = 1; + if (dir[DIR_Attr] & AM_DIR) { /* Update .. entry in the directory if needed */ + dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(dj_new.fs, dw); + dir = dj_new.fs->win+32; + if (res == FR_OK && dir[1] == '.') { + dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust; + ST_WORD(dir+DIR_FstClusLO, dw); + ST_WORD(dir+DIR_FstClusHI, dw >> 16); + dj_new.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj_old); /* Remove old entry */ + if (res == FR_OK) + res = sync(dj_old.fs); + } + } + } + + LEAVE_FF(dj_old.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly (Available on only _FS_TINY cfg) */ +/*-----------------------------------------------------------------------*/ +#if _USE_FORWARD && _FS_TINY + +FRESULT f_forward ( + FIL *fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btr, /* Number of bytes to forward */ + UINT *bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + DWORD remain, clst, sect; + UINT rcnt; + + + *bf = 0; + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check error flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr && (*func)(NULL, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_fat(fp->fs, fp->curr_clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector address in the cluster */ + } + fp->csect++; /* Next sector address in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect - 1; + if (move_window(fp->fs, sect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + fp->dsect = sect; + rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ + if (rcnt > btr) rcnt = btr; + rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); + if (!rcnt) ABORT(fp->fs, FR_INT_ERR); + } + + LEAVE_FF(fp->fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create File System on the Drive */ +/*-----------------------------------------------------------------------*/ +#define N_ROOTDIR 512 /* Multiple of 32 and <= 2048 */ +#define N_FATS 1 /* 1 or 2 */ +#define MAX_SECTOR 131072000UL /* Maximum partition size */ +#define MIN_SECTOR 2000UL /* Minimum partition size */ + + +FRESULT f_mkfs ( + BYTE drv, /* Logical drive number */ + BYTE partition, /* Partitioning rule 0:FDISK, 1:SFD */ + WORD allocsize /* Allocation unit size [bytes] */ +) +{ + static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000, 0 }; + static const WORD cstbl[] = { 32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512 }; + BYTE fmt, m, *tbl; + DWORD b_part, b_fat, b_dir, b_data; /* Area offset (LBA) */ + DWORD n_part, n_rsv, n_fat, n_dir; /* Area size */ + DWORD n_clst, d, n; + WORD as; + FATFS *fs; + DSTATUS stat; + + + /* Check validity of the parameters */ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; + if (partition >= 2) return FR_MKFS_ABORTED; + + /* Check mounted drive and clear work area */ + fs = FatFs[drv]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + drv = LD2PD(drv); + + /* Get disk statics */ + stat = disk_initialize(drv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; +#if _MAX_SS != 512 /* Get disk sector size */ + if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK + || SS(fs) > _MAX_SS) + return FR_MKFS_ABORTED; +#endif + if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR) + return FR_MKFS_ABORTED; + if (n_part > MAX_SECTOR) n_part = MAX_SECTOR; + b_part = (!partition) ? 63 : 0; /* Boot sector */ + n_part -= b_part; + for (d = 512; d <= 32768U && d != allocsize; d <<= 1) ; /* Check validity of the allocation unit size */ + if (d != allocsize) allocsize = 0; + if (!allocsize) { /* Auto selection of cluster size */ + d = n_part; + for (as = SS(fs); as > 512U; as >>= 1) d >>= 1; + for (n = 0; d < sstbl[n]; n++) ; + allocsize = cstbl[n]; + } + if (allocsize < SS(fs)) allocsize = SS(fs); + + allocsize /= SS(fs); /* Number of sectors per cluster */ + + /* Pre-compute number of clusters and FAT type */ + n_clst = n_part / allocsize; + fmt = FS_FAT12; + if (n_clst >= 0xFF5) fmt = FS_FAT16; + if (n_clst >= 0xFFF5) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + switch (fmt) { + case FS_FAT12: + n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + case FS_FAT16: + n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + default: + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 33 - partition; + n_dir = 0; + } + b_fat = b_part + n_rsv; /* FATs start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */ + b_data = b_dir + n_dir; /* Data start sector */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED; + n = (b_data + n - 1) & ~(n - 1); + n_fat += (n - b_data) / N_FATS; + /* b_dir and b_data are no longer used below */ + + /* Determine number of cluster and final check of validity of the FAT type */ + n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize; + if ( (fmt == FS_FAT16 && n_clst < 0xFF5) + || (fmt == FS_FAT32 && n_clst < 0xFFF5)) + return FR_MKFS_ABORTED; + + /* Create partition table if needed */ + if (!partition) { + DWORD n_disk = b_part + n_part; + + mem_set(fs->win, 0, SS(fs)); + tbl = fs->win+MBR_Table; + ST_DWORD(tbl, 0x00010180); /* Partition start in CHS */ + if (n_disk < 63UL * 255 * 1024) { /* Partition end in CHS */ + n_disk = n_disk / 63 / 255; + tbl[7] = (BYTE)n_disk; + tbl[6] = (BYTE)((n_disk >> 2) | 63); + } else { + ST_WORD(&tbl[6], 0xFFFF); + } + tbl[5] = 254; + if (fmt != FS_FAT32) /* System ID */ + tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06; + else + tbl[4] = 0x0c; + ST_DWORD(tbl+8, 63); /* Partition start in LBA */ + ST_DWORD(tbl+12, n_part); /* Partition size in LBA */ + ST_WORD(tbl+64, 0xAA55); /* Signature */ + if (disk_write(drv, fs->win, 0, 1) != RES_OK) + return FR_DISK_ERR; + partition = 0xF8; + } else { + partition = 0xF0; + } + + /* Create boot record */ + tbl = fs->win; /* Clear buffer */ + mem_set(tbl, 0, SS(fs)); + ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB); /* Boot code (jmp $, nop) */ + ST_WORD(tbl+BPB_BytsPerSec, SS(fs)); /* Sector size */ + tbl[BPB_SecPerClus] = (BYTE)allocsize; /* Sectors per cluster */ + ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */ + if (n_part < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl+BPB_TotSec16, n_part); + } else { + ST_DWORD(tbl+BPB_TotSec32, n_part); + } + tbl[BPB_Media] = partition; /* Media descripter */ + ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */ + ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */ + ST_DWORD(tbl+BPB_HiddSec, b_part); /* Hidden sectors */ + n = get_fattime(); /* Use current time as a VSN */ + if (fmt != FS_FAT32) { + ST_DWORD(tbl+BS_VolID, n); /* Volume serial number */ + ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of secters per FAT */ + tbl[BS_DrvNum] = 0x80; /* Drive number */ + tbl[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab, "NO NAME FAT ", 19); /* Volume lavel, FAT signature */ + } else { + ST_DWORD(tbl+BS_VolID32, n); /* Volume serial number */ + ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of secters per FAT */ + ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory cluster (2) */ + ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (bs+1) */ + ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (bs+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab32, "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */ + } + ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature */ + if (SS(fs) > 512U) { + ST_WORD(tbl+SS(fs)-2, 0xAA55); + } + if (disk_write(drv, tbl, b_part+0, 1) != RES_OK) + return FR_DISK_ERR; + if (fmt == FS_FAT32) + disk_write(drv, tbl, b_part+6, 1); + + /* Initialize FAT area */ + for (m = 0; m < N_FATS; m++) { + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + if (fmt != FS_FAT32) { + n = (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; + n |= partition; + ST_DWORD(tbl, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + ST_DWORD(tbl+0, 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */ + ST_DWORD(tbl+4, 0xFFFFFFFF); + ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ + } + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Following FAT entries are filled by zero */ + for (n = 1; n < n_fat; n++) { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize Root directory */ + m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir); + do { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--m); + + /* Create FSInfo record if needed */ + if (fmt == FS_FAT32) { + ST_WORD(tbl+BS_55AA, 0xAA55); + ST_DWORD(tbl+FSI_LeadSig, 0x41615252); + ST_DWORD(tbl+FSI_StrucSig, 0x61417272); + ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); + ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF); + disk_write(drv, tbl, b_part+1, 1); + disk_write(drv, tbl, b_part+7, 1); + } + + return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +char* f_gets ( + char* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer */ + FIL* fil /* Pointer to the file object */ +) +{ + int i = 0; + char *p = buff; + UINT rc; + + + while (i < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, p, 1, &rc); + if (rc != 1) break; /* Break when no data to read */ +#if _USE_STRFUNC >= 2 + if (*p == '\r') continue; /* Strip '\r' */ +#endif + i++; + if (*p++ == '\n') break; /* Break when reached end of line */ + } + *p = 0; + return i ? buff : NULL; /* When no data read (eof or error), return with error. */ +} + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int f_putc ( + int chr, /* A character to be output */ + FIL* fil /* Ponter to the file object */ +) +{ + UINT bw; + char c; + + +#if _USE_STRFUNC >= 2 + if (chr == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ +#endif + if (!fil) { /* Special value may be used to switch the destination to any other device */ + /* put_console(chr); */ + return chr; + } + c = (char)chr; + f_write(fil, &c, 1, &bw); /* Write a byte to the file */ + return bw ? chr : EOF; /* Return the result */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int f_puts ( + const char* str, /* Pointer to the string to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + int n; + + + for (n = 0; *str; str++, n++) { + if (f_putc(*str, fil) == EOF) return EOF; + } + return n; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ +int f_printf ( + FIL* fil, /* Pointer to the file object */ + const char* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + UCHAR c, f, r; + ULONG val; + char s[16]; + int i, w, res, cc; + + + va_start(arp, str); + + for (cc = res = 0; cc != EOF; res += cc) { + c = *str++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape cahracter */ + cc = f_putc(c, fil); + if (cc != EOF) cc = 1; + continue; + } + w = f = 0; + c = *str++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *str++; + } + while (c >= '0' && c <= '9') { /* Precision */ + w = w * 10 + (c - '0'); + c = *str++; + } + if (c == 'l') { /* Prefix: Size is long int */ + f |= 2; c = *str++; + } + if (c == 's') { /* Type is string */ + cc = f_puts(va_arg(arp, char*), fil); + continue; + } + if (c == 'c') { /* Type is character */ + cc = f_putc(va_arg(arp, int), fil); + if (cc != EOF) cc = 1; + continue; + } + r = 0; + if (c == 'd') r = 10; /* Type is signed decimal */ + if (c == 'u') r = 10; /* Type is unsigned decimal */ + if (c == 'X') r = 16; /* Type is unsigned hexdecimal */ + if (r == 0) break; /* Unknown type */ + if (f & 2) { /* Get the value */ + val = (ULONG)va_arg(arp, long); + } else { + val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int); + } + /* Put numeral string */ + if (c == 'd') { + if (val & 0x80000000) { + val = 0 - val; + f |= 4; + } + } + i = sizeof(s) - 1; s[i] = 0; + do { + c = (UCHAR)(val % r + '0'); + if (c > '9') c += 7; + s[--i] = c; + val /= r; + } while (i && val); + if (i && (f & 4)) s[--i] = '-'; + w = sizeof(s) - 1 - w; + while (i && i > w) s[--i] = (f & 1) ? '0' : ' '; + cc = f_puts(&s[i], fil); + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/Projects/Webserver/Lib/FATFs/ff.h b/Projects/Webserver/Lib/FATFs/ff.h new file mode 100644 index 0000000000..41e136ee50 --- /dev/null +++ b/Projects/Webserver/Lib/FATFs/ff.h @@ -0,0 +1,596 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.07e (C)ChaN, 2010 +/----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following trems. +/ +/ Copyright (C) 2010, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/----------------------------------------------------------------------------*/ + +#ifndef _FATFS +#define _FATFS 0x007E + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONFIG +#error Wrong configuration file (ffconf.h). +#endif + + +/* DBCS code ranges and SBCS extend char conversion table */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} + +#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1253 /* Greek (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ + 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} + +#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + + +/* Character code support macros */ + +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) + +#if _DF1S /* DBCS configuration */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* SBCS configuration */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + + +/* Definitions corresponds to multi partition */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ + +typedef struct _PARTITION { + BYTE pd; /* Physical drive# */ + BYTE pt; /* Partition # (0-3) */ +} PARTITION; + +extern +const PARTITION Drives[]; /* Logical drive# to physical location conversion table */ +#define LD2PD(drv) (Drives[drv].pd) /* Get physical drive# */ +#define LD2PT(drv) (Drives[drv].pt) /* Get partition# */ + +#else /* Single partition configuration */ + +#define LD2PD(drv) (drv) /* Physical drive# is equal to the logical drive# */ +#define LD2PT(drv) 0 /* Always mounts the 1st partition */ + +#endif + + + +/* Definitions corresponds to multiple sector size */ + +#if _MAX_SS == 512 /* Single sector size */ +#define SS(fs) 512U + +#elif _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096 /* Multiple sector size */ +#define SS(fs) ((fs)->s_size) + +#else +#error Sector size must be 512, 1024, 2048 or 4096. + +#endif + + + +/* Type of file name on FatFs API */ + +#if _LFN_UNICODE && _USE_LFN +typedef WCHAR XCHAR; /* Unicode */ +#else +typedef char XCHAR; /* SBCS, DBCS */ +#endif + + + +/* File system object structure */ + +typedef struct _FATFS_ { + BYTE fs_type; /* FAT sub type */ + BYTE drive; /* Physical drive number */ + BYTE csize; /* Number of sectors per cluster */ + BYTE n_fats; /* Number of FAT copies */ + BYTE wflag; /* win[] dirty flag (1:must be written back) */ + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (0 on FAT32) */ +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if _MAX_SS != 512 + WORD s_size; /* Sector size */ +#endif +#if !_FS_READONLY + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector */ +#endif +#if _FS_RPATH + DWORD cdir; /* Current directory (0:root)*/ +#endif + DWORD sects_fat; /* Sectors per fat */ + DWORD max_clust; /* Maximum cluster# + 1. Number of clusters is max_clust - 2 */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (Cluster# on FAT32) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS];/* Disk access window for Directory/FAT */ +} FATFS; + + + +/* Directory object structure */ + +typedef struct _DIR_ { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + WORD index; /* Current read/write index number */ + DWORD sclust; /* Table start cluster (0:Static table) */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ + BYTE* dir; /* Pointer to the current SFN entry in the win[] */ + BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ +#if _USE_LFN + WCHAR* lfn; /* Pointer to the LFN working buffer */ + WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ +#endif +} DIR; + + + +/* File object structure */ + +typedef struct _FIL_ { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE flag; /* File status flags */ + BYTE csect; /* Sector address in the cluster */ + DWORD fptr; /* File R/W pointer */ + DWORD fsize; /* File size */ + DWORD org_clust; /* File start cluster */ + DWORD curr_clust; /* Current cluster */ + DWORD dsect; /* Current data sector */ +#if !_FS_READONLY + DWORD dir_sect; /* Sector containing the directory entry */ + BYTE* dir_ptr; /* Ponter to the directory entry in the window */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS];/* File R/W buffer */ +#endif +} FIL; + + + +/* File status structure */ + +typedef struct _FILINFO_ { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + char fname[13]; /* Short file name (8.3 format) */ +#if _USE_LFN + XCHAR* lfname; /* Pointer to the LFN buffer */ + int lfsize; /* Size of LFN buffer [chrs] */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* 0 */ + FR_DISK_ERR, /* 1 */ + FR_INT_ERR, /* 2 */ + FR_NOT_READY, /* 3 */ + FR_NO_FILE, /* 4 */ + FR_NO_PATH, /* 5 */ + FR_INVALID_NAME, /* 6 */ + FR_DENIED, /* 7 */ + FR_EXIST, /* 8 */ + FR_INVALID_OBJECT, /* 9 */ + FR_WRITE_PROTECTED, /* 10 */ + FR_INVALID_DRIVE, /* 11 */ + FR_NOT_ENABLED, /* 12 */ + FR_NO_FILESYSTEM, /* 13 */ + FR_MKFS_ABORTED, /* 14 */ + FR_TIMEOUT /* 15 */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const XCHAR*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ +FRESULT f_close (FIL*); /* Close an open file object */ +FRESULT f_opendir (DIR*, const XCHAR*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const XCHAR*, FILINFO*); /* Get file status */ +FRESULT f_getfree (const XCHAR*, DWORD*, FATFS**); /* Get number of free clusters on the drive */ +FRESULT f_truncate (FIL*); /* Truncate file */ +FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ +FRESULT f_unlink (const XCHAR*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const XCHAR*); /* Create a new directory */ +FRESULT f_chmod (const XCHAR*, BYTE, BYTE); /* Change attriburte of the file/dir */ +FRESULT f_utime (const XCHAR*, const FILINFO*); /* Change timestamp of the file/dir */ +FRESULT f_rename (const XCHAR*, const XCHAR*); /* Rename/Move a file or directory */ +FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */ +FRESULT f_mkfs (BYTE, BYTE, WORD); /* Create a file system on the drive */ +FRESULT f_chdir (const XCHAR*); /* Change current directory */ +FRESULT f_chdrive (BYTE); /* Change current drive */ + +#if _USE_STRFUNC +int f_putc (int, FIL*); /* Put a character to the file */ +int f_puts (const char*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const char*, ...); /* Put a formatted string to the file */ +char* f_gets (char*, int, FIL*); /* Get a string from the file */ +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#ifndef EOF +#define EOF -1 +#endif +#endif + + + +/*--------------------------------------------------------------*/ +/* User defined functions */ + +/* Real time clock */ +#if !_FS_READONLY +DWORD get_fattime (void); /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */ + /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */ +#endif + +/* Unicode - OEM code conversion */ +#if _USE_LFN +WCHAR ff_convert (WCHAR, UINT); +WCHAR ff_wtoupper (WCHAR); +#endif + +/* Sync functions */ +#if _FS_REENTRANT +BOOL ff_cre_syncobj(BYTE, _SYNC_t*); +BOOL ff_del_syncobj(_SYNC_t); +BOOL ff_req_grant(_SYNC_t); +void ff_rel_grant(_SYNC_t); +#endif + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#if _FS_READONLY == 0 +#define FA_WRITE 0x02 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 +#endif +#define FA__ERROR 0x80 + + +/* FAT sub type (FATFS.fs_type) */ + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + + +/* File attribute bits for directory entry */ + +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* FatFs refers the members in the FAT structures with byte offset instead +/ of structure member because there are incompatibility of the packing option +/ between various compilers. */ + +#define BS_jmpBoot 0 +#define BS_OEMName 3 +#define BPB_BytsPerSec 11 +#define BPB_SecPerClus 13 +#define BPB_RsvdSecCnt 14 +#define BPB_NumFATs 16 +#define BPB_RootEntCnt 17 +#define BPB_TotSec16 19 +#define BPB_Media 21 +#define BPB_FATSz16 22 +#define BPB_SecPerTrk 24 +#define BPB_NumHeads 26 +#define BPB_HiddSec 28 +#define BPB_TotSec32 32 +#define BS_55AA 510 + +#define BS_DrvNum 36 +#define BS_BootSig 38 +#define BS_VolID 39 +#define BS_VolLab 43 +#define BS_FilSysType 54 + +#define BPB_FATSz32 36 +#define BPB_ExtFlags 40 +#define BPB_FSVer 42 +#define BPB_RootClus 44 +#define BPB_FSInfo 48 +#define BPB_BkBootSec 50 +#define BS_DrvNum32 64 +#define BS_BootSig32 66 +#define BS_VolID32 67 +#define BS_VolLab32 71 +#define BS_FilSysType32 82 + +#define FSI_LeadSig 0 +#define FSI_StrucSig 484 +#define FSI_Free_Count 488 +#define FSI_Nxt_Free 492 + +#define MBR_Table 446 + +#define DIR_Name 0 +#define DIR_Attr 11 +#define DIR_NTres 12 +#define DIR_CrtTime 14 +#define DIR_CrtDate 16 +#define DIR_FstClusHI 20 +#define DIR_WrtTime 22 +#define DIR_WrtDate 24 +#define DIR_FstClusLO 26 +#define DIR_FileSize 28 +#define LDIR_Ord 0 +#define LDIR_Attr 11 +#define LDIR_Type 12 +#define LDIR_Chksum 13 +#define LDIR_FstClusLO 26 + + + +/*--------------------------------*/ +/* Multi-byte word access macros */ + +#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) +#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) +#else /* Use byte-by-byte access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(((DWORD)*(BYTE*)((ptr)+3)<<24)|((DWORD)*(BYTE*)((ptr)+2)<<16)|((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8) +#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24) +#endif + + +#endif /* _FATFS */ diff --git a/Projects/Webserver/Lib/FATFs/ff.lst b/Projects/Webserver/Lib/FATFs/ff.lst new file mode 100644 index 0000000000..2a8f9fc4ed --- /dev/null +++ b/Projects/Webserver/Lib/FATFs/ff.lst @@ -0,0 +1,3062 @@ + 1 .file "ff.c" + 2 __SREG__ = 0x3f + 3 __SP_H__ = 0x3e + 4 __SP_L__ = 0x3d + 5 __CCP__ = 0x34 + 6 __tmp_reg__ = 0 + 7 __zero_reg__ = 1 + 15 .Ltext0: + 16 .section .text.clust2sect,"ax",@progbits + 17 .global clust2sect + 19 clust2sect: + 20 .LFB60: + 21 .LSM0: + 22 .LVL0: + 23 0000 EF92 push r14 + 24 0002 FF92 push r15 + 25 0004 0F93 push r16 + 26 0006 1F93 push r17 + 27 0008 CF93 push r28 + 28 000a DF93 push r29 + 29 /* prologue: function */ + 30 /* frame size = 0 */ + 31 000c EC01 movw r28,r24 + 32 000e 7A01 movw r14,r20 + 33 0010 8B01 movw r16,r22 + 34 .LSM1: + 35 0012 8EEF ldi r24,lo8(-2) + 36 0014 9FEF ldi r25,hi8(-2) + 37 0016 AFEF ldi r26,hlo8(-2) + 38 0018 BFEF ldi r27,hhi8(-2) + 39 .LVL1: + 40 001a E80E add r14,r24 + 41 001c F91E adc r15,r25 + 42 001e 0A1F adc r16,r26 + 43 0020 1B1F adc r17,r27 + 44 .LVL2: + 45 .LSM2: + 46 0022 8E85 ldd r24,Y+14 + 47 0024 9F85 ldd r25,Y+15 + 48 0026 A889 ldd r26,Y+16 + 49 0028 B989 ldd r27,Y+17 + 50 002a 0297 sbiw r24,2 + 51 002c A109 sbc r26,__zero_reg__ + 52 002e B109 sbc r27,__zero_reg__ + 53 0030 E816 cp r14,r24 + 54 0032 F906 cpc r15,r25 + 55 0034 0A07 cpc r16,r26 + 56 0036 1B07 cpc r17,r27 + 57 0038 00F0 brlo .L2 + 58 003a 20E0 ldi r18,lo8(0) + 59 003c 30E0 ldi r19,hi8(0) + 60 003e 40E0 ldi r20,hlo8(0) + 61 0040 50E0 ldi r21,hhi8(0) + 62 0042 00C0 rjmp .L3 + 63 .L2: + 64 .LSM3: + 65 0044 2A81 ldd r18,Y+2 + 66 0046 30E0 ldi r19,lo8(0) + 67 0048 40E0 ldi r20,lo8(0) + 68 004a 50E0 ldi r21,hi8(0) + 69 004c C801 movw r24,r16 + 70 004e B701 movw r22,r14 + 71 0050 0E94 0000 call __mulsi3 + 72 0054 9B01 movw r18,r22 + 73 0056 AC01 movw r20,r24 + 74 0058 8A8D ldd r24,Y+26 + 75 005a 9B8D ldd r25,Y+27 + 76 005c AC8D ldd r26,Y+28 + 77 005e BD8D ldd r27,Y+29 + 78 0060 280F add r18,r24 + 79 0062 391F adc r19,r25 + 80 0064 4A1F adc r20,r26 + 81 0066 5B1F adc r21,r27 + 82 .L3: + 83 .LSM4: + 84 0068 B901 movw r22,r18 + 85 006a CA01 movw r24,r20 + 86 /* epilogue start */ + 87 006c DF91 pop r29 + 88 006e CF91 pop r28 + 89 .LVL3: + 90 0070 1F91 pop r17 + 91 0072 0F91 pop r16 + 92 0074 FF90 pop r15 + 93 0076 EF90 pop r14 + 94 .LVL4: + 95 0078 0895 ret + 96 .LFE60: + 98 .section .text.f_mount,"ax",@progbits + 99 .global f_mount + 101 f_mount: + 102 .LFB69: + 103 .LSM5: + 104 .LVL5: + 105 /* prologue: function */ + 106 /* frame size = 0 */ + 107 0000 DB01 movw r26,r22 + 108 .LSM6: + 109 0002 8823 tst r24 + 110 0004 01F0 breq .L6 + 111 .LVL6: + 112 0006 8BE0 ldi r24,lo8(11) + 113 .LVL7: + 114 0008 0895 ret + 115 .LVL8: + 116 .L6: + 117 .LSM7: + 118 000a E091 0000 lds r30,FatFs + 119 000e F091 0000 lds r31,(FatFs)+1 + 120 .LVL9: + 121 .LSM8: + 122 0012 3097 sbiw r30,0 + 123 0014 01F0 breq .L8 + 124 .LSM9: + 125 0016 1082 st Z,__zero_reg__ + 126 .L8: + 127 .LSM10: + 128 0018 1097 sbiw r26,0 + 129 001a 01F0 breq .L9 + 130 .LSM11: + 131 001c 1C92 st X,__zero_reg__ + 132 .L9: + 133 .LSM12: + 134 001e B093 0000 sts (FatFs)+1,r27 + 135 0022 A093 0000 sts FatFs,r26 + 136 0026 80E0 ldi r24,lo8(0) + 137 .LVL10: + 138 .LSM13: + 139 0028 0895 ret + 140 .LFE69: + 142 .section .text.validate,"ax",@progbits + 144 validate: + 145 .LFB68: + 146 .LSM14: + 147 .LVL11: + 148 /* prologue: function */ + 149 /* frame size = 0 */ + 150 0000 FC01 movw r30,r24 + 151 .LSM15: + 152 0002 0097 sbiw r24,0 + 153 0004 01F0 breq .L12 + 154 0006 8081 ld r24,Z + 155 .LVL12: + 156 0008 8823 tst r24 + 157 000a 01F0 breq .L12 + 158 000c 8681 ldd r24,Z+6 + 159 000e 9781 ldd r25,Z+7 + 160 0010 8617 cp r24,r22 + 161 0012 9707 cpc r25,r23 + 162 0014 01F4 brne .L12 + 163 .LSM16: + 164 0016 8181 ldd r24,Z+1 + 165 0018 0E94 0000 call disk_status + 166 .LVL13: + 167 001c 80FD sbrc r24,0 + 168 001e 00C0 rjmp .L13 + 169 0020 80E0 ldi r24,lo8(0) + 170 0022 0895 ret + 171 .L13: + 172 0024 83E0 ldi r24,lo8(3) + 173 0026 0895 ret + 174 .LVL14: + 175 .L12: + 176 0028 89E0 ldi r24,lo8(9) + 177 .LSM17: + 178 002a 0895 ret + 179 .LFE68: + 181 .section .text.f_close,"ax",@progbits + 182 .global f_close + 184 f_close: + 185 .LFB72: + 186 .LSM18: + 187 .LVL15: + 188 0000 CF93 push r28 + 189 0002 DF93 push r29 + 190 /* prologue: function */ + 191 /* frame size = 0 */ + 192 0004 EC01 movw r28,r24 + 193 .LSM19: + 194 0006 6A81 ldd r22,Y+2 + 195 0008 7B81 ldd r23,Y+3 + 196 000a 8881 ld r24,Y + 197 000c 9981 ldd r25,Y+1 + 198 .LVL16: + 199 000e 0E94 0000 call validate + 200 .LSM20: + 201 0012 8823 tst r24 + 202 .LVL17: + 203 0014 01F4 brne .L17 + 204 0016 1982 std Y+1,__zero_reg__ + 205 0018 1882 st Y,__zero_reg__ + 206 .L17: + 207 .LVL18: + 208 /* epilogue start */ + 209 .LSM21: + 210 001a DF91 pop r29 + 211 001c CF91 pop r28 + 212 .LVL19: + 213 001e 0895 ret + 214 .LFE72: + 216 .section .text.move_window,"ax",@progbits + 218 move_window: + 219 .LFB58: + 220 .LSM22: + 221 .LVL20: + 222 0000 CF92 push r12 + 223 0002 DF92 push r13 + 224 0004 EF92 push r14 + 225 0006 FF92 push r15 + 226 0008 0F93 push r16 + 227 000a CF93 push r28 + 228 000c DF93 push r29 + 229 /* prologue: function */ + 230 /* frame size = 0 */ + 231 000e EC01 movw r28,r24 + 232 0010 6A01 movw r12,r20 + 233 0012 7B01 movw r14,r22 + 234 .LSM23: + 235 0014 8E8D ldd r24,Y+30 + 236 0016 9F8D ldd r25,Y+31 + 237 0018 A8A1 ldd r26,Y+32 + 238 001a B9A1 ldd r27,Y+33 + 239 .LVL21: + 240 001c 8417 cp r24,r20 + 241 001e 9507 cpc r25,r21 + 242 0020 A607 cpc r26,r22 + 243 0022 B707 cpc r27,r23 + 244 0024 01F0 breq .L20 + 245 .LSM24: + 246 0026 4115 cp r20,__zero_reg__ + 247 0028 5105 cpc r21,__zero_reg__ + 248 002a 6105 cpc r22,__zero_reg__ + 249 002c 7105 cpc r23,__zero_reg__ + 250 002e 01F0 breq .L20 + 251 .LSM25: + 252 0030 BE01 movw r22,r28 + 253 0032 6E5D subi r22,lo8(-(34)) + 254 0034 7F4F sbci r23,hi8(-(34)) + 255 0036 8981 ldd r24,Y+1 + 256 0038 A701 movw r20,r14 + 257 003a 9601 movw r18,r12 + 258 003c 01E0 ldi r16,lo8(1) + 259 003e 0E94 0000 call disk_read + 260 .LVL22: + 261 0042 8823 tst r24 + 262 0044 01F0 breq .L21 + 263 0046 81E0 ldi r24,lo8(1) + 264 0048 00C0 rjmp .L22 + 265 .L21: + 266 .LSM26: + 267 004a CE8E std Y+30,r12 + 268 004c DF8E std Y+31,r13 + 269 004e E8A2 std Y+32,r14 + 270 0050 F9A2 std Y+33,r15 + 271 .LVL23: + 272 .L20: + 273 0052 80E0 ldi r24,lo8(0) + 274 .L22: + 275 /* epilogue start */ + 276 .LSM27: + 277 0054 DF91 pop r29 + 278 0056 CF91 pop r28 + 279 .LVL24: + 280 0058 0F91 pop r16 + 281 005a FF90 pop r15 + 282 005c EF90 pop r14 + 283 005e DF90 pop r13 + 284 0060 CF90 pop r12 + 285 .LVL25: + 286 0062 0895 ret + 287 .LFE58: + 289 .section .text.get_fat,"ax",@progbits + 290 .global get_fat + 292 get_fat: + 293 .LFB59: + 294 .LSM28: + 295 .LVL26: + 296 0000 7F92 push r7 + 297 0002 8F92 push r8 + 298 0004 9F92 push r9 + 299 0006 AF92 push r10 + 300 0008 BF92 push r11 + 301 000a CF92 push r12 + 302 000c DF92 push r13 + 303 000e EF92 push r14 + 304 0010 FF92 push r15 + 305 0012 0F93 push r16 + 306 0014 1F93 push r17 + 307 0016 CF93 push r28 + 308 0018 DF93 push r29 + 309 /* prologue: function */ + 310 /* frame size = 0 */ + 311 001a 4C01 movw r8,r24 + 312 001c 7A01 movw r14,r20 + 313 001e 8B01 movw r16,r22 + 314 .LSM29: + 315 0020 4230 cpi r20,lo8(2) + 316 0022 5105 cpc r21,__zero_reg__ + 317 0024 6105 cpc r22,__zero_reg__ + 318 0026 7105 cpc r23,__zero_reg__ + 319 .LVL27: + 320 0028 00F4 brsh .+2 + 321 002a 00C0 rjmp .L25 + 322 002c F401 movw r30,r8 + 323 002e 8685 ldd r24,Z+14 + 324 0030 9785 ldd r25,Z+15 + 325 0032 A089 ldd r26,Z+16 + 326 0034 B189 ldd r27,Z+17 + 327 0036 4817 cp r20,r24 + 328 0038 5907 cpc r21,r25 + 329 003a 6A07 cpc r22,r26 + 330 003c 7B07 cpc r23,r27 + 331 003e 00F0 brlo .+2 + 332 0040 00C0 rjmp .L25 + 333 .LSM30: + 334 0042 A288 ldd r10,Z+18 + 335 0044 B388 ldd r11,Z+19 + 336 0046 C488 ldd r12,Z+20 + 337 0048 D588 ldd r13,Z+21 + 338 .LVL28: + 339 .LSM31: + 340 004a 8081 ld r24,Z + 341 004c 8230 cpi r24,lo8(2) + 342 004e 01F4 brne .+2 + 343 0050 00C0 rjmp .L28 + 344 0052 8330 cpi r24,lo8(3) + 345 0054 01F4 brne .+2 + 346 0056 00C0 rjmp .L29 + 347 0058 8130 cpi r24,lo8(1) + 348 005a 01F0 breq .+2 + 349 005c 00C0 rjmp .L26 + 350 .LSM32: + 351 005e EA01 movw r28,r20 + 352 .LVL29: + 353 0060 D695 lsr r29 + 354 0062 C795 ror r28 + 355 .LVL30: + 356 0064 C40F add r28,r20 + 357 0066 D51F adc r29,r21 + 358 .LSM33: + 359 0068 AE01 movw r20,r28 + 360 .LVL31: + 361 006a 452F mov r20,r21 + 362 006c 5527 clr r21 + 363 006e 4695 lsr r20 + 364 0070 60E0 ldi r22,lo8(0) + 365 0072 70E0 ldi r23,hi8(0) + 366 0074 4A0D add r20,r10 + 367 0076 5B1D adc r21,r11 + 368 0078 6C1D adc r22,r12 + 369 007a 7D1D adc r23,r13 + 370 007c C401 movw r24,r8 + 371 007e 0E94 0000 call move_window + 372 0082 8823 tst r24 + 373 0084 01F0 breq .+2 + 374 0086 00C0 rjmp .L26 + 375 .LSM34: + 376 0088 FE01 movw r30,r28 + 377 008a F170 andi r31,hi8(511) + 378 008c E80D add r30,r8 + 379 008e F91D adc r31,r9 + 380 0090 72A0 ldd r7,Z+34 + 381 0092 2196 adiw r28,1 + 382 .LVL32: + 383 .LSM35: + 384 0094 AE01 movw r20,r28 + 385 0096 452F mov r20,r21 + 386 0098 5527 clr r21 + 387 009a 4695 lsr r20 + 388 009c 60E0 ldi r22,lo8(0) + 389 009e 70E0 ldi r23,hi8(0) + 390 00a0 4A0D add r20,r10 + 391 00a2 5B1D adc r21,r11 + 392 00a4 6C1D adc r22,r12 + 393 00a6 7D1D adc r23,r13 + 394 00a8 C401 movw r24,r8 + 395 00aa 0E94 0000 call move_window + 396 00ae 8823 tst r24 + 397 00b0 01F0 breq .+2 + 398 00b2 00C0 rjmp .L26 + 399 .LSM36: + 400 00b4 672D mov r22,r7 + 401 .LVL33: + 402 00b6 70E0 ldi r23,lo8(0) + 403 .LVL34: + 404 .LSM37: + 405 00b8 D170 andi r29,hi8(511) + 406 00ba C80D add r28,r8 + 407 00bc D91D adc r29,r9 + 408 00be 9AA1 ldd r25,Y+34 + 409 00c0 80E0 ldi r24,lo8(0) + 410 00c2 682B or r22,r24 + 411 00c4 792B or r23,r25 + 412 .LVL35: + 413 .LSM38: + 414 00c6 C701 movw r24,r14 + 415 .LVL36: + 416 00c8 8170 andi r24,lo8(1) + 417 00ca 9070 andi r25,hi8(1) + 418 00cc 892B or r24,r25 + 419 00ce 01F0 breq .L30 + 420 00d0 44E0 ldi r20,4 + 421 00d2 7695 1: lsr r23 + 422 00d4 6795 ror r22 + 423 00d6 4A95 dec r20 + 424 00d8 01F4 brne 1b + 425 00da 9B01 movw r18,r22 + 426 00dc 00C0 rjmp .L33 + 427 .L30: + 428 00de 9B01 movw r18,r22 + 429 00e0 40E0 ldi r20,lo8(0) + 430 00e2 50E0 ldi r21,hi8(0) + 431 00e4 3F70 andi r19,hi8(4095) + 432 00e6 4070 andi r20,hlo8(4095) + 433 00e8 5070 andi r21,hhi8(4095) + 434 00ea 00C0 rjmp .L31 + 435 .LVL37: + 436 .L28: + 437 .LSM39: + 438 00ec 452F mov r20,r21 + 439 00ee 562F mov r21,r22 + 440 00f0 672F mov r22,r23 + 441 00f2 7727 clr r23 + 442 .LVL38: + 443 00f4 4A0D add r20,r10 + 444 00f6 5B1D adc r21,r11 + 445 00f8 6C1D adc r22,r12 + 446 00fa 7D1D adc r23,r13 + 447 00fc C401 movw r24,r8 + 448 00fe 0E94 0000 call move_window + 449 0102 8823 tst r24 + 450 0104 01F4 brne .L26 + 451 .LSM40: + 452 0106 F701 movw r30,r14 + 453 .LVL39: + 454 0108 EE0F lsl r30 + 455 010a FF1F rol r31 + 456 010c F170 andi r31,hi8(511) + 457 010e E80D add r30,r8 + 458 0110 F91D adc r31,r9 + 459 0112 82A1 ldd r24,Z+34 + 460 0114 93A1 ldd r25,Z+35 + 461 0116 9C01 movw r18,r24 + 462 .LVL40: + 463 .L33: + 464 0118 40E0 ldi r20,lo8(0) + 465 011a 50E0 ldi r21,hi8(0) + 466 011c 00C0 rjmp .L31 + 467 .LVL41: + 468 .L29: + 469 .LSM41: + 470 011e 97E0 ldi r25,7 + 471 0120 7695 1: lsr r23 + 472 0122 6795 ror r22 + 473 0124 5795 ror r21 + 474 0126 4795 ror r20 + 475 0128 9A95 dec r25 + 476 012a 01F4 brne 1b + 477 .LVL42: + 478 012c 4A0D add r20,r10 + 479 012e 5B1D adc r21,r11 + 480 0130 6C1D adc r22,r12 + 481 0132 7D1D adc r23,r13 + 482 0134 C401 movw r24,r8 + 483 0136 0E94 0000 call move_window + 484 013a 8823 tst r24 + 485 013c 01F4 brne .L26 + 486 .LSM42: + 487 013e F701 movw r30,r14 + 488 .LVL43: + 489 0140 EE0F lsl r30 + 490 0142 FF1F rol r31 + 491 0144 EE0F lsl r30 + 492 0146 FF1F rol r31 + 493 0148 F170 andi r31,hi8(511) + 494 014a E80D add r30,r8 + 495 014c F91D adc r31,r9 + 496 014e 22A1 ldd r18,Z+34 + 497 0150 33A1 ldd r19,Z+35 + 498 0152 44A1 ldd r20,Z+36 + 499 0154 55A1 ldd r21,Z+37 + 500 0156 5F70 andi r21,hhi8(268435455) + 501 0158 00C0 rjmp .L31 + 502 .LVL44: + 503 .L25: + 504 015a 21E0 ldi r18,lo8(1) + 505 015c 30E0 ldi r19,hi8(1) + 506 015e 40E0 ldi r20,hlo8(1) + 507 0160 50E0 ldi r21,hhi8(1) + 508 0162 00C0 rjmp .L31 + 509 .LVL45: + 510 .L26: + 511 0164 2FEF ldi r18,lo8(-1) + 512 0166 3FEF ldi r19,hi8(-1) + 513 0168 4FEF ldi r20,hlo8(-1) + 514 016a 5FEF ldi r21,hhi8(-1) + 515 .LVL46: + 516 .L31: + 517 .LSM43: + 518 016c B901 movw r22,r18 + 519 .LVL47: + 520 016e CA01 movw r24,r20 + 521 /* epilogue start */ + 522 0170 DF91 pop r29 + 523 0172 CF91 pop r28 + 524 .LVL48: + 525 0174 1F91 pop r17 + 526 0176 0F91 pop r16 + 527 0178 FF90 pop r15 + 528 017a EF90 pop r14 + 529 .LVL49: + 530 017c DF90 pop r13 + 531 017e CF90 pop r12 + 532 0180 BF90 pop r11 + 533 0182 AF90 pop r10 + 534 .LVL50: + 535 0184 9F90 pop r9 + 536 0186 8F90 pop r8 + 537 .LVL51: + 538 0188 7F90 pop r7 + 539 018a 0895 ret + 540 .LFE59: + 542 .section .text.f_lseek,"ax",@progbits + 543 .global f_lseek + 545 f_lseek: + 546 .LFB73: + 547 .LSM44: + 548 .LVL52: + 549 0000 2F92 push r2 + 550 0002 3F92 push r3 + 551 0004 4F92 push r4 + 552 0006 5F92 push r5 + 553 0008 6F92 push r6 + 554 000a 7F92 push r7 + 555 000c 8F92 push r8 + 556 000e 9F92 push r9 + 557 0010 AF92 push r10 + 558 0012 BF92 push r11 + 559 0014 CF92 push r12 + 560 0016 DF92 push r13 + 561 0018 EF92 push r14 + 562 001a FF92 push r15 + 563 001c 0F93 push r16 + 564 001e 1F93 push r17 + 565 0020 DF93 push r29 + 566 0022 CF93 push r28 + 567 0024 00D0 rcall . + 568 0026 0F92 push __tmp_reg__ + 569 0028 CDB7 in r28,__SP_L__ + 570 002a DEB7 in r29,__SP_H__ + 571 /* prologue: function */ + 572 /* frame size = 3 */ + 573 002c 9B83 std Y+3,r25 + 574 002e 8A83 std Y+2,r24 + 575 0030 E42E mov r14,r20 + 576 0032 D52E mov r13,r21 + 577 .LVL53: + 578 0034 C62E mov r12,r22 + 579 .LVL54: + 580 0036 B72E mov r11,r23 + 581 .LVL55: + 582 .LSM45: + 583 0038 DC01 movw r26,r24 + 584 003a 1296 adiw r26,2 + 585 003c 6D91 ld r22,X+ + 586 003e 7C91 ld r23,X + 587 0040 1397 sbiw r26,2+1 + 588 .LVL56: + 589 0042 8D91 ld r24,X+ + 590 0044 9C91 ld r25,X + 591 .LVL57: + 592 0046 0E94 0000 call validate + 593 .LVL58: + 594 004a 8983 std Y+1,r24 + 595 .LVL59: + 596 .LSM46: + 597 004c 8823 tst r24 + 598 004e 01F0 breq .+2 + 599 0050 00C0 rjmp .L35 + 600 .LSM47: + 601 0052 EA81 ldd r30,Y+2 + 602 0054 FB81 ldd r31,Y+3 + 603 0056 8481 ldd r24,Z+4 + 604 0058 87FD sbrc r24,7 + 605 005a 00C0 rjmp .L51 + 606 .L36: + 607 005c AA81 ldd r26,Y+2 + 608 005e BB81 ldd r27,Y+3 + 609 0060 1A96 adiw r26,10 + 610 0062 6D90 ld r6,X+ + 611 0064 7D90 ld r7,X+ + 612 0066 8D90 ld r8,X+ + 613 0068 9C90 ld r9,X + 614 006a 1D97 sbiw r26,10+3 + 615 006c 2E2D mov r18,r14 + 616 006e 3D2D mov r19,r13 + 617 0070 4C2D mov r20,r12 + 618 0072 5B2D mov r21,r11 + 619 0074 C901 movw r24,r18 + 620 0076 DA01 movw r26,r20 + 621 0078 5C01 movw r10,r24 + 622 007a 6D01 movw r12,r26 + 623 007c 6A14 cp r6,r10 + 624 007e 7B04 cpc r7,r11 + 625 0080 8C04 cpc r8,r12 + 626 0082 9D04 cpc r9,r13 + 627 0084 00F4 brsh .L37 + 628 .LVL60: + 629 0086 6401 movw r12,r8 + 630 0088 5301 movw r10,r6 + 631 .LVL61: + 632 .L37: + 633 .LSM48: + 634 008a EA81 ldd r30,Y+2 + 635 008c FB81 ldd r31,Y+3 + 636 008e 2681 ldd r18,Z+6 + 637 0090 3781 ldd r19,Z+7 + 638 0092 4085 ldd r20,Z+8 + 639 0094 5185 ldd r21,Z+9 + 640 .LVL62: + 641 .LSM49: + 642 0096 1682 std Z+6,__zero_reg__ + 643 0098 1782 std Z+7,__zero_reg__ + 644 009a 1086 std Z+8,__zero_reg__ + 645 009c 1186 std Z+9,__zero_reg__ + 646 009e 8FEF ldi r24,lo8(-1) + 647 00a0 8583 std Z+5,r24 + 648 .LSM50: + 649 00a2 A114 cp r10,__zero_reg__ + 650 00a4 B104 cpc r11,__zero_reg__ + 651 00a6 C104 cpc r12,__zero_reg__ + 652 00a8 D104 cpc r13,__zero_reg__ + 653 00aa 01F4 brne .+2 + 654 00ac 00C0 rjmp .L38 + 655 .LSM51: + 656 00ae 0190 ld __tmp_reg__,Z+ + 657 00b0 F081 ld r31,Z + 658 00b2 E02D mov r30,__tmp_reg__ + 659 00b4 8281 ldd r24,Z+2 + 660 00b6 682E mov r6,r24 + 661 .LVL63: + 662 00b8 7724 clr r7 + 663 .LVL64: + 664 00ba 8824 clr r8 + 665 00bc 9924 clr r9 + 666 .LVL65: + 667 00be 09E0 ldi r16,9 + 668 00c0 660C 1: lsl r6 + 669 00c2 771C rol r7 + 670 00c4 881C rol r8 + 671 00c6 991C rol r9 + 672 00c8 0A95 dec r16 + 673 00ca 01F4 brne 1b + 674 .LVL66: + 675 .LSM52: + 676 00cc 2115 cp r18,__zero_reg__ + 677 00ce 3105 cpc r19,__zero_reg__ + 678 00d0 4105 cpc r20,__zero_reg__ + 679 00d2 5105 cpc r21,__zero_reg__ + 680 00d4 01F0 breq .L39 + 681 00d6 1901 movw r2,r18 + 682 00d8 2A01 movw r4,r20 + 683 00da 0894 sec + 684 00dc 2108 sbc r2,__zero_reg__ + 685 00de 3108 sbc r3,__zero_reg__ + 686 00e0 4108 sbc r4,__zero_reg__ + 687 00e2 5108 sbc r5,__zero_reg__ + 688 00e4 C601 movw r24,r12 + 689 00e6 B501 movw r22,r10 + 690 00e8 6150 subi r22,lo8(-(-1)) + 691 00ea 7040 sbci r23,hi8(-(-1)) + 692 00ec 8040 sbci r24,hlo8(-(-1)) + 693 00ee 9040 sbci r25,hhi8(-(-1)) + 694 00f0 A401 movw r20,r8 + 695 00f2 9301 movw r18,r6 + 696 .LVL67: + 697 00f4 0E94 0000 call __udivmodsi4 + 698 00f8 7901 movw r14,r18 + 699 00fa 8A01 movw r16,r20 + 700 00fc C201 movw r24,r4 + 701 00fe B101 movw r22,r2 + 702 0100 A401 movw r20,r8 + 703 0102 9301 movw r18,r6 + 704 0104 0E94 0000 call __udivmodsi4 + 705 0108 E216 cp r14,r18 + 706 010a F306 cpc r15,r19 + 707 010c 0407 cpc r16,r20 + 708 010e 1507 cpc r17,r21 + 709 0110 00F0 brlo .L39 + 710 .LSM53: + 711 0112 8827 clr r24 + 712 0114 9927 clr r25 + 713 0116 DC01 movw r26,r24 + 714 0118 8619 sub r24,r6 + 715 011a 9709 sbc r25,r7 + 716 011c A809 sbc r26,r8 + 717 011e B909 sbc r27,r9 + 718 0120 8221 and r24,r2 + 719 0122 9321 and r25,r3 + 720 0124 A421 and r26,r4 + 721 0126 B521 and r27,r5 + 722 0128 EA81 ldd r30,Y+2 + 723 012a FB81 ldd r31,Y+3 + 724 012c 8683 std Z+6,r24 + 725 012e 9783 std Z+7,r25 + 726 0130 A087 std Z+8,r26 + 727 0132 B187 std Z+9,r27 + 728 .LSM54: + 729 0134 A81A sub r10,r24 + 730 0136 B90A sbc r11,r25 + 731 0138 CA0A sbc r12,r26 + 732 013a DB0A sbc r13,r27 + 733 .LSM55: + 734 013c 4289 ldd r20,Z+18 + 735 013e 5389 ldd r21,Z+19 + 736 0140 6489 ldd r22,Z+20 + 737 0142 7589 ldd r23,Z+21 + 738 .LVL68: + 739 0144 00C0 rjmp .L40 + 740 .LVL69: + 741 .L39: + 742 .LSM56: + 743 0146 AA81 ldd r26,Y+2 + 744 0148 BB81 ldd r27,Y+3 + 745 014a 1E96 adiw r26,14 + 746 014c 4D91 ld r20,X+ + 747 014e 5D91 ld r21,X+ + 748 0150 6D91 ld r22,X+ + 749 0152 7C91 ld r23,X + 750 0154 5197 sbiw r26,14+3 + 751 .LVL70: + 752 .LSM57: + 753 0156 FD01 movw r30,r26 + 754 0158 428B std Z+18,r20 + 755 015a 538B std Z+19,r21 + 756 015c 648B std Z+20,r22 + 757 015e 758B std Z+21,r23 + 758 .L40: + 759 .LSM58: + 760 0160 4115 cp r20,__zero_reg__ + 761 0162 5105 cpc r21,__zero_reg__ + 762 0164 6105 cpc r22,__zero_reg__ + 763 0166 7105 cpc r23,__zero_reg__ + 764 0168 01F0 breq .+2 + 765 016a 00C0 rjmp .L49 + 766 016c 00C0 rjmp .L38 + 767 .L45: + 768 .LSM59: + 769 016e AA81 ldd r26,Y+2 + 770 0170 BB81 ldd r27,Y+3 + 771 0172 8D91 ld r24,X+ + 772 0174 9C91 ld r25,X + 773 0176 0E94 0000 call get_fat + 774 .LVL71: + 775 017a AB01 movw r20,r22 + 776 017c BC01 movw r22,r24 + 777 .LVL72: + 778 .LSM60: + 779 017e 4F3F cpi r20,lo8(-1) + 780 0180 BFEF ldi r27,hi8(-1) + 781 0182 5B07 cpc r21,r27 + 782 0184 BFEF ldi r27,hlo8(-1) + 783 0186 6B07 cpc r22,r27 + 784 0188 BFEF ldi r27,hhi8(-1) + 785 018a 7B07 cpc r23,r27 + 786 018c 01F4 brne .L42 + 787 018e EA81 ldd r30,Y+2 + 788 0190 FB81 ldd r31,Y+3 + 789 0192 8481 ldd r24,Z+4 + 790 0194 8068 ori r24,lo8(-128) + 791 0196 8483 std Z+4,r24 + 792 0198 F1E0 ldi r31,lo8(1) + 793 .L50: + 794 019a F983 std Y+1,r31 + 795 .LVL73: + 796 019c 00C0 rjmp .L35 + 797 .L42: + 798 .LSM61: + 799 019e 4230 cpi r20,lo8(2) + 800 01a0 5105 cpc r21,__zero_reg__ + 801 01a2 6105 cpc r22,__zero_reg__ + 802 01a4 7105 cpc r23,__zero_reg__ + 803 01a6 00F0 brlo .L43 + 804 01a8 AA81 ldd r26,Y+2 + 805 01aa BB81 ldd r27,Y+3 + 806 01ac ED91 ld r30,X+ + 807 01ae FC91 ld r31,X + 808 01b0 8685 ldd r24,Z+14 + 809 01b2 9785 ldd r25,Z+15 + 810 01b4 A089 ldd r26,Z+16 + 811 01b6 B189 ldd r27,Z+17 + 812 01b8 4817 cp r20,r24 + 813 01ba 5907 cpc r21,r25 + 814 01bc 6A07 cpc r22,r26 + 815 01be 7B07 cpc r23,r27 + 816 01c0 00F0 brlo .L44 + 817 .L43: + 818 01c2 EA81 ldd r30,Y+2 + 819 01c4 FB81 ldd r31,Y+3 + 820 01c6 8481 ldd r24,Z+4 + 821 01c8 8068 ori r24,lo8(-128) + 822 01ca 8483 std Z+4,r24 + 823 .LVL74: + 824 .L51: + 825 01cc F2E0 ldi r31,lo8(2) + 826 01ce 00C0 rjmp .L50 + 827 .LVL75: + 828 .L44: + 829 .LSM62: + 830 01d0 AA81 ldd r26,Y+2 + 831 01d2 BB81 ldd r27,Y+3 + 832 01d4 5296 adiw r26,18 + 833 01d6 4D93 st X+,r20 + 834 01d8 5D93 st X+,r21 + 835 01da 6D93 st X+,r22 + 836 01dc 7C93 st X,r23 + 837 01de 5597 sbiw r26,18+3 + 838 .LSM63: + 839 01e0 FD01 movw r30,r26 + 840 01e2 8681 ldd r24,Z+6 + 841 01e4 9781 ldd r25,Z+7 + 842 01e6 A085 ldd r26,Z+8 + 843 01e8 B185 ldd r27,Z+9 + 844 01ea 860D add r24,r6 + 845 01ec 971D adc r25,r7 + 846 01ee A81D adc r26,r8 + 847 01f0 B91D adc r27,r9 + 848 01f2 8683 std Z+6,r24 + 849 01f4 9783 std Z+7,r25 + 850 01f6 A087 std Z+8,r26 + 851 01f8 B187 std Z+9,r27 + 852 .LSM64: + 853 01fa A618 sub r10,r6 + 854 01fc B708 sbc r11,r7 + 855 01fe C808 sbc r12,r8 + 856 0200 D908 sbc r13,r9 + 857 .LVL76: + 858 .L49: + 859 .LSM65: + 860 0202 6A14 cp r6,r10 + 861 0204 7B04 cpc r7,r11 + 862 0206 8C04 cpc r8,r12 + 863 0208 9D04 cpc r9,r13 + 864 020a 00F4 brsh .+2 + 865 020c 00C0 rjmp .L45 + 866 .LVL77: + 867 .LSM66: + 868 020e EA81 ldd r30,Y+2 + 869 0210 FB81 ldd r31,Y+3 + 870 0212 8681 ldd r24,Z+6 + 871 0214 9781 ldd r25,Z+7 + 872 0216 A085 ldd r26,Z+8 + 873 0218 B185 ldd r27,Z+9 + 874 021a 8A0D add r24,r10 + 875 021c 9B1D adc r25,r11 + 876 021e AC1D adc r26,r12 + 877 0220 BD1D adc r27,r13 + 878 0222 8683 std Z+6,r24 + 879 0224 9783 std Z+7,r25 + 880 0226 A087 std Z+8,r26 + 881 0228 B187 std Z+9,r27 + 882 .LSM67: + 883 022a D601 movw r26,r12 + 884 022c C501 movw r24,r10 + 885 022e 19E0 ldi r17,9 + 886 0230 B695 1: lsr r27 + 887 0232 A795 ror r26 + 888 0234 9795 ror r25 + 889 0236 8795 ror r24 + 890 0238 1A95 dec r17 + 891 023a 01F4 brne 1b + 892 023c E82E mov r14,r24 + 893 023e 8583 std Z+5,r24 + 894 .LSM68: + 895 0240 8FEF ldi r24,lo8(511) + 896 0242 91E0 ldi r25,hi8(511) + 897 0244 A0E0 ldi r26,hlo8(511) + 898 0246 B0E0 ldi r27,hhi8(511) + 899 0248 A822 and r10,r24 + 900 024a B922 and r11,r25 + 901 024c CA22 and r12,r26 + 902 024e DB22 and r13,r27 + 903 0250 A114 cp r10,__zero_reg__ + 904 0252 B104 cpc r11,__zero_reg__ + 905 0254 C104 cpc r12,__zero_reg__ + 906 0256 D104 cpc r13,__zero_reg__ + 907 0258 01F0 breq .L38 + 908 .LSM69: + 909 025a 8081 ld r24,Z + 910 025c 9181 ldd r25,Z+1 + 911 025e 0E94 0000 call clust2sect + 912 .LVL78: + 913 .LSM70: + 914 0262 6115 cp r22,__zero_reg__ + 915 0264 7105 cpc r23,__zero_reg__ + 916 0266 8105 cpc r24,__zero_reg__ + 917 0268 9105 cpc r25,__zero_reg__ + 918 .LVL79: + 919 026a 01F4 brne .L46 + 920 026c AA81 ldd r26,Y+2 + 921 026e BB81 ldd r27,Y+3 + 922 0270 1496 adiw r26,4 + 923 0272 8C91 ld r24,X + 924 0274 1497 sbiw r26,4 + 925 0276 8068 ori r24,lo8(-128) + 926 0278 1496 adiw r26,4 + 927 027a 8C93 st X,r24 + 928 027c B2E0 ldi r27,lo8(2) + 929 027e B983 std Y+1,r27 + 930 .LVL80: + 931 0280 00C0 rjmp .L35 + 932 .L46: + 933 .LSM71: + 934 0282 9B01 movw r18,r22 + 935 0284 AC01 movw r20,r24 + 936 0286 2E0D add r18,r14 + 937 0288 311D adc r19,__zero_reg__ + 938 028a 411D adc r20,__zero_reg__ + 939 028c 511D adc r21,__zero_reg__ + 940 .LVL81: + 941 .LSM72: + 942 028e E394 inc r14 + 943 0290 EA81 ldd r30,Y+2 + 944 0292 FB81 ldd r31,Y+3 + 945 0294 E582 std Z+5,r14 + 946 0296 00C0 rjmp .L47 + 947 .LVL82: + 948 .L38: + 949 0298 20E0 ldi r18,lo8(0) + 950 029a 30E0 ldi r19,hi8(0) + 951 029c 40E0 ldi r20,hlo8(0) + 952 029e 50E0 ldi r21,hhi8(0) + 953 .LVL83: + 954 .L47: + 955 .LSM73: + 956 02a0 EA81 ldd r30,Y+2 + 957 02a2 FB81 ldd r31,Y+3 + 958 02a4 8681 ldd r24,Z+6 + 959 02a6 9781 ldd r25,Z+7 + 960 02a8 A085 ldd r26,Z+8 + 961 02aa B185 ldd r27,Z+9 + 962 02ac 9170 andi r25,hi8(511) + 963 02ae A070 andi r26,hlo8(511) + 964 02b0 B070 andi r27,hhi8(511) + 965 02b2 0097 sbiw r24,0 + 966 02b4 A105 cpc r26,__zero_reg__ + 967 02b6 B105 cpc r27,__zero_reg__ + 968 02b8 01F0 breq .L35 + 969 02ba 8689 ldd r24,Z+22 + 970 02bc 9789 ldd r25,Z+23 + 971 02be A08D ldd r26,Z+24 + 972 02c0 B18D ldd r27,Z+25 + 973 02c2 2817 cp r18,r24 + 974 02c4 3907 cpc r19,r25 + 975 02c6 4A07 cpc r20,r26 + 976 02c8 5B07 cpc r21,r27 + 977 02ca 01F0 breq .L35 + 978 .LSM74: + 979 02cc 268B std Z+22,r18 + 980 02ce 378B std Z+23,r19 + 981 02d0 408F std Z+24,r20 + 982 02d2 518F std Z+25,r21 + 983 .LVL84: + 984 .L35: + 985 .LSM75: + 986 02d4 8981 ldd r24,Y+1 + 987 /* epilogue start */ + 988 02d6 0F90 pop __tmp_reg__ + 989 02d8 0F90 pop __tmp_reg__ + 990 02da 0F90 pop __tmp_reg__ + 991 02dc CF91 pop r28 + 992 02de DF91 pop r29 + 993 02e0 1F91 pop r17 + 994 02e2 0F91 pop r16 + 995 02e4 FF90 pop r15 + 996 02e6 EF90 pop r14 + 997 02e8 DF90 pop r13 + 998 .LVL85: + 999 02ea CF90 pop r12 + 1000 .LVL86: + 1001 02ec BF90 pop r11 + 1002 .LVL87: + 1003 02ee AF90 pop r10 + 1004 .LVL88: + 1005 02f0 9F90 pop r9 + 1006 02f2 8F90 pop r8 + 1007 .LVL89: + 1008 02f4 7F90 pop r7 + 1009 .LVL90: + 1010 02f6 6F90 pop r6 + 1011 .LVL91: + 1012 02f8 5F90 pop r5 + 1013 02fa 4F90 pop r4 + 1014 02fc 3F90 pop r3 + 1015 02fe 2F90 pop r2 + 1016 0300 0895 ret + 1017 .LFE73: + 1019 .section .text.dir_seek,"ax",@progbits + 1021 dir_seek: + 1022 .LFB61: + 1023 .LSM76: + 1024 .LVL92: + 1025 0000 EF92 push r14 + 1026 0002 FF92 push r15 + 1027 0004 0F93 push r16 + 1028 0006 1F93 push r17 + 1029 0008 CF93 push r28 + 1030 000a DF93 push r29 + 1031 /* prologue: function */ + 1032 /* frame size = 0 */ + 1033 000c 8C01 movw r16,r24 + 1034 000e EB01 movw r28,r22 + 1035 .LSM77: + 1036 0010 DC01 movw r26,r24 + 1037 0012 1596 adiw r26,4+1 + 1038 0014 7C93 st X,r23 + 1039 0016 6E93 st -X,r22 + 1040 0018 1497 sbiw r26,4 + 1041 .LSM78: + 1042 001a 1696 adiw r26,6 + 1043 001c 4D91 ld r20,X+ + 1044 001e 5D91 ld r21,X+ + 1045 0020 6D91 ld r22,X+ + 1046 0022 7C91 ld r23,X + 1047 0024 1997 sbiw r26,6+3 + 1048 .LVL93: + 1049 .LSM79: + 1050 0026 4130 cpi r20,lo8(1) + 1051 0028 5105 cpc r21,__zero_reg__ + 1052 002a 6105 cpc r22,__zero_reg__ + 1053 002c 7105 cpc r23,__zero_reg__ + 1054 002e 01F4 brne .+2 + 1055 0030 00C0 rjmp .L53 + 1056 0032 ED91 ld r30,X+ + 1057 0034 FC91 ld r31,X + 1058 0036 8685 ldd r24,Z+14 + 1059 0038 9785 ldd r25,Z+15 + 1060 003a A089 ldd r26,Z+16 + 1061 003c B189 ldd r27,Z+17 + 1062 .LVL94: + 1063 003e 4817 cp r20,r24 + 1064 0040 5907 cpc r21,r25 + 1065 0042 6A07 cpc r22,r26 + 1066 0044 7B07 cpc r23,r27 + 1067 0046 00F0 brlo .+2 + 1068 0048 00C0 rjmp .L53 + 1069 .LSM80: + 1070 004a 4115 cp r20,__zero_reg__ + 1071 004c 5105 cpc r21,__zero_reg__ + 1072 004e 6105 cpc r22,__zero_reg__ + 1073 0050 7105 cpc r23,__zero_reg__ + 1074 0052 01F4 brne .L54 + 1075 0054 8081 ld r24,Z + 1076 0056 8330 cpi r24,lo8(3) + 1077 0058 01F4 brne .L55 + 1078 .LSM81: + 1079 005a 4689 ldd r20,Z+22 + 1080 005c 5789 ldd r21,Z+23 + 1081 005e 608D ldd r22,Z+24 + 1082 0060 718D ldd r23,Z+25 + 1083 .LSM82: + 1084 0062 4115 cp r20,__zero_reg__ + 1085 0064 5105 cpc r21,__zero_reg__ + 1086 0066 6105 cpc r22,__zero_reg__ + 1087 0068 7105 cpc r23,__zero_reg__ + 1088 006a 01F4 brne .L54 + 1089 .L55: + 1090 .LSM83: + 1091 006c F801 movw r30,r16 + 1092 006e 1286 std Z+10,__zero_reg__ + 1093 0070 1386 std Z+11,__zero_reg__ + 1094 0072 1486 std Z+12,__zero_reg__ + 1095 0074 1586 std Z+13,__zero_reg__ + 1096 .LSM84: + 1097 0076 0190 ld __tmp_reg__,Z+ + 1098 0078 F081 ld r31,Z + 1099 007a E02D mov r30,__tmp_reg__ + 1100 007c 8085 ldd r24,Z+8 + 1101 007e 9185 ldd r25,Z+9 + 1102 0080 C817 cp r28,r24 + 1103 0082 D907 cpc r29,r25 + 1104 0084 00F0 brlo .+2 + 1105 0086 00C0 rjmp .L53 + 1106 .LVL95: + 1107 .LSM85: + 1108 0088 CE01 movw r24,r28 + 1109 008a A4E0 ldi r26,4 + 1110 008c 9695 1: lsr r25 + 1111 008e 8795 ror r24 + 1112 0090 AA95 dec r26 + 1113 0092 01F4 brne 1b + 1114 0094 A0E0 ldi r26,lo8(0) + 1115 0096 B0E0 ldi r27,hi8(0) + 1116 0098 2689 ldd r18,Z+22 + 1117 009a 3789 ldd r19,Z+23 + 1118 009c 408D ldd r20,Z+24 + 1119 009e 518D ldd r21,Z+25 + 1120 00a0 820F add r24,r18 + 1121 00a2 931F adc r25,r19 + 1122 00a4 A41F adc r26,r20 + 1123 00a6 B51F adc r27,r21 + 1124 00a8 F801 movw r30,r16 + 1125 00aa 8687 std Z+14,r24 + 1126 00ac 9787 std Z+15,r25 + 1127 00ae A08B std Z+16,r26 + 1128 00b0 B18B std Z+17,r27 + 1129 00b2 00C0 rjmp .L56 + 1130 .LVL96: + 1131 .L54: + 1132 .LSM86: + 1133 00b4 8281 ldd r24,Z+2 + 1134 00b6 E82E mov r14,r24 + 1135 .LVL97: + 1136 00b8 FF24 clr r15 + 1137 .LVL98: + 1138 00ba E4E0 ldi r30,4 + 1139 00bc EE0C 1: lsl r14 + 1140 00be FF1C rol r15 + 1141 00c0 EA95 dec r30 + 1142 00c2 01F4 brne 1b + 1143 .LVL99: + 1144 00c4 00C0 rjmp .L57 + 1145 .LVL100: + 1146 .L60: + 1147 .LSM87: + 1148 00c6 0E94 0000 call get_fat + 1149 .LVL101: + 1150 00ca AB01 movw r20,r22 + 1151 00cc BC01 movw r22,r24 + 1152 .LVL102: + 1153 .LSM88: + 1154 00ce 4F3F cpi r20,lo8(-1) + 1155 00d0 FFEF ldi r31,hi8(-1) + 1156 00d2 5F07 cpc r21,r31 + 1157 00d4 FFEF ldi r31,hlo8(-1) + 1158 00d6 6F07 cpc r22,r31 + 1159 00d8 FFEF ldi r31,hhi8(-1) + 1160 00da 7F07 cpc r23,r31 + 1161 00dc 01F4 brne .L58 + 1162 00de 81E0 ldi r24,lo8(1) + 1163 00e0 00C0 rjmp .L59 + 1164 .L58: + 1165 .LSM89: + 1166 00e2 4230 cpi r20,lo8(2) + 1167 00e4 5105 cpc r21,__zero_reg__ + 1168 00e6 6105 cpc r22,__zero_reg__ + 1169 00e8 7105 cpc r23,__zero_reg__ + 1170 00ea 00F4 brsh .+2 + 1171 00ec 00C0 rjmp .L53 + 1172 00ee D801 movw r26,r16 + 1173 00f0 ED91 ld r30,X+ + 1174 00f2 FC91 ld r31,X + 1175 00f4 8685 ldd r24,Z+14 + 1176 00f6 9785 ldd r25,Z+15 + 1177 00f8 A089 ldd r26,Z+16 + 1178 00fa B189 ldd r27,Z+17 + 1179 00fc 4817 cp r20,r24 + 1180 00fe 5907 cpc r21,r25 + 1181 0100 6A07 cpc r22,r26 + 1182 0102 7B07 cpc r23,r27 + 1183 0104 00F4 brsh .L53 + 1184 .LSM90: + 1185 0106 CE19 sub r28,r14 + 1186 0108 DF09 sbc r29,r15 + 1187 .LVL103: + 1188 .L57: + 1189 010a F801 movw r30,r16 + 1190 010c 8081 ld r24,Z + 1191 010e 9181 ldd r25,Z+1 + 1192 .LSM91: + 1193 0110 CE15 cp r28,r14 + 1194 0112 DF05 cpc r29,r15 + 1195 0114 00F4 brsh .L60 + 1196 .LSM92: + 1197 0116 D801 movw r26,r16 + 1198 0118 1A96 adiw r26,10 + 1199 011a 4D93 st X+,r20 + 1200 011c 5D93 st X+,r21 + 1201 011e 6D93 st X+,r22 + 1202 0120 7C93 st X,r23 + 1203 0122 1D97 sbiw r26,10+3 + 1204 .LSM93: + 1205 0124 0E94 0000 call clust2sect + 1206 .LVL104: + 1207 0128 9E01 movw r18,r28 + 1208 012a 44E0 ldi r20,4 + 1209 012c 3695 1: lsr r19 + 1210 012e 2795 ror r18 + 1211 0130 4A95 dec r20 + 1212 0132 01F4 brne 1b + 1213 0134 40E0 ldi r20,lo8(0) + 1214 0136 50E0 ldi r21,hi8(0) + 1215 0138 260F add r18,r22 + 1216 013a 371F adc r19,r23 + 1217 013c 481F adc r20,r24 + 1218 013e 591F adc r21,r25 + 1219 0140 F801 movw r30,r16 + 1220 0142 2687 std Z+14,r18 + 1221 0144 3787 std Z+15,r19 + 1222 0146 408B std Z+16,r20 + 1223 0148 518B std Z+17,r21 + 1224 .LVL105: + 1225 .L56: + 1226 .LSM94: + 1227 014a CF70 andi r28,lo8(15) + 1228 014c D070 andi r29,hi8(15) + 1229 014e 85E0 ldi r24,5 + 1230 0150 CC0F 1: lsl r28 + 1231 0152 DD1F rol r29 + 1232 0154 8A95 dec r24 + 1233 0156 01F4 brne 1b + 1234 0158 A296 adiw r28,34 + 1235 015a D801 movw r26,r16 + 1236 015c 8D91 ld r24,X+ + 1237 015e 9C91 ld r25,X + 1238 0160 1197 sbiw r26,1 + 1239 0162 8C0F add r24,r28 + 1240 0164 9D1F adc r25,r29 + 1241 0166 5396 adiw r26,18+1 + 1242 0168 9C93 st X,r25 + 1243 016a 8E93 st -X,r24 + 1244 016c 5297 sbiw r26,18 + 1245 016e 80E0 ldi r24,lo8(0) + 1246 0170 00C0 rjmp .L59 + 1247 .LVL106: + 1248 .L53: + 1249 .LSM95: + 1250 0172 82E0 ldi r24,lo8(2) + 1251 .L59: + 1252 /* epilogue start */ + 1253 .LSM96: + 1254 0174 DF91 pop r29 + 1255 0176 CF91 pop r28 + 1256 .LVL107: + 1257 0178 1F91 pop r17 + 1258 017a 0F91 pop r16 + 1259 .LVL108: + 1260 017c FF90 pop r15 + 1261 .LVL109: + 1262 017e EF90 pop r14 + 1263 .LVL110: + 1264 0180 0895 ret + 1265 .LFE61: + 1267 .section .text.f_read,"ax",@progbits + 1268 .global f_read + 1270 f_read: + 1271 .LFB71: + 1272 .LSM97: + 1273 .LVL111: + 1274 0000 2F92 push r2 + 1275 0002 3F92 push r3 + 1276 0004 4F92 push r4 + 1277 0006 5F92 push r5 + 1278 0008 7F92 push r7 + 1279 000a 8F92 push r8 + 1280 000c 9F92 push r9 + 1281 000e AF92 push r10 + 1282 0010 BF92 push r11 + 1283 0012 CF92 push r12 + 1284 0014 DF92 push r13 + 1285 0016 EF92 push r14 + 1286 0018 FF92 push r15 + 1287 001a 0F93 push r16 + 1288 001c 1F93 push r17 + 1289 001e CF93 push r28 + 1290 0020 DF93 push r29 + 1291 /* prologue: function */ + 1292 /* frame size = 0 */ + 1293 0022 EC01 movw r28,r24 + 1294 0024 162F mov r17,r22 + 1295 0026 072F mov r16,r23 + 1296 .LVL112: + 1297 0028 4A01 movw r8,r20 + 1298 002a 1901 movw r2,r18 + 1299 .LSM98: + 1300 002c D901 movw r26,r18 + 1301 002e 1D92 st X+,__zero_reg__ + 1302 0030 1C92 st X,__zero_reg__ + 1303 .LSM99: + 1304 0032 6A81 ldd r22,Y+2 + 1305 0034 7B81 ldd r23,Y+3 + 1306 .LVL113: + 1307 0036 8881 ld r24,Y + 1308 0038 9981 ldd r25,Y+1 + 1309 .LVL114: + 1310 003a 0E94 0000 call validate + 1311 .LVL115: + 1312 003e 782E mov r7,r24 + 1313 .LVL116: + 1314 .LSM100: + 1315 0040 8823 tst r24 + 1316 0042 01F0 breq .+2 + 1317 0044 00C0 rjmp .L63 + 1318 .LVL117: + 1319 .LSM101: + 1320 0046 8C81 ldd r24,Y+4 + 1321 .LVL118: + 1322 0048 87FD sbrc r24,7 + 1323 004a 00C0 rjmp .L85 + 1324 .L64: + 1325 .LSM102: + 1326 004c 80FD sbrc r24,0 + 1327 004e 00C0 rjmp .L65 + 1328 0050 27E0 ldi r18,lo8(7) + 1329 0052 722E mov r7,r18 + 1330 0054 00C0 rjmp .L63 + 1331 .L65: + 1332 .LSM103: + 1333 0056 2A85 ldd r18,Y+10 + 1334 0058 3B85 ldd r19,Y+11 + 1335 005a 4C85 ldd r20,Y+12 + 1336 005c 5D85 ldd r21,Y+13 + 1337 .LVL119: + 1338 005e 8E81 ldd r24,Y+6 + 1339 0060 9F81 ldd r25,Y+7 + 1340 0062 A885 ldd r26,Y+8 + 1341 0064 B985 ldd r27,Y+9 + 1342 0066 281B sub r18,r24 + 1343 0068 390B sbc r19,r25 + 1344 006a 4A0B sbc r20,r26 + 1345 006c 5B0B sbc r21,r27 + 1346 .LSM104: + 1347 006e C401 movw r24,r8 + 1348 0070 A0E0 ldi r26,lo8(0) + 1349 0072 B0E0 ldi r27,hi8(0) + 1350 .LVL120: + 1351 0074 2817 cp r18,r24 + 1352 0076 3907 cpc r19,r25 + 1353 0078 4A07 cpc r20,r26 + 1354 007a 5B07 cpc r21,r27 + 1355 007c 00F4 brsh .L66 + 1356 .LVL121: + 1357 007e 4901 movw r8,r18 + 1358 .LVL122: + 1359 .L66: + 1360 .LSM105: + 1361 0080 812F mov r24,r17 + 1362 .LVL123: + 1363 0082 902F mov r25,r16 + 1364 .LVL124: + 1365 0084 9C01 movw r18,r24 + 1366 .LVL125: + 1367 0086 2901 movw r4,r18 + 1368 .LVL126: + 1369 0088 00C0 rjmp .L67 + 1370 .LVL127: + 1371 .L83: + 1372 .LSM106: + 1373 008a 2E81 ldd r18,Y+6 + 1374 008c 3F81 ldd r19,Y+7 + 1375 008e 4885 ldd r20,Y+8 + 1376 0090 5985 ldd r21,Y+9 + 1377 0092 DA01 movw r26,r20 + 1378 0094 C901 movw r24,r18 + 1379 0096 9170 andi r25,hi8(511) + 1380 0098 A070 andi r26,hlo8(511) + 1381 009a B070 andi r27,hhi8(511) + 1382 009c 0097 sbiw r24,0 + 1383 009e A105 cpc r26,__zero_reg__ + 1384 00a0 B105 cpc r27,__zero_reg__ + 1385 00a2 01F0 breq .+2 + 1386 00a4 00C0 rjmp .L68 + 1387 .LSM107: + 1388 00a6 E881 ld r30,Y + 1389 00a8 F981 ldd r31,Y+1 + 1390 .LVL128: + 1391 00aa 9D81 ldd r25,Y+5 + 1392 .LVL129: + 1393 00ac 8281 ldd r24,Z+2 + 1394 00ae 9817 cp r25,r24 + 1395 00b0 00F0 brlo .L69 + 1396 .LSM108: + 1397 00b2 2115 cp r18,__zero_reg__ + 1398 00b4 3105 cpc r19,__zero_reg__ + 1399 00b6 4105 cpc r20,__zero_reg__ + 1400 00b8 5105 cpc r21,__zero_reg__ + 1401 00ba 01F4 brne .L70 + 1402 00bc 6E85 ldd r22,Y+14 + 1403 00be 7F85 ldd r23,Y+15 + 1404 00c0 8889 ldd r24,Y+16 + 1405 00c2 9989 ldd r25,Y+17 + 1406 .LVL130: + 1407 00c4 00C0 rjmp .L71 + 1408 .LVL131: + 1409 .L70: + 1410 00c6 4A89 ldd r20,Y+18 + 1411 00c8 5B89 ldd r21,Y+19 + 1412 00ca 6C89 ldd r22,Y+20 + 1413 00cc 7D89 ldd r23,Y+21 + 1414 00ce CF01 movw r24,r30 + 1415 00d0 0E94 0000 call get_fat + 1416 .LVL132: + 1417 .L71: + 1418 .LSM109: + 1419 00d4 6230 cpi r22,lo8(2) + 1420 00d6 7105 cpc r23,__zero_reg__ + 1421 00d8 8105 cpc r24,__zero_reg__ + 1422 00da 9105 cpc r25,__zero_reg__ + 1423 00dc 00F4 brsh .L72 + 1424 .LVL133: + 1425 .L87: + 1426 00de 8C81 ldd r24,Y+4 + 1427 00e0 8068 ori r24,lo8(-128) + 1428 00e2 8C83 std Y+4,r24 + 1429 .L85: + 1430 00e4 92E0 ldi r25,lo8(2) + 1431 00e6 792E mov r7,r25 + 1432 00e8 00C0 rjmp .L63 + 1433 .LVL134: + 1434 .L72: + 1435 .LSM110: + 1436 00ea 6F3F cpi r22,lo8(-1) + 1437 00ec BFEF ldi r27,hi8(-1) + 1438 00ee 7B07 cpc r23,r27 + 1439 00f0 BFEF ldi r27,hlo8(-1) + 1440 00f2 8B07 cpc r24,r27 + 1441 00f4 BFEF ldi r27,hhi8(-1) + 1442 00f6 9B07 cpc r25,r27 + 1443 00f8 01F4 brne .+2 + 1444 00fa 00C0 rjmp .L86 + 1445 .L73: + 1446 .LSM111: + 1447 00fc 6A8B std Y+18,r22 + 1448 00fe 7B8B std Y+19,r23 + 1449 0100 8C8B std Y+20,r24 + 1450 0102 9D8B std Y+21,r25 + 1451 .LSM112: + 1452 0104 1D82 std Y+5,__zero_reg__ + 1453 .LVL135: + 1454 .L69: + 1455 .LSM113: + 1456 0106 0881 ld r16,Y + 1457 0108 1981 ldd r17,Y+1 + 1458 .LVL136: + 1459 010a 4A89 ldd r20,Y+18 + 1460 010c 5B89 ldd r21,Y+19 + 1461 010e 6C89 ldd r22,Y+20 + 1462 0110 7D89 ldd r23,Y+21 + 1463 0112 C801 movw r24,r16 + 1464 0114 0E94 0000 call clust2sect + 1465 .LVL137: + 1466 .LSM114: + 1467 0118 6115 cp r22,__zero_reg__ + 1468 011a 7105 cpc r23,__zero_reg__ + 1469 011c 8105 cpc r24,__zero_reg__ + 1470 011e 9105 cpc r25,__zero_reg__ + 1471 0120 01F0 breq .L87 + 1472 .LVL138: + 1473 .L74: + 1474 .LSM115: + 1475 0122 ED81 ldd r30,Y+5 + 1476 0124 5B01 movw r10,r22 + 1477 0126 6C01 movw r12,r24 + 1478 0128 AE0E add r10,r30 + 1479 012a B11C adc r11,__zero_reg__ + 1480 012c C11C adc r12,__zero_reg__ + 1481 012e D11C adc r13,__zero_reg__ + 1482 .LVL139: + 1483 .LSM116: + 1484 0130 7401 movw r14,r8 + 1485 0132 EF2C mov r14,r15 + 1486 0134 FF24 clr r15 + 1487 0136 E694 lsr r14 + 1488 .LSM117: + 1489 0138 E114 cp r14,__zero_reg__ + 1490 013a F104 cpc r15,__zero_reg__ + 1491 013c 01F0 breq .L75 + 1492 .LSM118: + 1493 013e D801 movw r26,r16 + 1494 0140 1296 adiw r26,2 + 1495 0142 2C91 ld r18,X + 1496 0144 F0E0 ldi r31,lo8(0) + 1497 0146 C701 movw r24,r14 + 1498 0148 8E0F add r24,r30 + 1499 014a 9F1F adc r25,r31 + 1500 014c 622F mov r22,r18 + 1501 014e 70E0 ldi r23,lo8(0) + 1502 0150 6817 cp r22,r24 + 1503 0152 7907 cpc r23,r25 + 1504 0154 00F4 brsh .L76 + 1505 .LSM119: + 1506 0156 7B01 movw r14,r22 + 1507 0158 EE1A sub r14,r30 + 1508 015a FF0A sbc r15,r31 + 1509 .L76: + 1510 .LSM120: + 1511 015c F801 movw r30,r16 + 1512 015e 8181 ldd r24,Z+1 + 1513 0160 B201 movw r22,r4 + 1514 0162 A601 movw r20,r12 + 1515 0164 9501 movw r18,r10 + 1516 0166 0E2D mov r16,r14 + 1517 0168 0E94 0000 call disk_read + 1518 016c 8823 tst r24 + 1519 016e 01F4 brne .L86 + 1520 .L77: + 1521 .LSM121: + 1522 0170 8D81 ldd r24,Y+5 + 1523 0172 8E0D add r24,r14 + 1524 0174 8D83 std Y+5,r24 + 1525 .LSM122: + 1526 0176 B701 movw r22,r14 + 1527 .LVL140: + 1528 0178 762F mov r23,r22 + 1529 017a 6627 clr r22 + 1530 017c 770F lsl r23 + 1531 017e 00C0 rjmp .L78 + 1532 .LVL141: + 1533 .L75: + 1534 .LSM123: + 1535 0180 AE8A std Y+22,r10 + 1536 0182 BF8A std Y+23,r11 + 1537 0184 C88E std Y+24,r12 + 1538 0186 D98E std Y+25,r13 + 1539 .LSM124: + 1540 0188 EF5F subi r30,lo8(-(1)) + 1541 018a ED83 std Y+5,r30 + 1542 .LVL142: + 1543 .L68: + 1544 .LSM125: + 1545 018c EE80 ldd r14,Y+6 + 1546 018e FF80 ldd r15,Y+7 + 1547 0190 0885 ldd r16,Y+8 + 1548 0192 1985 ldd r17,Y+9 + 1549 .LVL143: + 1550 .LSM126: + 1551 0194 4E89 ldd r20,Y+22 + 1552 0196 5F89 ldd r21,Y+23 + 1553 0198 688D ldd r22,Y+24 + 1554 019a 798D ldd r23,Y+25 + 1555 019c 8881 ld r24,Y + 1556 019e 9981 ldd r25,Y+1 + 1557 01a0 0E94 0000 call move_window + 1558 .LVL144: + 1559 01a4 8823 tst r24 + 1560 01a6 01F0 breq .L79 + 1561 .LVL145: + 1562 .L86: + 1563 .LSM127: + 1564 01a8 8C81 ldd r24,Y+4 + 1565 01aa 8068 ori r24,lo8(-128) + 1566 01ac 8C83 std Y+4,r24 + 1567 01ae 7724 clr r7 + 1568 01b0 7394 inc r7 + 1569 01b2 00C0 rjmp .L63 + 1570 .LVL146: + 1571 .L79: + 1572 .LSM128: + 1573 01b4 C701 movw r24,r14 + 1574 01b6 9170 andi r25,hi8(511) + 1575 01b8 20E0 ldi r18,lo8(512) + 1576 01ba 32E0 ldi r19,hi8(512) + 1577 01bc 281B sub r18,r24 + 1578 01be 390B sbc r19,r25 + 1579 01c0 B401 movw r22,r8 + 1580 .LVL147: + 1581 01c2 2815 cp r18,r8 + 1582 01c4 3905 cpc r19,r9 + 1583 01c6 00F4 brsh .L80 + 1584 .LVL148: + 1585 01c8 B901 movw r22,r18 + 1586 .L80: + 1587 .LSM129: + 1588 01ca 9B01 movw r18,r22 + 1589 .LVL149: + 1590 .LBB4: + 1591 .LBB5: + 1592 .LSM130: + 1593 01cc 8E81 ldd r24,Y+6 + 1594 01ce 9F81 ldd r25,Y+7 + 1595 01d0 9170 andi r25,hi8(511) + 1596 01d2 8296 adiw r24,34 + 1597 01d4 E881 ld r30,Y + 1598 01d6 F981 ldd r31,Y+1 + 1599 .LVL150: + 1600 01d8 E80F add r30,r24 + 1601 01da F91F adc r31,r25 + 1602 01dc D201 movw r26,r4 + 1603 01de 00C0 rjmp .L81 + 1604 .LVL151: + 1605 .L82: + 1606 .LSM131: + 1607 01e0 8191 ld r24,Z+ + 1608 01e2 8D93 st X+,r24 + 1609 .L81: + 1610 01e4 2150 subi r18,lo8(-(-1)) + 1611 01e6 3040 sbci r19,hi8(-(-1)) + 1612 01e8 8FEF ldi r24,hi8(-1) + 1613 01ea 2F3F cpi r18,lo8(-1) + 1614 01ec 3807 cpc r19,r24 + 1615 01ee 01F4 brne .L82 + 1616 .LVL152: + 1617 .L78: + 1618 .LBE5: + 1619 .LBE4: + 1620 .LSM132: + 1621 01f0 460E add r4,r22 + 1622 01f2 571E adc r5,r23 + 1623 01f4 CB01 movw r24,r22 + 1624 01f6 A0E0 ldi r26,lo8(0) + 1625 01f8 B0E0 ldi r27,hi8(0) + 1626 .LVL153: + 1627 01fa 2E81 ldd r18,Y+6 + 1628 01fc 3F81 ldd r19,Y+7 + 1629 01fe 4885 ldd r20,Y+8 + 1630 0200 5985 ldd r21,Y+9 + 1631 .LVL154: + 1632 0202 280F add r18,r24 + 1633 0204 391F adc r19,r25 + 1634 0206 4A1F adc r20,r26 + 1635 0208 5B1F adc r21,r27 + 1636 020a 2E83 std Y+6,r18 + 1637 020c 3F83 std Y+7,r19 + 1638 020e 4887 std Y+8,r20 + 1639 0210 5987 std Y+9,r21 + 1640 0212 D101 movw r26,r2 + 1641 .LVL155: + 1642 0214 8D91 ld r24,X+ + 1643 0216 9C91 ld r25,X + 1644 0218 1197 sbiw r26,1 + 1645 021a 860F add r24,r22 + 1646 021c 971F adc r25,r23 + 1647 021e 8D93 st X+,r24 + 1648 0220 9C93 st X,r25 + 1649 0222 861A sub r8,r22 + 1650 0224 970A sbc r9,r23 + 1651 .LVL156: + 1652 .L67: + 1653 .LSM133: + 1654 0226 8114 cp r8,__zero_reg__ + 1655 0228 9104 cpc r9,__zero_reg__ + 1656 022a 01F0 breq .+2 + 1657 022c 00C0 rjmp .L83 + 1658 .LVL157: + 1659 .L63: + 1660 .LSM134: + 1661 022e 872D mov r24,r7 + 1662 /* epilogue start */ + 1663 0230 DF91 pop r29 + 1664 0232 CF91 pop r28 + 1665 .LVL158: + 1666 0234 1F91 pop r17 + 1667 .LVL159: + 1668 0236 0F91 pop r16 + 1669 .LVL160: + 1670 0238 FF90 pop r15 + 1671 023a EF90 pop r14 + 1672 .LVL161: + 1673 023c DF90 pop r13 + 1674 023e CF90 pop r12 + 1675 0240 BF90 pop r11 + 1676 0242 AF90 pop r10 + 1677 .LVL162: + 1678 0244 9F90 pop r9 + 1679 0246 8F90 pop r8 + 1680 .LVL163: + 1681 0248 7F90 pop r7 + 1682 .LVL164: + 1683 024a 5F90 pop r5 + 1684 024c 4F90 pop r4 + 1685 .LVL165: + 1686 024e 3F90 pop r3 + 1687 0250 2F90 pop r2 + 1688 .LVL166: + 1689 0252 0895 ret + 1690 .LFE71: + 1692 .section .text.check_fs,"ax",@progbits + 1694 check_fs: + 1695 .LFB66: + 1696 .LSM135: + 1697 .LVL167: + 1698 0000 0F93 push r16 + 1699 0002 CF93 push r28 + 1700 0004 DF93 push r29 + 1701 /* prologue: function */ + 1702 /* frame size = 0 */ + 1703 0006 EC01 movw r28,r24 + 1704 0008 9A01 movw r18,r20 + 1705 000a AB01 movw r20,r22 + 1706 .LSM136: + 1707 000c BC01 movw r22,r24 + 1708 000e 6E5D subi r22,lo8(-(34)) + 1709 0010 7F4F sbci r23,hi8(-(34)) + 1710 0012 8981 ldd r24,Y+1 + 1711 .LVL168: + 1712 0014 01E0 ldi r16,lo8(1) + 1713 0016 0E94 0000 call disk_read + 1714 .LVL169: + 1715 001a 8823 tst r24 + 1716 001c 01F0 breq .L89 + 1717 001e 23E0 ldi r18,lo8(3) + 1718 0020 00C0 rjmp .L90 + 1719 .L89: + 1720 .LSM137: + 1721 0022 C05E subi r28,lo8(-(544)) + 1722 0024 DD4F sbci r29,hi8(-(544)) + 1723 0026 8881 ld r24,Y + 1724 0028 9981 ldd r25,Y+1 + 1725 002a C052 subi r28,lo8(-(-544)) + 1726 002c D240 sbci r29,hi8(-(-544)) + 1727 002e 8555 subi r24,lo8(-21931) + 1728 0030 9A4A sbci r25,hi8(-21931) + 1729 0032 01F0 breq .L91 + 1730 0034 22E0 ldi r18,lo8(2) + 1731 0036 00C0 rjmp .L90 + 1732 .L91: + 1733 .LSM138: + 1734 0038 C85A subi r28,lo8(-(88)) + 1735 003a DF4F sbci r29,hi8(-(88)) + 1736 003c 8881 ld r24,Y + 1737 003e 9981 ldd r25,Y+1 + 1738 0040 AA81 ldd r26,Y+2 + 1739 0042 BB81 ldd r27,Y+3 + 1740 0044 C855 subi r28,lo8(-(-88)) + 1741 0046 D040 sbci r29,hi8(-(-88)) + 1742 0048 B070 andi r27,hhi8(16777215) + 1743 004a 8654 subi r24,lo8(5521734) + 1744 004c 9144 sbci r25,hi8(5521734) + 1745 004e A445 sbci r26,hlo8(5521734) + 1746 0050 B040 sbci r27,hhi8(5521734) + 1747 0052 01F4 brne .L92 + 1748 0054 20E0 ldi r18,lo8(0) + 1749 0056 00C0 rjmp .L90 + 1750 .L92: + 1751 0058 20E0 ldi r18,lo8(0) + 1752 005a CC58 subi r28,lo8(-(116)) + 1753 005c DF4F sbci r29,hi8(-(116)) + 1754 005e 8881 ld r24,Y + 1755 0060 9981 ldd r25,Y+1 + 1756 0062 AA81 ldd r26,Y+2 + 1757 0064 BB81 ldd r27,Y+3 + 1758 0066 B070 andi r27,hhi8(16777215) + 1759 0068 8654 subi r24,lo8(5521734) + 1760 006a 9144 sbci r25,hi8(5521734) + 1761 006c A445 sbci r26,hlo8(5521734) + 1762 006e B040 sbci r27,hhi8(5521734) + 1763 0070 01F0 breq .L90 + 1764 0072 21E0 ldi r18,lo8(1) + 1765 .L90: + 1766 .LSM139: + 1767 0074 822F mov r24,r18 + 1768 /* epilogue start */ + 1769 0076 DF91 pop r29 + 1770 0078 CF91 pop r28 + 1771 .LVL170: + 1772 007a 0F91 pop r16 + 1773 007c 0895 ret + 1774 .LFE66: + 1776 .section .text.chk_mounted,"ax",@progbits + 1777 .global chk_mounted + 1779 chk_mounted: + 1780 .LFB67: + 1781 .LSM140: + 1782 .LVL171: + 1783 0000 AF92 push r10 + 1784 0002 BF92 push r11 + 1785 0004 CF92 push r12 + 1786 0006 DF92 push r13 + 1787 0008 EF92 push r14 + 1788 000a FF92 push r15 + 1789 000c 0F93 push r16 + 1790 000e 1F93 push r17 + 1791 0010 CF93 push r28 + 1792 0012 DF93 push r29 + 1793 /* prologue: function */ + 1794 /* frame size = 0 */ + 1795 0014 DC01 movw r26,r24 + 1796 0016 AB01 movw r20,r22 + 1797 .LVL172: + 1798 .LSM141: + 1799 0018 ED91 ld r30,X+ + 1800 001a FC91 ld r31,X + 1801 001c 1197 sbiw r26,1 + 1802 .LVL173: + 1803 .LSM142: + 1804 001e 8081 ld r24,Z + 1805 .LVL174: + 1806 0020 282F mov r18,r24 + 1807 .LVL175: + 1808 0022 30E0 ldi r19,lo8(0) + 1809 .LVL176: + 1810 0024 2053 subi r18,lo8(-(-48)) + 1811 0026 3040 sbci r19,hi8(-(-48)) + 1812 .LVL177: + 1813 .LSM143: + 1814 0028 2A30 cpi r18,10 + 1815 002a 3105 cpc r19,__zero_reg__ + 1816 002c 00F4 brsh .L96 + 1817 .LVL178: + 1818 002e 8181 ldd r24,Z+1 + 1819 0030 8A33 cpi r24,lo8(58) + 1820 0032 01F4 brne .L96 + 1821 .LSM144: + 1822 0034 3296 adiw r30,2 + 1823 0036 ED93 st X+,r30 + 1824 0038 FC93 st X,r31 + 1825 .LSM145: + 1826 003a 232B or r18,r19 + 1827 003c 01F0 breq .L96 + 1828 003e 8BE0 ldi r24,lo8(11) + 1829 0040 00C0 rjmp .L97 + 1830 .L96: + 1831 .LSM146: + 1832 0042 C091 0000 lds r28,FatFs + 1833 0046 D091 0000 lds r29,(FatFs)+1 + 1834 .LVL179: + 1835 004a FA01 movw r30,r20 + 1836 .LVL180: + 1837 004c D183 std Z+1,r29 + 1838 004e C083 st Z,r28 + 1839 .LSM147: + 1840 0050 2097 sbiw r28,0 + 1841 0052 01F4 brne .L98 + 1842 0054 8CE0 ldi r24,lo8(12) + 1843 0056 00C0 rjmp .L97 + 1844 .L98: + 1845 .LSM148: + 1846 0058 8881 ld r24,Y + 1847 005a 8823 tst r24 + 1848 005c 01F0 breq .L99 + 1849 .LSM149: + 1850 005e 8981 ldd r24,Y+1 + 1851 0060 0E94 0000 call disk_status + 1852 .LVL181: + 1853 .LSM150: + 1854 0064 80FF sbrs r24,0 + 1855 0066 00C0 rjmp .L112 + 1856 .LVL182: + 1857 .L99: + 1858 .LSM151: + 1859 0068 1882 st Y,__zero_reg__ + 1860 .LSM152: + 1861 006a 1982 std Y+1,__zero_reg__ + 1862 .LSM153: + 1863 006c 80E0 ldi r24,lo8(0) + 1864 006e 0E94 0000 call disk_initialize + 1865 .LVL183: + 1866 .LSM154: + 1867 0072 80FF sbrs r24,0 + 1868 0074 00C0 rjmp .L100 + 1869 0076 83E0 ldi r24,lo8(3) + 1870 0078 00C0 rjmp .L97 + 1871 .L100: + 1872 .LSM155: + 1873 007a CE01 movw r24,r28 + 1874 007c 40E0 ldi r20,lo8(0) + 1875 007e 50E0 ldi r21,hi8(0) + 1876 0080 60E0 ldi r22,hlo8(0) + 1877 0082 70E0 ldi r23,hhi8(0) + 1878 0084 0E94 0000 call check_fs + 1879 .LSM156: + 1880 0088 8130 cpi r24,lo8(1) + 1881 .LVL184: + 1882 008a 01F0 breq .L101 + 1883 008c EE24 clr r14 + 1884 008e FF24 clr r15 + 1885 0090 8701 movw r16,r14 + 1886 .LVL185: + 1887 0092 00C0 rjmp .L102 + 1888 .LVL186: + 1889 .L101: + 1890 .LSM157: + 1891 0094 FE01 movw r30,r28 + 1892 .LVL187: + 1893 0096 E052 subi r30,lo8(-(480)) + 1894 0098 FE4F sbci r31,hi8(-(480)) + 1895 .LSM158: + 1896 009a 8481 ldd r24,Z+4 + 1897 .LVL188: + 1898 009c 8823 tst r24 + 1899 009e 01F4 brne .+2 + 1900 00a0 00C0 rjmp .L103 + 1901 .LSM159: + 1902 00a2 E084 ldd r14,Z+8 + 1903 00a4 F184 ldd r15,Z+9 + 1904 00a6 0285 ldd r16,Z+10 + 1905 00a8 1385 ldd r17,Z+11 + 1906 .LVL189: + 1907 .LSM160: + 1908 00aa CE01 movw r24,r28 + 1909 00ac B801 movw r22,r16 + 1910 00ae A701 movw r20,r14 + 1911 00b0 0E94 0000 call check_fs + 1912 .LVL190: + 1913 .L102: + 1914 .LSM161: + 1915 00b4 8330 cpi r24,lo8(3) + 1916 00b6 01F4 brne .L104 + 1917 00b8 81E0 ldi r24,lo8(1) + 1918 .LVL191: + 1919 00ba 00C0 rjmp .L97 + 1920 .LVL192: + 1921 .L104: + 1922 .LSM162: + 1923 00bc 8823 tst r24 + 1924 00be 01F0 breq .+2 + 1925 00c0 00C0 rjmp .L103 + 1926 00c2 8DA5 ldd r24,Y+45 + 1927 00c4 9EA5 ldd r25,Y+46 + 1928 .LVL193: + 1929 00c6 8050 subi r24,lo8(512) + 1930 00c8 9240 sbci r25,hi8(512) + 1931 00ca 01F0 breq .+2 + 1932 00cc 00C0 rjmp .L103 + 1933 .LSM163: + 1934 00ce 88AD ldd r24,Y+56 + 1935 00d0 99AD ldd r25,Y+57 + 1936 00d2 BC01 movw r22,r24 + 1937 .LVL194: + 1938 00d4 80E0 ldi r24,lo8(0) + 1939 00d6 90E0 ldi r25,hi8(0) + 1940 .LVL195: + 1941 .LSM164: + 1942 00d8 6115 cp r22,__zero_reg__ + 1943 00da 7105 cpc r23,__zero_reg__ + 1944 00dc 8105 cpc r24,__zero_reg__ + 1945 00de 9105 cpc r25,__zero_reg__ + 1946 00e0 01F4 brne .L105 + 1947 00e2 CA5B subi r28,lo8(-(70)) + 1948 00e4 DF4F sbci r29,hi8(-(70)) + 1949 00e6 6881 ld r22,Y + 1950 00e8 7981 ldd r23,Y+1 + 1951 00ea 8A81 ldd r24,Y+2 + 1952 00ec 9B81 ldd r25,Y+3 + 1953 .LVL196: + 1954 00ee C654 subi r28,lo8(-(-70)) + 1955 00f0 D040 sbci r29,hi8(-(-70)) + 1956 .L105: + 1957 .LSM165: + 1958 00f2 6A87 std Y+10,r22 + 1959 00f4 7B87 std Y+11,r23 + 1960 00f6 8C87 std Y+12,r24 + 1961 00f8 9D87 std Y+13,r25 + 1962 .LSM166: + 1963 00fa 2AA9 ldd r18,Y+50 + 1964 00fc 2B83 std Y+3,r18 + 1965 .LSM167: + 1966 00fe 30E0 ldi r19,lo8(0) + 1967 0100 40E0 ldi r20,lo8(0) + 1968 0102 50E0 ldi r21,hi8(0) + 1969 0104 0E94 0000 call __mulsi3 + 1970 .LVL197: + 1971 0108 5B01 movw r10,r22 + 1972 010a 6C01 movw r12,r24 + 1973 .LVL198: + 1974 .LSM168: + 1975 010c 88A9 ldd r24,Y+48 + 1976 010e 99A9 ldd r25,Y+49 + 1977 .LVL199: + 1978 0110 A0E0 ldi r26,lo8(0) + 1979 0112 B0E0 ldi r27,hi8(0) + 1980 0114 E80E add r14,r24 + 1981 0116 F91E adc r15,r25 + 1982 0118 0A1F adc r16,r26 + 1983 011a 1B1F adc r17,r27 + 1984 011c EA8A std Y+18,r14 + 1985 011e FB8A std Y+19,r15 + 1986 0120 0C8B std Y+20,r16 + 1987 0122 1D8B std Y+21,r17 + 1988 .LSM169: + 1989 0124 8FA5 ldd r24,Y+47 + 1990 0126 8A83 std Y+2,r24 + 1991 .LSM170: + 1992 0128 2BA9 ldd r18,Y+51 + 1993 012a 3CA9 ldd r19,Y+52 + 1994 012c 3987 std Y+9,r19 + 1995 012e 2887 std Y+8,r18 + 1996 .LSM171: + 1997 0130 8DA9 ldd r24,Y+53 + 1998 0132 9EA9 ldd r25,Y+54 + 1999 0134 7C01 movw r14,r24 + 2000 .LVL200: + 2001 0136 00E0 ldi r16,lo8(0) + 2002 0138 10E0 ldi r17,hi8(0) + 2003 .LVL201: + 2004 .LSM172: + 2005 013a E114 cp r14,__zero_reg__ + 2006 013c F104 cpc r15,__zero_reg__ + 2007 013e 0105 cpc r16,__zero_reg__ + 2008 0140 1105 cpc r17,__zero_reg__ + 2009 0142 01F4 brne .L106 + 2010 0144 CE5B subi r28,lo8(-(66)) + 2011 0146 DF4F sbci r29,hi8(-(66)) + 2012 0148 E880 ld r14,Y + 2013 014a F980 ldd r15,Y+1 + 2014 014c 0A81 ldd r16,Y+2 + 2015 014e 1B81 ldd r17,Y+3 + 2016 .LVL202: + 2017 0150 C254 subi r28,lo8(-(-66)) + 2018 0152 D040 sbci r29,hi8(-(-66)) + 2019 .L106: + 2020 .LSM173: + 2021 0154 88A9 ldd r24,Y+48 + 2022 0156 99A9 ldd r25,Y+49 + 2023 0158 A0E0 ldi r26,lo8(0) + 2024 015a B0E0 ldi r27,hi8(0) + 2025 015c E81A sub r14,r24 + 2026 015e F90A sbc r15,r25 + 2027 0160 0A0B sbc r16,r26 + 2028 0162 1B0B sbc r17,r27 + 2029 .LVL203: + 2030 0164 EA18 sub r14,r10 + 2031 0166 FB08 sbc r15,r11 + 2032 0168 0C09 sbc r16,r12 + 2033 016a 1D09 sbc r17,r13 + 2034 016c 44E0 ldi r20,4 + 2035 016e 3695 1: lsr r19 + 2036 0170 2795 ror r18 + 2037 0172 4A95 dec r20 + 2038 0174 01F4 brne 1b + 2039 0176 C901 movw r24,r18 + 2040 0178 A0E0 ldi r26,lo8(0) + 2041 017a B0E0 ldi r27,hi8(0) + 2042 017c E81A sub r14,r24 + 2043 017e F90A sbc r15,r25 + 2044 0180 0A0B sbc r16,r26 + 2045 0182 1B0B sbc r17,r27 + 2046 0184 2A81 ldd r18,Y+2 + 2047 0186 30E0 ldi r19,lo8(0) + 2048 0188 40E0 ldi r20,lo8(0) + 2049 018a 50E0 ldi r21,hi8(0) + 2050 018c C801 movw r24,r16 + 2051 018e B701 movw r22,r14 + 2052 0190 0E94 0000 call __udivmodsi4 + 2053 0194 2E5F subi r18,lo8(-(2)) + 2054 0196 3F4F sbci r19,hi8(-(2)) + 2055 0198 4F4F sbci r20,hlo8(-(2)) + 2056 019a 5F4F sbci r21,hhi8(-(2)) + 2057 .LVL204: + 2058 019c 2E87 std Y+14,r18 + 2059 019e 3F87 std Y+15,r19 + 2060 01a0 488B std Y+16,r20 + 2061 01a2 598B std Y+17,r21 + 2062 .LSM174: + 2063 01a4 273F cpi r18,lo8(4087) + 2064 01a6 8FE0 ldi r24,hi8(4087) + 2065 01a8 3807 cpc r19,r24 + 2066 01aa 80E0 ldi r24,hlo8(4087) + 2067 01ac 4807 cpc r20,r24 + 2068 01ae 80E0 ldi r24,hhi8(4087) + 2069 01b0 5807 cpc r21,r24 + 2070 01b2 00F4 brsh .L107 + 2071 01b4 61E0 ldi r22,lo8(1) + 2072 .LVL205: + 2073 01b6 00C0 rjmp .L108 + 2074 .LVL206: + 2075 .L107: + 2076 .LSM175: + 2077 01b8 275F subi r18,lo8(65527) + 2078 01ba 3F4F sbci r19,hi8(65527) + 2079 01bc 4040 sbci r20,hlo8(65527) + 2080 01be 5040 sbci r21,hhi8(65527) + 2081 01c0 00F0 brlo .+2 + 2082 01c2 00C0 rjmp .L109 + 2083 01c4 62E0 ldi r22,lo8(2) + 2084 .LVL207: + 2085 .L108: + 2086 .LSM176: + 2087 01c6 8A89 ldd r24,Y+18 + 2088 01c8 9B89 ldd r25,Y+19 + 2089 01ca AC89 ldd r26,Y+20 + 2090 01cc BD89 ldd r27,Y+21 + 2091 01ce 8A0D add r24,r10 + 2092 01d0 9B1D adc r25,r11 + 2093 01d2 AC1D adc r26,r12 + 2094 01d4 BD1D adc r27,r13 + 2095 01d6 8E8B std Y+22,r24 + 2096 01d8 9F8B std Y+23,r25 + 2097 01da A88F std Y+24,r26 + 2098 01dc B98F std Y+25,r27 + 2099 .L110: + 2100 .LSM177: + 2101 01de 8885 ldd r24,Y+8 + 2102 01e0 9985 ldd r25,Y+9 + 2103 01e2 34E0 ldi r19,4 + 2104 01e4 9695 1: lsr r25 + 2105 01e6 8795 ror r24 + 2106 01e8 3A95 dec r19 + 2107 01ea 01F4 brne 1b + 2108 01ec A0E0 ldi r26,lo8(0) + 2109 01ee B0E0 ldi r27,hi8(0) + 2110 01f0 2A89 ldd r18,Y+18 + 2111 01f2 3B89 ldd r19,Y+19 + 2112 01f4 4C89 ldd r20,Y+20 + 2113 01f6 5D89 ldd r21,Y+21 + 2114 .LVL208: + 2115 01f8 820F add r24,r18 + 2116 01fa 931F adc r25,r19 + 2117 01fc A41F adc r26,r20 + 2118 01fe B51F adc r27,r21 + 2119 0200 8A0D add r24,r10 + 2120 0202 9B1D adc r25,r11 + 2121 0204 AC1D adc r26,r12 + 2122 0206 BD1D adc r27,r13 + 2123 0208 8A8F std Y+26,r24 + 2124 020a 9B8F std Y+27,r25 + 2125 020c AC8F std Y+28,r26 + 2126 020e BD8F std Y+29,r27 + 2127 .LSM178: + 2128 0210 6883 st Y,r22 + 2129 .LSM179: + 2130 0212 1E8E std Y+30,__zero_reg__ + 2131 0214 1F8E std Y+31,__zero_reg__ + 2132 0216 18A2 std Y+32,__zero_reg__ + 2133 0218 19A2 std Y+33,__zero_reg__ + 2134 .LSM180: + 2135 021a 8091 0000 lds r24,Fsid + 2136 021e 9091 0000 lds r25,(Fsid)+1 + 2137 0222 0196 adiw r24,1 + 2138 0224 9093 0000 sts (Fsid)+1,r25 + 2139 0228 8093 0000 sts Fsid,r24 + 2140 022c 9F83 std Y+7,r25 + 2141 022e 8E83 std Y+6,r24 + 2142 .L112: + 2143 0230 80E0 ldi r24,lo8(0) + 2144 0232 00C0 rjmp .L97 + 2145 .LVL209: + 2146 .L103: + 2147 .LSM181: + 2148 0234 8DE0 ldi r24,lo8(13) + 2149 .LVL210: + 2150 .L97: + 2151 /* epilogue start */ + 2152 .LSM182: + 2153 0236 DF91 pop r29 + 2154 0238 CF91 pop r28 + 2155 .LVL211: + 2156 023a 1F91 pop r17 + 2157 023c 0F91 pop r16 + 2158 .LVL212: + 2159 023e FF90 pop r15 + 2160 0240 EF90 pop r14 + 2161 .LVL213: + 2162 0242 DF90 pop r13 + 2163 0244 CF90 pop r12 + 2164 0246 BF90 pop r11 + 2165 0248 AF90 pop r10 + 2166 .LVL214: + 2167 024a 0895 ret + 2168 .LVL215: + 2169 .L109: + 2170 .LSM183: + 2171 024c C25B subi r28,lo8(-(78)) + 2172 024e DF4F sbci r29,hi8(-(78)) + 2173 0250 8881 ld r24,Y + 2174 0252 9981 ldd r25,Y+1 + 2175 0254 AA81 ldd r26,Y+2 + 2176 0256 BB81 ldd r27,Y+3 + 2177 0258 CE54 subi r28,lo8(-(-78)) + 2178 025a D040 sbci r29,hi8(-(-78)) + 2179 025c 8E8B std Y+22,r24 + 2180 025e 9F8B std Y+23,r25 + 2181 0260 A88F std Y+24,r26 + 2182 0262 B98F std Y+25,r27 + 2183 0264 63E0 ldi r22,lo8(3) + 2184 .LVL216: + 2185 0266 00C0 rjmp .L110 + 2186 .LFE67: + 2188 .data + 2189 .LC0: + 2190 0000 2022 2A2B .string " \"*+,[=]|\177" + 2190 2C5B 3D5D + 2190 7C7F 00 + 2191 .section .text.f_open,"ax",@progbits + 2192 .global f_open + 2194 f_open: + 2195 .LFB70: + 2196 .LSM184: + 2197 .LVL217: + 2198 0000 3F92 push r3 + 2199 0002 4F92 push r4 + 2200 0004 5F92 push r5 + 2201 0006 6F92 push r6 + 2202 0008 7F92 push r7 + 2203 000a 8F92 push r8 + 2204 000c 9F92 push r9 + 2205 000e AF92 push r10 + 2206 0010 BF92 push r11 + 2207 0012 CF92 push r12 + 2208 0014 DF92 push r13 + 2209 0016 EF92 push r14 + 2210 0018 FF92 push r15 + 2211 001a 0F93 push r16 + 2212 001c 1F93 push r17 + 2213 001e DF93 push r29 + 2214 0020 CF93 push r28 + 2215 0022 CDB7 in r28,__SP_L__ + 2216 0024 DEB7 in r29,__SP_H__ + 2217 0026 A497 sbiw r28,36 + 2218 0028 0FB6 in __tmp_reg__,__SREG__ + 2219 002a F894 cli + 2220 002c DEBF out __SP_H__,r29 + 2221 002e 0FBE out __SREG__,__tmp_reg__ + 2222 0030 CDBF out __SP_L__,r28 + 2223 /* prologue: function */ + 2224 /* frame size = 36 */ + 2225 0032 4C01 movw r8,r24 + 2226 0034 7CA3 std Y+36,r23 + 2227 0036 6BA3 std Y+35,r22 + 2228 0038 742E mov r7,r20 + 2229 .LSM185: + 2230 003a DC01 movw r26,r24 + 2231 003c 1D92 st X+,__zero_reg__ + 2232 003e 1C92 st X,__zero_reg__ + 2233 .LSM186: + 2234 0040 CE01 movw r24,r28 + 2235 .LVL218: + 2236 0042 8396 adiw r24,35 + 2237 0044 BE01 movw r22,r28 + 2238 .LVL219: + 2239 0046 635F subi r22,lo8(-(13)) + 2240 0048 7F4F sbci r23,hi8(-(13)) + 2241 004a 40E0 ldi r20,lo8(0) + 2242 .LVL220: + 2243 004c 0E94 0000 call chk_mounted + 2244 .LSM187: + 2245 0050 8823 tst r24 + 2246 .LVL221: + 2247 0052 01F0 breq .+2 + 2248 0054 00C0 rjmp .L115 + 2249 .LSM188: + 2250 0056 CE01 movw r24,r28 + 2251 .LVL222: + 2252 0058 0196 adiw r24,1 + 2253 005a 9AA3 std Y+34,r25 + 2254 005c 89A3 std Y+33,r24 + 2255 .LSM189: + 2256 005e CBA0 ldd r12,Y+35 + 2257 0060 DCA0 ldd r13,Y+36 + 2258 .LVL223: + 2259 0062 00C0 rjmp .L116 + 2260 .L117: + 2261 .LBB20: + 2262 .LBB21: + 2263 .LSM190: + 2264 0064 0894 sec + 2265 0066 C11C adc r12,__zero_reg__ + 2266 0068 D11C adc r13,__zero_reg__ + 2267 .L116: + 2268 006a F601 movw r30,r12 + 2269 006c 8081 ld r24,Z + 2270 006e 8032 cpi r24,lo8(32) + 2271 0070 01F0 breq .L117 + 2272 .LSM191: + 2273 0072 8F32 cpi r24,lo8(47) + 2274 0074 01F0 breq .L118 + 2275 0076 8C35 cpi r24,lo8(92) + 2276 0078 01F4 brne .L119 + 2277 .L118: + 2278 .LSM192: + 2279 007a 0894 sec + 2280 007c C11C adc r12,__zero_reg__ + 2281 007e D11C adc r13,__zero_reg__ + 2282 .L119: + 2283 .LSM193: + 2284 0080 1B8A std Y+19,__zero_reg__ + 2285 0082 1C8A std Y+20,__zero_reg__ + 2286 0084 1D8A std Y+21,__zero_reg__ + 2287 0086 1E8A std Y+22,__zero_reg__ + 2288 .LSM194: + 2289 0088 D601 movw r26,r12 + 2290 008a 8C91 ld r24,X + 2291 008c 8032 cpi r24,lo8(32) + 2292 008e 00F4 brsh .L120 + 2293 .LBE21: + 2294 .LSM195: + 2295 0090 CE01 movw r24,r28 + 2296 0092 0D96 adiw r24,13 + 2297 0094 60E0 ldi r22,lo8(0) + 2298 0096 70E0 ldi r23,hi8(0) + 2299 0098 0E94 0000 call dir_seek + 2300 .LBE20: + 2301 .LSM196: + 2302 009c 8823 tst r24 + 2303 .LVL224: + 2304 009e 01F4 brne .+2 + 2305 00a0 00C0 rjmp .L160 + 2306 00a2 00C0 rjmp .L115 + 2307 .LVL225: + 2308 .L120: + 2309 .LBB45: + 2310 .LBB44: + 2311 .LBB22: + 2312 .LBB24: + 2313 .LBB28: + 2314 .LBB29: + 2315 .LSM197: + 2316 00a4 F0E2 ldi r31,lo8(32) + 2317 00a6 6F2E mov r6,r31 + 2318 .LBE29: + 2319 .LBE28: + 2320 .LSM198: + 2321 00a8 E5E0 ldi r30,lo8(5) + 2322 00aa 3E2E mov r3,r30 + 2323 .LBE24: + 2324 .LBE22: + 2325 .LBB32: + 2326 .LSM199: + 2327 00ac 7DE0 ldi r23,lo8(13) + 2328 00ae 472E mov r4,r23 + 2329 00b0 512C mov r5,__zero_reg__ + 2330 00b2 4C0E add r4,r28 + 2331 00b4 5D1E adc r5,r29 + 2332 .LVL226: + 2333 .L163: + 2334 .LBE32: + 2335 .LBB41: + 2336 .LBB23: + 2337 .LSM200: + 2338 00b6 A9A1 ldd r26,Y+33 + 2339 00b8 BAA1 ldd r27,Y+34 + 2340 .LVL227: + 2341 00ba FD01 movw r30,r26 + 2342 .LVL228: + 2343 00bc 8BE0 ldi r24,lo8(11) + 2344 00be 90E0 ldi r25,hi8(11) + 2345 .LVL229: + 2346 00c0 00C0 rjmp .L122 + 2347 .LVL230: + 2348 .L123: + 2349 .LBB27: + 2350 .LBB30: + 2351 .LSM201: + 2352 00c2 6192 st Z+,r6 + 2353 .L122: + 2354 00c4 0197 sbiw r24,1 + 2355 00c6 2FEF ldi r18,hi8(-1) + 2356 00c8 8F3F cpi r24,lo8(-1) + 2357 00ca 9207 cpc r25,r18 + 2358 00cc 01F4 brne .L123 + 2359 00ce 40E0 ldi r20,lo8(0) + 2360 00d0 50E0 ldi r21,hi8(0) + 2361 .LVL231: + 2362 00d2 AA24 clr r10 + 2363 00d4 BB24 clr r11 + 2364 00d6 68E0 ldi r22,lo8(8) + 2365 00d8 E62E mov r14,r22 + 2366 00da F12C mov r15,__zero_reg__ + 2367 00dc 10E0 ldi r17,lo8(0) + 2368 .LVL232: + 2369 .L161: + 2370 .LBE30: + 2371 .LBE27: + 2372 .LSM202: + 2373 00de F601 movw r30,r12 + 2374 00e0 EA0D add r30,r10 + 2375 00e2 FB1D adc r31,r11 + 2376 00e4 2081 ld r18,Z + 2377 00e6 0894 sec + 2378 00e8 A11C adc r10,__zero_reg__ + 2379 00ea B11C adc r11,__zero_reg__ + 2380 .LSM203: + 2381 00ec 2132 cpi r18,lo8(33) + 2382 00ee 00F4 brsh .+2 + 2383 00f0 00C0 rjmp .L124 + 2384 00f2 2F32 cpi r18,lo8(47) + 2385 00f4 01F4 brne .+2 + 2386 00f6 00C0 rjmp .L125 + 2387 00f8 2C35 cpi r18,lo8(92) + 2388 00fa 01F4 brne .+2 + 2389 00fc 00C0 rjmp .L125 + 2390 .LSM204: + 2391 00fe 2E32 cpi r18,lo8(46) + 2392 0100 01F0 breq .L126 + 2393 0102 4E15 cp r20,r14 + 2394 0104 5F05 cpc r21,r15 + 2395 0106 04F0 brlt .L127 + 2396 .L126: + 2397 .LSM205: + 2398 0108 88E0 ldi r24,lo8(8) + 2399 010a E816 cp r14,r24 + 2400 010c F104 cpc r15,__zero_reg__ + 2401 .LVL233: + 2402 010e 01F0 breq .+2 + 2403 0110 00C0 rjmp .L128 + 2404 0112 2E32 cpi r18,lo8(46) + 2405 0114 01F0 breq .+2 + 2406 0116 00C0 rjmp .L128 + 2407 .LSM206: + 2408 0118 110F lsl r17 + 2409 011a 110F lsl r17 + 2410 011c 48E0 ldi r20,lo8(8) + 2411 011e 50E0 ldi r21,hi8(8) + 2412 0120 3BE0 ldi r19,lo8(11) + 2413 0122 E32E mov r14,r19 + 2414 0124 F12C mov r15,__zero_reg__ + 2415 .LVL234: + 2416 0126 00C0 rjmp .L161 + 2417 .LVL235: + 2418 .L127: + 2419 .LSM207: + 2420 0128 27FD sbrc r18,7 + 2421 .LSM208: + 2422 012a 1360 ori r17,lo8(3) + 2423 .L130: + 2424 .LSM209: + 2425 012c 822F mov r24,r18 + 2426 .LVL236: + 2427 012e 8158 subi r24,lo8(-(127)) + 2428 0130 8F31 cpi r24,lo8(31) + 2429 0132 00F0 brlo .L131 + 2430 0134 8F55 subi r24,lo8(-(-95)) + 2431 0136 8D31 cpi r24,lo8(29) + 2432 0138 00F4 brsh .L132 + 2433 .L131: + 2434 .LSM210: + 2435 013a F601 movw r30,r12 + 2436 013c EA0D add r30,r10 + 2437 013e FB1D adc r31,r11 + 2438 0140 3081 ld r19,Z + 2439 .LSM211: + 2440 0142 832F mov r24,r19 + 2441 0144 8054 subi r24,lo8(-(-64)) + 2442 0146 8F33 cpi r24,lo8(63) + 2443 0148 00F0 brlo .L133 + 2444 014a 8054 subi r24,lo8(-(-64)) + 2445 014c 8D37 cpi r24,lo8(125) + 2446 014e 00F0 brlo .+2 + 2447 0150 00C0 rjmp .L128 + 2448 .L133: + 2449 0152 C701 movw r24,r14 + 2450 0154 0197 sbiw r24,1 + 2451 0156 4817 cp r20,r24 + 2452 0158 5907 cpc r21,r25 + 2453 015a 04F0 brlt .+2 + 2454 015c 00C0 rjmp .L128 + 2455 .LSM212: + 2456 015e 0894 sec + 2457 0160 A11C adc r10,__zero_reg__ + 2458 0162 B11C adc r11,__zero_reg__ + 2459 .LSM213: + 2460 0164 FD01 movw r30,r26 + 2461 0166 E40F add r30,r20 + 2462 0168 F51F adc r31,r21 + 2463 016a 2083 st Z,r18 + 2464 016c CA01 movw r24,r20 + 2465 016e 0196 adiw r24,1 + 2466 .LVL237: + 2467 .LSM214: + 2468 0170 FD01 movw r30,r26 + 2469 0172 E80F add r30,r24 + 2470 0174 F91F adc r31,r25 + 2471 0176 3083 st Z,r19 + 2472 0178 AC01 movw r20,r24 + 2473 017a 00C0 rjmp .L164 + 2474 .LVL238: + 2475 .L132: + 2476 .LSM215: + 2477 017c 622F mov r22,r18 + 2478 017e 70E0 ldi r23,lo8(0) + 2479 0180 E0E0 ldi r30,lo8(.LC0) + 2480 0182 F0E0 ldi r31,hi8(.LC0) + 2481 .LVL239: + 2482 0184 00C0 rjmp .L134 + 2483 .LVL240: + 2484 .L136: + 2485 .LBB25: + 2486 .LBB26: + 2487 .LSM216: + 2488 0186 3196 adiw r30,1 + 2489 .LVL241: + 2490 .L134: + 2491 0188 8081 ld r24,Z + 2492 018a 8823 tst r24 + 2493 018c 01F0 breq .L135 + 2494 018e 90E0 ldi r25,lo8(0) + 2495 .LVL242: + 2496 0190 8617 cp r24,r22 + 2497 0192 9707 cpc r25,r23 + 2498 0194 01F4 brne .L136 + 2499 0196 00C0 rjmp .L128 + 2500 .LVL243: + 2501 .L135: + 2502 .LBE26: + 2503 .LBE25: + 2504 .LSM217: + 2505 0198 822F mov r24,r18 + 2506 019a 8154 subi r24,lo8(-(-65)) + 2507 019c 8A31 cpi r24,lo8(26) + 2508 019e 00F4 brsh .L137 + 2509 .LSM218: + 2510 01a0 1260 ori r17,lo8(2) + 2511 01a2 00C0 rjmp .L138 + 2512 .L137: + 2513 .LSM219: + 2514 01a4 822F mov r24,r18 + 2515 01a6 8156 subi r24,lo8(-(-97)) + 2516 01a8 8A31 cpi r24,lo8(26) + 2517 01aa 00F4 brsh .L138 + 2518 .LSM220: + 2519 01ac 1160 ori r17,lo8(1) + 2520 01ae 2052 subi r18,lo8(-(-32)) + 2521 .L138: + 2522 .LSM221: + 2523 01b0 FD01 movw r30,r26 + 2524 .LVL244: + 2525 01b2 E40F add r30,r20 + 2526 01b4 F51F adc r31,r21 + 2527 01b6 2083 st Z,r18 + 2528 .LVL245: + 2529 .L164: + 2530 01b8 4F5F subi r20,lo8(-(1)) + 2531 01ba 5F4F sbci r21,hi8(-(1)) + 2532 01bc 00C0 rjmp .L161 + 2533 .LVL246: + 2534 .L124: + 2535 01be 912F mov r25,r17 + 2536 .LVL247: + 2537 01c0 64E0 ldi r22,lo8(4) + 2538 .LVL248: + 2539 .L158: + 2540 .LSM222: + 2541 01c2 452B or r20,r21 + 2542 01c4 01F4 brne .+2 + 2543 01c6 00C0 rjmp .L128 + 2544 .LSM223: + 2545 01c8 8C91 ld r24,X + 2546 .LVL249: + 2547 01ca 853E cpi r24,lo8(-27) + 2548 01cc 01F4 brne .L139 + 2549 01ce 3C92 st X,r3 + 2550 .L139: + 2551 .LSM224: + 2552 01d0 E8E0 ldi r30,lo8(8) + 2553 01d2 EE16 cp r14,r30 + 2554 01d4 F104 cpc r15,__zero_reg__ + 2555 01d6 01F4 brne .L140 + 2556 01d8 912F mov r25,r17 + 2557 01da 990F lsl r25 + 2558 01dc 990F lsl r25 + 2559 .LVL250: + 2560 .L140: + 2561 .LSM225: + 2562 01de 292F mov r18,r25 + 2563 01e0 30E0 ldi r19,lo8(0) + 2564 .LVL251: + 2565 01e2 C901 movw r24,r18 + 2566 01e4 8370 andi r24,lo8(3) + 2567 01e6 9070 andi r25,hi8(3) + 2568 01e8 0197 sbiw r24,1 + 2569 01ea 01F4 brne .L141 + 2570 01ec 6061 ori r22,lo8(16) + 2571 .L141: + 2572 .LSM226: + 2573 01ee 2C70 andi r18,lo8(12) + 2574 01f0 3070 andi r19,hi8(12) + 2575 01f2 2430 cpi r18,4 + 2576 01f4 3105 cpc r19,__zero_reg__ + 2577 01f6 01F4 brne .L142 + 2578 01f8 6860 ori r22,lo8(8) + 2579 .L142: + 2580 .LSM227: + 2581 01fa 1B96 adiw r26,11 + 2582 01fc 6C93 st X,r22 + 2583 .LBE23: + 2584 .LBE41: + 2585 .LBB42: + 2586 .LSM228: + 2587 01fe C201 movw r24,r4 + 2588 0200 60E0 ldi r22,lo8(0) + 2589 0202 70E0 ldi r23,hi8(0) + 2590 .LVL252: + 2591 0204 0E94 0000 call dir_seek + 2592 .LVL253: + 2593 0208 482F mov r20,r24 + 2594 .LVL254: + 2595 .LBB33: + 2596 .LSM229: + 2597 020a 8823 tst r24 + 2598 020c 01F0 breq .+2 + 2599 020e 00C0 rjmp .L143 + 2600 .LVL255: + 2601 .L162: + 2602 .LBE33: + 2603 .LSM230: + 2604 0210 4B8D ldd r20,Y+27 + 2605 0212 5C8D ldd r21,Y+28 + 2606 0214 6D8D ldd r22,Y+29 + 2607 0216 7E8D ldd r23,Y+30 + 2608 .LVL256: + 2609 0218 8D85 ldd r24,Y+13 + 2610 021a 9E85 ldd r25,Y+14 + 2611 .LVL257: + 2612 021c 0E94 0000 call move_window + 2613 .LVL258: + 2614 0220 482F mov r20,r24 + 2615 .LVL259: + 2616 .LBB40: + 2617 .LSM231: + 2618 0222 8823 tst r24 + 2619 0224 01F0 breq .+2 + 2620 0226 00C0 rjmp .L143 + 2621 .LVL260: + 2622 .LSM232: + 2623 0228 EF8D ldd r30,Y+31 + 2624 022a F8A1 ldd r31,Y+32 + 2625 .LVL261: + 2626 .LSM233: + 2627 022c 8081 ld r24,Z + 2628 .LVL262: + 2629 022e 8823 tst r24 + 2630 0230 01F4 brne .+2 + 2631 0232 00C0 rjmp .L147 + 2632 .L144: + 2633 .LSM234: + 2634 0234 8385 ldd r24,Z+11 + 2635 0236 83FD sbrc r24,3 + 2636 0238 00C0 rjmp .L145 + 2637 023a 89A1 ldd r24,Y+33 + 2638 .LVL263: + 2639 023c 9AA1 ldd r25,Y+34 + 2640 .LVL264: + 2641 023e 9C01 movw r18,r24 + 2642 .LVL265: + 2643 0240 D901 movw r26,r18 + 2644 .LVL266: + 2645 0242 2BE0 ldi r18,lo8(11) + 2646 0244 30E0 ldi r19,hi8(11) + 2647 .LVL267: + 2648 .L146: + 2649 .LBB34: + 2650 .LBB35: + 2651 .LSM235: + 2652 0246 2150 subi r18,lo8(-(-1)) + 2653 0248 3040 sbci r19,hi8(-(-1)) + 2654 024a 8FEF ldi r24,hi8(-1) + 2655 024c 2F3F cpi r18,lo8(-1) + 2656 024e 3807 cpc r19,r24 + 2657 0250 01F4 brne .+2 + 2658 0252 00C0 rjmp .L143 + 2659 0254 9081 ld r25,Z + 2660 .LVL268: + 2661 0256 8C91 ld r24,X + 2662 0258 9817 cp r25,r24 + 2663 025a 01F4 brne .L145 + 2664 025c 3196 adiw r30,1 + 2665 .LVL269: + 2666 025e 1196 adiw r26,1 + 2667 0260 00C0 rjmp .L146 + 2668 .LVL270: + 2669 .L145: + 2670 .LBE35: + 2671 .LBE34: + 2672 .LBB36: + 2673 .LBB37: + 2674 .LSM236: + 2675 0262 0989 ldd r16,Y+17 + 2676 0264 1A89 ldd r17,Y+18 + 2677 0266 0F5F subi r16,lo8(-(1)) + 2678 0268 1F4F sbci r17,hi8(-(1)) + 2679 .LSM237: + 2680 026a 01F4 brne .+2 + 2681 026c 00C0 rjmp .L147 + 2682 026e 8B8D ldd r24,Y+27 + 2683 0270 9C8D ldd r25,Y+28 + 2684 0272 AD8D ldd r26,Y+29 + 2685 0274 BE8D ldd r27,Y+30 + 2686 0276 0097 sbiw r24,0 + 2687 0278 A105 cpc r26,__zero_reg__ + 2688 027a B105 cpc r27,__zero_reg__ + 2689 027c 01F4 brne .+2 + 2690 027e 00C0 rjmp .L147 + 2691 .LSM238: + 2692 0280 2FE0 ldi r18,lo8(15) + 2693 0282 E22E mov r14,r18 + 2694 0284 F12C mov r15,__zero_reg__ + 2695 .LVL271: + 2696 0286 E022 and r14,r16 + 2697 0288 F122 and r15,r17 + 2698 028a E114 cp r14,__zero_reg__ + 2699 028c F104 cpc r15,__zero_reg__ + 2700 028e 01F0 breq .+2 + 2701 0290 00C0 rjmp .L148 + 2702 .LSM239: + 2703 0292 0196 adiw r24,1 + 2704 0294 A11D adc r26,__zero_reg__ + 2705 0296 B11D adc r27,__zero_reg__ + 2706 0298 8B8F std Y+27,r24 + 2707 029a 9C8F std Y+28,r25 + 2708 029c AD8F std Y+29,r26 + 2709 029e BE8F std Y+30,r27 + 2710 .LSM240: + 2711 02a0 4F89 ldd r20,Y+23 + 2712 02a2 588D ldd r21,Y+24 + 2713 02a4 698D ldd r22,Y+25 + 2714 02a6 7A8D ldd r23,Y+26 + 2715 .LVL272: + 2716 02a8 ED85 ldd r30,Y+13 + 2717 02aa FE85 ldd r31,Y+14 + 2718 .LVL273: + 2719 02ac 4115 cp r20,__zero_reg__ + 2720 02ae 5105 cpc r21,__zero_reg__ + 2721 02b0 6105 cpc r22,__zero_reg__ + 2722 02b2 7105 cpc r23,__zero_reg__ + 2723 02b4 01F4 brne .L149 + 2724 .LSM241: + 2725 02b6 8085 ldd r24,Z+8 + 2726 02b8 9185 ldd r25,Z+9 + 2727 02ba 0817 cp r16,r24 + 2728 02bc 1907 cpc r17,r25 + 2729 02be 00F0 brlo .L148 + 2730 02c0 00C0 rjmp .L147 + 2731 .L149: + 2732 .LSM242: + 2733 02c2 8281 ldd r24,Z+2 + 2734 02c4 90E0 ldi r25,lo8(0) + 2735 02c6 0197 sbiw r24,1 + 2736 02c8 9801 movw r18,r16 + 2737 02ca A4E0 ldi r26,4 + 2738 02cc 3695 1: lsr r19 + 2739 02ce 2795 ror r18 + 2740 02d0 AA95 dec r26 + 2741 02d2 01F4 brne 1b + 2742 .LVL274: + 2743 02d4 8223 and r24,r18 + 2744 02d6 9323 and r25,r19 + 2745 02d8 892B or r24,r25 + 2746 02da 01F4 brne .L148 + 2747 .LBE37: + 2748 .LSM243: + 2749 02dc CF01 movw r24,r30 + 2750 02de 0E94 0000 call get_fat + 2751 02e2 AB01 movw r20,r22 + 2752 02e4 BC01 movw r22,r24 + 2753 .LVL275: + 2754 .LBB38: + 2755 .LSM244: + 2756 02e6 4230 cpi r20,lo8(2) + 2757 02e8 5105 cpc r21,__zero_reg__ + 2758 02ea 6105 cpc r22,__zero_reg__ + 2759 02ec 7105 cpc r23,__zero_reg__ + 2760 02ee 00F4 brsh .L150 + 2761 02f0 42E0 ldi r20,lo8(2) + 2762 .LVL276: + 2763 02f2 00C0 rjmp .L143 + 2764 .LVL277: + 2765 .L150: + 2766 .LSM245: + 2767 02f4 4F3F cpi r20,lo8(-1) + 2768 02f6 9FEF ldi r25,hi8(-1) + 2769 02f8 5907 cpc r21,r25 + 2770 02fa 9FEF ldi r25,hlo8(-1) + 2771 02fc 6907 cpc r22,r25 + 2772 02fe 9FEF ldi r25,hhi8(-1) + 2773 0300 7907 cpc r23,r25 + 2774 0302 01F4 brne .L151 + 2775 0304 41E0 ldi r20,lo8(1) + 2776 .LVL278: + 2777 0306 00C0 rjmp .L143 + 2778 .LVL279: + 2779 .L151: + 2780 .LSM246: + 2781 0308 ED85 ldd r30,Y+13 + 2782 030a FE85 ldd r31,Y+14 + 2783 030c 8685 ldd r24,Z+14 + 2784 030e 9785 ldd r25,Z+15 + 2785 0310 A089 ldd r26,Z+16 + 2786 0312 B189 ldd r27,Z+17 + 2787 0314 4817 cp r20,r24 + 2788 0316 5907 cpc r21,r25 + 2789 0318 6A07 cpc r22,r26 + 2790 031a 7B07 cpc r23,r27 + 2791 031c 00F4 brsh .L147 + 2792 .LSM247: + 2793 031e 4F8B std Y+23,r20 + 2794 0320 588F std Y+24,r21 + 2795 0322 698F std Y+25,r22 + 2796 0324 7A8F std Y+26,r23 + 2797 .LBE38: + 2798 .LSM248: + 2799 0326 CF01 movw r24,r30 + 2800 0328 0E94 0000 call clust2sect + 2801 .LVL280: + 2802 .LBB39: + 2803 032c 6B8F std Y+27,r22 + 2804 032e 7C8F std Y+28,r23 + 2805 0330 8D8F std Y+29,r24 + 2806 0332 9E8F std Y+30,r25 + 2807 .LVL281: + 2808 .L148: + 2809 .LSM249: + 2810 0334 1A8B std Y+18,r17 + 2811 0336 098B std Y+17,r16 + 2812 .LSM250: + 2813 0338 75E0 ldi r23,5 + 2814 033a EE0C 1: lsl r14 + 2815 033c FF1C rol r15 + 2816 033e 7A95 dec r23 + 2817 0340 01F4 brne 1b + 2818 0342 A2E2 ldi r26,lo8(34) + 2819 0344 B0E0 ldi r27,hi8(34) + 2820 .LVL282: + 2821 0346 EA0E add r14,r26 + 2822 0348 FB1E adc r15,r27 + 2823 034a 8D85 ldd r24,Y+13 + 2824 034c 9E85 ldd r25,Y+14 + 2825 034e 8E0D add r24,r14 + 2826 0350 9F1D adc r25,r15 + 2827 0352 98A3 std Y+32,r25 + 2828 0354 8F8F std Y+31,r24 + 2829 0356 00C0 rjmp .L162 + 2830 .LVL283: + 2831 .L147: + 2832 0358 44E0 ldi r20,lo8(4) + 2833 .LVL284: + 2834 .L143: + 2835 .LBE39: + 2836 .LBE36: + 2837 .LBE40: + 2838 .LBE42: + 2839 .LSM251: + 2840 035a E9A1 ldd r30,Y+33 + 2841 035c FAA1 ldd r31,Y+34 + 2842 .LVL285: + 2843 035e 9385 ldd r25,Z+11 + 2844 .LVL286: + 2845 0360 9470 andi r25,lo8(4) + 2846 .LSM252: + 2847 0362 4423 tst r20 + 2848 0364 01F0 breq .L153 + 2849 0366 842F mov r24,r20 + 2850 .LSM253: + 2851 0368 4430 cpi r20,lo8(4) + 2852 036a 01F0 breq .+2 + 2853 036c 00C0 rjmp .L115 + 2854 036e 9923 tst r25 + 2855 0370 01F4 brne .+2 + 2856 0372 00C0 rjmp .L155 + 2857 0374 00C0 rjmp .L115 + 2858 .L153: + 2859 .LSM254: + 2860 0376 9923 tst r25 + 2861 0378 01F4 brne .L156 + 2862 .LSM255: + 2863 037a EF8D ldd r30,Y+31 + 2864 037c F8A1 ldd r31,Y+32 + 2865 .LVL287: + 2866 .LSM256: + 2867 037e 8385 ldd r24,Z+11 + 2868 0380 84FF sbrs r24,4 + 2869 0382 00C0 rjmp .L155 + 2870 .LBB43: + 2871 .LBB31: + 2872 .LSM257: + 2873 0384 CA0C add r12,r10 + 2874 0386 DB1C adc r13,r11 + 2875 .LBE31: + 2876 .LBE43: + 2877 .LSM258: + 2878 0388 8489 ldd r24,Z+20 + 2879 038a 9589 ldd r25,Z+21 + 2880 038c A0E0 ldi r26,lo8(0) + 2881 038e B0E0 ldi r27,hi8(0) + 2882 .LVL288: + 2883 0390 DC01 movw r26,r24 + 2884 0392 9927 clr r25 + 2885 0394 8827 clr r24 + 2886 0396 228D ldd r18,Z+26 + 2887 0398 338D ldd r19,Z+27 + 2888 .LVL289: + 2889 039a 40E0 ldi r20,lo8(0) + 2890 039c 50E0 ldi r21,hi8(0) + 2891 .LVL290: + 2892 039e 822B or r24,r18 + 2893 03a0 932B or r25,r19 + 2894 03a2 A42B or r26,r20 + 2895 03a4 B52B or r27,r21 + 2896 03a6 8B8B std Y+19,r24 + 2897 03a8 9C8B std Y+20,r25 + 2898 03aa AD8B std Y+21,r26 + 2899 03ac BE8B std Y+22,r27 + 2900 03ae 00C0 rjmp .L163 + 2901 .LVL291: + 2902 .L160: + 2903 .LSM259: + 2904 03b0 18A2 std Y+32,__zero_reg__ + 2905 03b2 1F8E std Y+31,__zero_reg__ + 2906 .LVL292: + 2907 .L156: + 2908 .LBE44: + 2909 .LBE45: + 2910 .LSM260: + 2911 03b4 EF8D ldd r30,Y+31 + 2912 03b6 F8A1 ldd r31,Y+32 + 2913 .LVL293: + 2914 .LSM261: + 2915 03b8 3097 sbiw r30,0 + 2916 03ba 01F4 brne .+2 + 2917 03bc 00C0 rjmp .L157 + 2918 03be 8385 ldd r24,Z+11 + 2919 .LVL294: + 2920 03c0 84FD sbrc r24,4 + 2921 03c2 00C0 rjmp .L157 + 2922 .LSM262: + 2923 03c4 B1E0 ldi r27,lo8(1) + 2924 03c6 7B22 and r7,r27 + 2925 03c8 D401 movw r26,r8 + 2926 .LVL295: + 2927 03ca 1496 adiw r26,4 + 2928 03cc 7C92 st X,r7 + 2929 .LSM263: + 2930 03ce 8489 ldd r24,Z+20 + 2931 03d0 9589 ldd r25,Z+21 + 2932 03d2 A0E0 ldi r26,lo8(0) + 2933 03d4 B0E0 ldi r27,hi8(0) + 2934 03d6 8C01 movw r16,r24 + 2935 03d8 FF24 clr r15 + 2936 03da EE24 clr r14 + 2937 .LVL296: + 2938 03dc 228D ldd r18,Z+26 + 2939 03de 338D ldd r19,Z+27 + 2940 .LVL297: + 2941 03e0 40E0 ldi r20,lo8(0) + 2942 03e2 50E0 ldi r21,hi8(0) + 2943 .LVL298: + 2944 03e4 E22A or r14,r18 + 2945 03e6 F32A or r15,r19 + 2946 03e8 042B or r16,r20 + 2947 03ea 152B or r17,r21 + 2948 03ec D401 movw r26,r8 + 2949 03ee 1E96 adiw r26,14 + 2950 03f0 ED92 st X+,r14 + 2951 03f2 FD92 st X+,r15 + 2952 03f4 0D93 st X+,r16 + 2953 03f6 1C93 st X,r17 + 2954 03f8 5197 sbiw r26,14+3 + 2955 .LSM264: + 2956 03fa 848D ldd r24,Z+28 + 2957 03fc 958D ldd r25,Z+29 + 2958 03fe A68D ldd r26,Z+30 + 2959 0400 B78D ldd r27,Z+31 + 2960 0402 F401 movw r30,r8 + 2961 .LVL299: + 2962 0404 8287 std Z+10,r24 + 2963 0406 9387 std Z+11,r25 + 2964 0408 A487 std Z+12,r26 + 2965 040a B587 std Z+13,r27 + 2966 .LSM265: + 2967 040c D401 movw r26,r8 + 2968 040e 1696 adiw r26,6 + 2969 0410 1D92 st X+,__zero_reg__ + 2970 0412 1D92 st X+,__zero_reg__ + 2971 0414 1D92 st X+,__zero_reg__ + 2972 0416 1C92 st X,__zero_reg__ + 2973 0418 1997 sbiw r26,6+3 + 2974 041a 8FEF ldi r24,lo8(-1) + 2975 041c 1596 adiw r26,5 + 2976 041e 8C93 st X,r24 + 2977 0420 1597 sbiw r26,5 + 2978 .LSM266: + 2979 0422 168A std Z+22,__zero_reg__ + 2980 0424 178A std Z+23,__zero_reg__ + 2981 0426 108E std Z+24,__zero_reg__ + 2982 0428 118E std Z+25,__zero_reg__ + 2983 .LSM267: + 2984 042a ED85 ldd r30,Y+13 + 2985 042c FE85 ldd r31,Y+14 + 2986 042e 1196 adiw r26,1 + 2987 0430 FC93 st X,r31 + 2988 0432 EE93 st -X,r30 + 2989 0434 8681 ldd r24,Z+6 + 2990 0436 9781 ldd r25,Z+7 + 2991 0438 1396 adiw r26,2+1 + 2992 043a 9C93 st X,r25 + 2993 043c 8E93 st -X,r24 + 2994 043e 1297 sbiw r26,2 + 2995 0440 80E0 ldi r24,lo8(0) + 2996 .LVL300: + 2997 0442 00C0 rjmp .L115 + 2998 .LVL301: + 2999 .L157: + 3000 .LSM268: + 3001 0444 84E0 ldi r24,lo8(4) + 3002 .LVL302: + 3003 .L115: + 3004 .LVL303: + 3005 /* epilogue start */ + 3006 .LSM269: + 3007 0446 A496 adiw r28,36 + 3008 0448 0FB6 in __tmp_reg__,__SREG__ + 3009 044a F894 cli + 3010 044c DEBF out __SP_H__,r29 + 3011 044e 0FBE out __SREG__,__tmp_reg__ + 3012 0450 CDBF out __SP_L__,r28 + 3013 0452 CF91 pop r28 + 3014 0454 DF91 pop r29 + 3015 0456 1F91 pop r17 + 3016 .LVL304: + 3017 0458 0F91 pop r16 + 3018 .LVL305: + 3019 045a FF90 pop r15 + 3020 045c EF90 pop r14 + 3021 .LVL306: + 3022 045e DF90 pop r13 + 3023 0460 CF90 pop r12 + 3024 .LVL307: + 3025 0462 BF90 pop r11 + 3026 0464 AF90 pop r10 + 3027 .LVL308: + 3028 0466 9F90 pop r9 + 3029 0468 8F90 pop r8 + 3030 .LVL309: + 3031 046a 7F90 pop r7 + 3032 .LVL310: + 3033 046c 6F90 pop r6 + 3034 046e 5F90 pop r5 + 3035 0470 4F90 pop r4 + 3036 0472 3F90 pop r3 + 3037 0474 0895 ret + 3038 .LVL311: + 3039 .L155: + 3040 .LSM270: + 3041 0476 85E0 ldi r24,lo8(5) + 3042 .LVL312: + 3043 0478 00C0 rjmp .L115 + 3044 .LVL313: + 3045 .L128: + 3046 047a 86E0 ldi r24,lo8(6) + 3047 .LVL314: + 3048 047c 00C0 rjmp .L115 + 3049 .LVL315: + 3050 .L125: + 3051 047e 912F mov r25,r17 + 3052 .LVL316: + 3053 0480 60E0 ldi r22,lo8(0) + 3054 .LVL317: + 3055 0482 00C0 rjmp .L158 + 3056 .LFE70: + 3058 .lcomm FatFs,2 + 3059 .lcomm Fsid,2 + 3172 .Letext0: +DEFINED SYMBOLS + *ABS*:00000000 ff.c +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:2 *ABS*:0000003f __SREG__ +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:3 *ABS*:0000003e __SP_H__ +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:4 *ABS*:0000003d __SP_L__ +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:5 *ABS*:00000034 __CCP__ +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:6 *ABS*:00000000 __tmp_reg__ +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:7 *ABS*:00000001 __zero_reg__ +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:19 .text.clust2sect:00000000 clust2sect +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:101 .text.f_mount:00000000 f_mount + .bss:00000000 FatFs +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:144 .text.validate:00000000 validate +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:184 .text.f_close:00000000 f_close +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:218 .text.move_window:00000000 move_window +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:292 .text.get_fat:00000000 get_fat +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:545 .text.f_lseek:00000000 f_lseek +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:1021 .text.dir_seek:00000000 dir_seek +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:1270 .text.f_read:00000000 f_read +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:1694 .text.check_fs:00000000 check_fs +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:1779 .text.chk_mounted:00000000 chk_mounted +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:3058 .bss:00000002 Fsid +C:\Users\Dean\AppData\Local\Temp/cchqutwM.s:2194 .text.f_open:00000000 f_open + +UNDEFINED SYMBOLS +__mulsi3 +disk_status +disk_read +__udivmodsi4 +disk_initialize +__do_copy_data +__do_clear_bss diff --git a/Projects/Webserver/Lib/FATFs/ffconf.h b/Projects/Webserver/Lib/FATFs/ffconf.h new file mode 100644 index 0000000000..4b19f1326d --- /dev/null +++ b/Projects/Webserver/Lib/FATFs/ffconf.h @@ -0,0 +1,166 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.07e (C)ChaN, 2010 +/----------------------------------------------------------------------------/ +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ +#ifndef _FFCONFIG +#define _FFCONFIG 0x007E + + +/*---------------------------------------------------------------------------/ +/ Function and Buffer Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 1 /* 0 or 1 */ +/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system +/ object instead of the sector buffer in the individual file object for file +/ data transfer. This reduces memory consumption 512 bytes each file object. */ + + +#define _FS_READONLY 1 /* 0 or 1 */ +/* Setting _FS_READONLY to 1 defines read only configuration. This removes +/ writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, +/ f_truncate and useless f_getfree. */ + + +#define _FS_MINIMIZE 2 /* 0, 1, 2 or 3 */ +/* The _FS_MINIMIZE option defines minimization level to remove some functions. +/ +/ 0: Full function. +/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename +/ are removed. +/ 2: f_opendir and f_readdir are removed in addition to level 1. +/ 3: f_lseek is removed in addition to level 2. */ + + +#define _USE_STRFUNC 0 /* 0, 1 or 2 */ +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + + +#define _USE_MKFS 0 /* 0 or 1 */ +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 /* 0 or 1 */ +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 932 +/* The _CODE_PAGE specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows) +/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) +/ 949 - Korean (DBCS, OEM, Windows) +/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) +/ 1250 - Central Europe (Windows) +/ 1251 - Cyrillic (Windows) +/ 1252 - Latin 1 (Windows) +/ 1253 - Greek (Windows) +/ 1254 - Turkish (Windows) +/ 1255 - Hebrew (Windows) +/ 1256 - Arabic (Windows) +/ 1257 - Baltic (Windows) +/ 1258 - Vietnam (OEM, Windows) +/ 437 - U.S. (OEM) +/ 720 - Arabic (OEM) +/ 737 - Greek (OEM) +/ 775 - Baltic (OEM) +/ 850 - Multilingual Latin 1 (OEM) +/ 858 - Multilingual Latin 1 + Euro (OEM) +/ 852 - Latin 2 (OEM) +/ 855 - Cyrillic (OEM) +/ 866 - Russian (OEM) +/ 857 - Turkish (OEM) +/ 862 - Hebrew (OEM) +/ 874 - Thai (OEM, Windows) +/ 1 - ASCII only (Valid for non LFN cfg.) +*/ + + +#define _USE_LFN 0 /* 0, 1 or 2 */ +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN. _MAX_LFN and _LFN_UNICODE have no effect. +/ 1: Enable LFN with static working buffer on the bss. NOT REENTRANT. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ +/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When enable LFN, +/ two Unicode handling functions ff_convert() and ff_wtoupper() must be added +/ to the project. */ + + +#define _LFN_UNICODE 0 /* 0 or 1 */ +/* To switch the character code set on FatFs API to Unicode, +/ enable LFN feature and set _LFN_UNICODE to 1. +*/ + + +#define _FS_RPATH 0 /* 0 or 1 */ +/* When _FS_RPATH is set to 1, relative path feature is enabled and f_chdir, +/ f_chdrive function are available. +/ Note that output of the f_readdir fnction is affected by this option. */ + + + +/*---------------------------------------------------------------------------/ +/ Physical Drive Configurations +/----------------------------------------------------------------------------*/ + +#define _DRIVES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +/* Maximum sector size to be handled. +/ Always set 512 for memory card and hard disk but a larger value may be +/ required for floppy disk (512/1024) and optical disk (512/2048). +/ When _MAX_SS is larger than 512, GET_SECTOR_SIZE command must be implememted +/ to the disk_ioctl function. */ + + +#define _MULTI_PARTITION 0 /* 0 or 1 */ +/* When _MULTI_PARTITION is set to 0, each volume is bound to the same physical +/ drive number and can mount only first primaly partition. When it is set to 1, +/ each volume is tied to the partitions listed in Drives[]. */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 1 /* 0 or 1 */ +/* The _WORD_ACCESS option defines which access method is used to the word +/ data on the FAT volume. +/ +/ 0: Byte-by-byte access. Always compatible with all platforms. +/ 1: Word access. Do not choose this unless following condition is met. +/ +/ When the byte order on the memory is big-endian or address miss-aligned +/ word access results incorrect behavior, the _WORD_ACCESS must be set to 0. +/ If it is not the case, the value can also be set to 1 to improve the +/ performance and code size. */ + + +#define _FS_REENTRANT 0 /* 0 or 1 */ +#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */ +/* The _FS_REENTRANT option switches the reentrancy of the FatFs module. +/ +/ 0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect. +/ 1: Enable reentrancy. Also user provided synchronization handlers, +/ ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj +/ function must be added to the project. */ + + +#endif /* _FFCONFIG */ diff --git a/Projects/Webserver/Lib/FATFs/integer.h b/Projects/Webserver/Lib/FATFs/integer.h new file mode 100644 index 0000000000..1d6bac3684 --- /dev/null +++ b/Projects/Webserver/Lib/FATFs/integer.h @@ -0,0 +1,37 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _INTEGER + +#if 0 +#include +#else + +/* These types must be 16-bit, 32-bit or larger integer */ +typedef int INT; +typedef unsigned int UINT; + +/* These types must be 8-bit integer */ +typedef signed char CHAR; +typedef unsigned char UCHAR; +typedef unsigned char BYTE; + +/* These types must be 16-bit integer */ +typedef short SHORT; +typedef unsigned short USHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types must be 32-bit integer */ +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long DWORD; + +/* Boolean type */ +typedef enum { FALSE = 0, TRUE } BOOL; + +#endif + +#define _INTEGER +#endif diff --git a/Projects/Webserver/Lib/HTTPServerApp.c b/Projects/Webserver/Lib/HTTPServerApp.c new file mode 100644 index 0000000000..01aab76eae --- /dev/null +++ b/Projects/Webserver/Lib/HTTPServerApp.c @@ -0,0 +1,177 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Simple HTTP Webserver Application. When connected to the uIP stack, + * this will serve out files to HTTP clients. + */ + +#include "HTTPServerApp.h" + +/** HTTP server response header, for transmission before the page contents. This indicates to the host that a page exists at the + * given location, and gives extra connection information. + */ +char PROGMEM HTTP200Header[] = "HTTP/1.1 200 OK\r\n" + "Server: LUFA RNDIS\r\n" + "Content-type: text/html\r\n" + "Connection: close\r\n\r\n"; + +/** HTTP server response header, for transmission before a resource not found error. This indicates to the host that the given + * given URL is invalid, and gives extra error information. + */ +char PROGMEM HTTP404Header[] = "HTTP/1.1 404 Not Found\r\n" + "Server: LUFA RNDIS\r\n" + "Connection: close\r\n\r\n" + "The requested file was not found."; + +/** FAT Fs structure to hold the internal state of the FAT driver for the dataflash contents. */ +FATFS DiskFATState; + +/** Initialization function for the simple HTTP webserver. */ +void WebserverApp_Init(void) +{ + /* Listen on port 80 for HTTP connections from hosts */ + uip_listen(HTONS(HTTP_SERVER_PORT)); + + /* Mount the dataflash disk via FatFS */ + f_mount(0, &DiskFATState); +} + +/** uIP stack application callback for the simple HTTP webserver. This function must be called each time the + * TCP/IP stack needs a TCP packet to be processed. + */ +void WebserverApp_Callback(void) +{ + uip_tcp_appstate_t* const AppState = &uip_conn->appstate; + char* AppData = (char*)uip_appdata; + uint16_t AppDataSize = 0; + + if (uip_aborted() || uip_timedout()) + { + /* Close the file before terminating, if it is open */ + f_close(&AppState->FileToSend); + + AppState->CurrentState = WEBSERVER_STATE_Closed; + + return; + } + else if (uip_closed()) + { + /* Completed connection, just return */ + return; + } + else if (uip_connected()) + { + /* New connection - initialize connection state and data pointer to the appropriate HTTP header */ + AppState->CurrentState = WEBSERVER_STATE_OpenRequestedFile; + } + + switch (AppState->CurrentState) + { + case WEBSERVER_STATE_OpenRequestedFile: + /* Wait for the packet containing the request header */ + if (uip_datalen()) + { + /* Must be a GET request, abort otherwise */ + if (strncmp(AppData, "GET ", (sizeof("GET ") - 1)) != 0) + { + uip_abort(); + break; + } + + char FileName[13]; + + /* Copy over the requested filename from the GET request */ + for (uint8_t i = 0; i < (sizeof(FileName) - 1); i++) + { + FileName[i] = AppData[sizeof("GET ") + i]; + + if (FileName[i] == ' ') + { + FileName[i] = 0x00; + break; + } + } + + /* Ensure requested filename is null-terminated */ + FileName[(sizeof(FileName) - 1)] = 0x00; + + /* If no filename specified, assume the default of INDEX.HTM */ + if (FileName[0] == 0x00) + strcpy(FileName, "INDEX.HTM"); + + /* Try to open the file from the Dataflash disk */ + AppState->FileOpen = (f_open(&AppState->FileToSend, FileName, FA_OPEN_EXISTING | FA_READ) == FR_OK); + + AppState->CurrentState = WEBSERVER_STATE_SendHeaders; + } + + break; + case WEBSERVER_STATE_SendHeaders: + /* Determine what HTTP header should be sent to the client */ + if (AppState->FileOpen) + { + AppDataSize = strlen_P(HTTP200Header); + strncpy_P(AppData, HTTP200Header, AppDataSize); + } + else + { + AppDataSize = strlen_P(HTTP404Header); + strncpy_P(AppData, HTTP404Header, AppDataSize); + } + + uip_send(AppData, AppDataSize); + + AppState->CurrentState = WEBSERVER_STATE_SendData; + break; + case WEBSERVER_STATE_SendData: + /* If end of file/file not open, progress to the close state */ + if (!(AppState->FileOpen)) + { + f_close(&AppState->FileToSend); + uip_close(); + AppState->CurrentState = WEBSERVER_STATE_Closed; + break; + } + + uint16_t MaxSegSize = uip_mss(); + + /* Read the next chunk of data from the open file */ + f_read(&AppState->FileToSend, AppData, MaxSegSize, &AppDataSize); + AppState->FileOpen = (MaxSegSize == AppDataSize); + + /* If data was read, send it to the client */ + if (AppDataSize) + uip_send(AppData, AppDataSize); + + break; + } +} diff --git a/Projects/Webserver/Lib/WebserverApp.h b/Projects/Webserver/Lib/HTTPServerApp.h similarity index 87% rename from Projects/Webserver/Lib/WebserverApp.h rename to Projects/Webserver/Lib/HTTPServerApp.h index c788bdbebb..b96e2e303d 100644 --- a/Projects/Webserver/Lib/WebserverApp.h +++ b/Projects/Webserver/Lib/HTTPServerApp.h @@ -30,24 +30,26 @@ /** \file * - * Header file for WebserverApp.c. + * Header file for HTTPServerApp.c. */ -#ifndef _WEBSERVER_APP_H_ -#define _WEBSERVER_APP_H_ +#ifndef _HTTPSERVER_APP_H_ +#define _HTTPSERVER_APP_H_ /* Includes: */ - #include #include + #include #include #include + #include /* Enums: */ /** States for each HTTP connection to the webserver. */ enum Webserver_States_t { + WEBSERVER_STATE_OpenRequestedFile, /** Currently opening requested file */ WEBSERVER_STATE_SendHeaders, /**< Currently sending HTTP headers to the client */ WEBSERVER_STATE_SendData, /**< Currently sending HTTP page data to the client */ WEBSERVER_STATE_Closed, /**< Connection closed after all data sent */ diff --git a/Projects/Webserver/Lib/SCSI.c b/Projects/Webserver/Lib/SCSI.c new file mode 100644 index 0000000000..933253b466 --- /dev/null +++ b/Projects/Webserver/Lib/SCSI.c @@ -0,0 +1,281 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * SCSI command processing routines, for SCSI commands issued by the host. Mass Storage + * devices use a thin "Bulk-Only Transport" protocol for issuing commands and status information, + * which wrap around standard SCSI device commands for controlling the actual storage medium. + */ + +#define INCLUDE_FROM_SCSI_C +#include "SCSI.h" + +/** Structure to hold the SCSI response data to a SCSI INQUIRY command. This gives information about the device's + * features and capabilities. + */ +SCSI_Inquiry_Response_t InquiryData = + { + .DeviceType = DEVICE_TYPE_BLOCK, + .PeripheralQualifier = 0, + + .Removable = true, + + .Version = 0, + + .ResponseDataFormat = 2, + .NormACA = false, + .TrmTsk = false, + .AERC = false, + + .AdditionalLength = 0x1F, + + .SoftReset = false, + .CmdQue = false, + .Linked = false, + .Sync = false, + .WideBus16Bit = false, + .WideBus32Bit = false, + .RelAddr = false, + + .VendorID = "LUFA", + .ProductID = "Dataflash Disk", + .RevisionID = {'0','.','0','0'}, + }; + +/** Structure to hold the sense data for the last issued SCSI command, which is returned to the host after a SCSI REQUEST SENSE + * command is issued. This gives information on exactly why the last command failed to complete. + */ +SCSI_Request_Sense_Response_t SenseData = + { + .ResponseCode = 0x70, + .AdditionalLength = 0x0A, + }; + + +/** Main routine to process the SCSI command located in the Command Block Wrapper read from the host. This dispatches + * to the appropriate SCSI command handling routine if the issued command is supported by the device, else it returns + * a command failure due to a ILLEGAL REQUEST. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + */ +bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* MSInterfaceInfo) +{ + /* Set initial sense data, before the requested command is processed */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_GOOD, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + + /* Run the appropriate SCSI command hander function based on the passed command */ + switch (MSInterfaceInfo->State.CommandBlock.SCSICommandData[0]) + { + case SCSI_CMD_INQUIRY: + SCSI_Command_Inquiry(MSInterfaceInfo); + break; + case SCSI_CMD_REQUEST_SENSE: + SCSI_Command_Request_Sense(MSInterfaceInfo); + break; + case SCSI_CMD_READ_CAPACITY_10: + SCSI_Command_Read_Capacity_10(MSInterfaceInfo); + break; + case SCSI_CMD_SEND_DIAGNOSTIC: + SCSI_Command_Send_Diagnostic(MSInterfaceInfo); + break; + case SCSI_CMD_WRITE_10: + SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_WRITE); + break; + case SCSI_CMD_READ_10: + SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_READ); + break; + case SCSI_CMD_TEST_UNIT_READY: + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + case SCSI_CMD_VERIFY_10: + /* These commands should just succeed, no handling required */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0; + break; + default: + /* Update the SENSE key to reflect the invalid command */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, + SCSI_ASENSE_INVALID_COMMAND, + SCSI_ASENSEQ_NO_QUALIFIER); + break; + } + + return (SenseData.SenseKey == SCSI_SENSE_KEY_GOOD); +} + +/** Command processing for an issued SCSI INQUIRY command. This command returns information about the device's features + * and capabilities to the host. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + */ +static void SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* MSInterfaceInfo) +{ + uint16_t AllocationLength = (((uint16_t)MSInterfaceInfo->State.CommandBlock.SCSICommandData[3] << 8) | + MSInterfaceInfo->State.CommandBlock.SCSICommandData[4]); + uint16_t BytesTransferred = (AllocationLength < sizeof(InquiryData))? AllocationLength : + sizeof(InquiryData); + + /* Only the standard INQUIRY data is supported, check if any optional INQUIRY bits set */ + if ((MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & ((1 << 0) | (1 << 1))) || + MSInterfaceInfo->State.CommandBlock.SCSICommandData[2]) + { + /* Optional but unsupported bits set - update the SENSE key and fail the request */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, + SCSI_ASENSE_INVALID_FIELD_IN_CDB, + SCSI_ASENSEQ_NO_QUALIFIER); + + return; + } + + Endpoint_Write_Stream_LE(&InquiryData, BytesTransferred, NO_STREAM_CALLBACK); + + uint8_t PadBytes[AllocationLength - BytesTransferred]; + + /* Pad out remaining bytes with 0x00 */ + Endpoint_Write_Stream_LE(&PadBytes, (AllocationLength - BytesTransferred), NO_STREAM_CALLBACK); + + /* Finalize the stream transfer to send the last packet */ + Endpoint_ClearIN(); + + /* Succeed the command and update the bytes transferred counter */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred; +} + +/** Command processing for an issued SCSI REQUEST SENSE command. This command returns information about the last issued command, + * including the error code and additional error information so that the host can determine why a command failed to complete. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + */ +static void SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* MSInterfaceInfo) +{ + uint8_t AllocationLength = MSInterfaceInfo->State.CommandBlock.SCSICommandData[4]; + uint8_t BytesTransferred = (AllocationLength < sizeof(SenseData))? AllocationLength : sizeof(SenseData); + + uint8_t PadBytes[AllocationLength - BytesTransferred]; + + Endpoint_Write_Stream_LE(&SenseData, BytesTransferred, NO_STREAM_CALLBACK); + Endpoint_Write_Stream_LE(&PadBytes, (AllocationLength - BytesTransferred), NO_STREAM_CALLBACK); + Endpoint_ClearIN(); + + /* Succeed the command and update the bytes transferred counter */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred; +} + +/** Command processing for an issued SCSI READ CAPACITY (10) command. This command returns information about the device's capacity + * on the selected Logical Unit (drive), as a number of OS-sized blocks. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + */ +static void SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* MSInterfaceInfo) +{ + uint32_t LastBlockAddressInLUN = (VIRTUAL_MEMORY_BLOCKS - 1); + uint32_t MediaBlockSize = VIRTUAL_MEMORY_BLOCK_SIZE; + + Endpoint_Write_Stream_BE(&LastBlockAddressInLUN, sizeof(LastBlockAddressInLUN), NO_STREAM_CALLBACK); + Endpoint_Write_Stream_BE(&MediaBlockSize, sizeof(MediaBlockSize), NO_STREAM_CALLBACK); + Endpoint_ClearIN(); + + /* Succeed the command and update the bytes transferred counter */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 8; +} + +/** Command processing for an issued SCSI SEND DIAGNOSTIC command. This command performs a quick check of the Dataflash ICs on the + * board, and indicates if they are present and functioning correctly. Only the Self-Test portion of the diagnostic command is + * supported. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + */ +static void SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* MSInterfaceInfo) +{ + /* Check to see if the SELF TEST bit is not set */ + if (!(MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & (1 << 2))) + { + /* Only self-test supported - update SENSE key and fail the command */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, + SCSI_ASENSE_INVALID_FIELD_IN_CDB, + SCSI_ASENSEQ_NO_QUALIFIER); + + return; + } + + /* Check to see if all attached Dataflash ICs are functional */ + if (!(DataflashManager_CheckDataflashOperation())) + { + /* Update SENSE key with a hardware error condition and return command fail */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_HARDWARE_ERROR, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + + return; + } + + /* Succeed the command and update the bytes transferred counter */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0; +} + +/** Command processing for an issued SCSI READ (10) or WRITE (10) command. This command reads in the block start address + * and total number of blocks to process, then calls the appropriate low-level dataflash routine to handle the actual + * reading and writing of the data. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + * \param[in] IsDataRead Indicates if the command is a READ (10) command or WRITE (10) command (DATA_READ or DATA_WRITE) + */ +static void SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* MSInterfaceInfo, const bool IsDataRead) +{ + uint32_t BlockAddress; + uint16_t TotalBlocks; + + /* Load in the 32-bit block address (SCSI uses big-endian, so have to reverse the byte order) */ + BlockAddress = SwapEndian_32(*(uint32_t*)&MSInterfaceInfo->State.CommandBlock.SCSICommandData[2]); + + /* Load in the 16-bit total blocks (SCSI uses big-endian, so have to reverse the byte order) */ + TotalBlocks = SwapEndian_16(*(uint32_t*)&MSInterfaceInfo->State.CommandBlock.SCSICommandData[7]); + + /* Check if the block address is outside the maximum allowable value for the LUN */ + if (BlockAddress >= VIRTUAL_MEMORY_BLOCKS) + { + /* Block address is invalid, update SENSE key and return command fail */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, + SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, + SCSI_ASENSEQ_NO_QUALIFIER); + + return; + } + + /* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */ + if (IsDataRead == DATA_READ) + DataflashManager_ReadBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks); + else + DataflashManager_WriteBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks); + + /* Update the bytes transferred counter and succeed the command */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= ((uint32_t)TotalBlocks * VIRTUAL_MEMORY_BLOCK_SIZE); +} diff --git a/Projects/Webserver/Lib/SCSI.h b/Projects/Webserver/Lib/SCSI.h new file mode 100644 index 0000000000..aa7d9327ca --- /dev/null +++ b/Projects/Webserver/Lib/SCSI.h @@ -0,0 +1,85 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Header file for SCSI.c. + */ + +#ifndef _SCSI_H_ +#define _SCSI_H_ + + /* Includes: */ + #include + #include + + #include + #include + + #include "../Descriptors.h" + #include "DataflashManager.h" + + /* Macros: */ + /** Macro to set the current SCSI sense data to the given key, additional sense code and additional sense qualifier. This + * is for convenience, as it allows for all three sense values (returned upon request to the host to give information about + * the last command failure) in a quick and easy manner. + * + * \param[in] key New SCSI sense key to set the sense code to + * \param[in] acode New SCSI additional sense key to set the additional sense code to + * \param[in] aqual New SCSI additional sense key qualifier to set the additional sense qualifier code to + */ + #define SCSI_SET_SENSE(key, acode, aqual) MACROS{ SenseData.SenseKey = (key); \ + SenseData.AdditionalSenseCode = (acode); \ + SenseData.AdditionalSenseQualifier = (aqual); }MACROE + + /** Macro for the SCSI_Command_ReadWrite_10() function, to indicate that data is to be read from the storage medium. */ + #define DATA_READ true + + /** Macro for the SCSI_Command_ReadWrite_10() function, to indicate that data is to be written to the storage medium. */ + #define DATA_WRITE false + + /** Value for the DeviceType entry in the SCSI_Inquiry_Response_t enum, indicating a Block Media device. */ + #define DEVICE_TYPE_BLOCK 0x00 + + /** Value for the DeviceType entry in the SCSI_Inquiry_Response_t enum, indicating a CD-ROM device. */ + #define DEVICE_TYPE_CDROM 0x05 + + /* Function Prototypes: */ + bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* MSInterfaceInfo); + + #if defined(INCLUDE_FROM_SCSI_C) + static void SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* MSInterfaceInfo); + static void SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* MSInterfaceInfo); + static void SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* MSInterfaceInfo); + static void SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* MSInterfaceInfo); + static void SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* MSInterfaceInfo, const bool IsDataRead); + #endif + +#endif diff --git a/Projects/Webserver/Lib/WebserverApp.c b/Projects/Webserver/Lib/WebserverApp.c deleted file mode 100644 index 02f38be874..0000000000 --- a/Projects/Webserver/Lib/WebserverApp.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - LUFA Library - Copyright (C) Dean Camera, 2010. - - dean [at] fourwalledcubicle [dot] com - www.fourwalledcubicle.com -*/ - -/* - Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that the copyright notice and this - permission notice and warranty disclaimer appear in supporting - documentation, and that the name of the author not be used in - advertising or publicity pertaining to distribution of the - software without specific, written prior permission. - - The author disclaim all warranties with regard to this - software, including all implied warranties of merchantability - and fitness. In no event shall the author be liable for any - special, indirect or consequential damages or any damages - whatsoever resulting from loss of use, data or profits, whether - in an action of contract, negligence or other tortious action, - arising out of or in connection with the use or performance of - this software. -*/ - -/** \file - * - * Simple HTTP Webserver Application. When connected to the uIP stack, - * this will serve out files to HTTP clients. - */ - -#include "WebserverApp.h" - -/** HTTP server response header, for transmission before the page contents. This indicates to the host that a page exists at the - * given location, and gives extra connection information. - */ -char PROGMEM HTTP200Header[] = "HTTP/1.1 200 OK\r\n" - "Server: LUFA RNDIS\r\n" - "Content-type: text/html\r\n" - "Connection: close\r\n\r\n"; - -/** HTTP server response header, for transmission before a resource not found error. This indicates to the host that the given - * given URL is invalid, and gives extra error information. - */ -char PROGMEM HTTP404Header[] = "HTTP/1.1 404 Not Found\r\n" - "Server: LUFA RNDIS\r\n" - "Connection: close\r\n\r\n"; - -/** Static HTTP page to serve to the host when a HTTP request is made from a host. */ -char PROGMEM HTTPPage[] = - "" - " " - " " - " LUFA Webserver Demo" - " " - " " - " " - "

Hello from your USB AVR!

" - "

" - " Hello! Welcome to the LUFA RNDIS Demo Webserver test page, running on your USB AVR via the LUFA library and uIP TCP/IP network stack. This" - " demonstrates a simple HTTP webserver serving out pages to HTTP clients." - "

" - " Project Information: http://www.fourwalledcubicle.com/LUFA.php." - "


" - " LUFA Version: " LUFA_VERSION_STRING - "

" - " " - ""; - -/** Initialization function for the simple HTTP webserver. */ -void WebserverApp_Init(void) -{ - /* Listen on port 80 for HTTP connections from hosts */ - uip_listen(HTONS(HTTP_SERVER_PORT)); -} - -/** uIP stack application callback for the simple HTTP webserver. This function must be called each time the - * TCP/IP stack needs a TCP packet to be processed. - */ -void WebserverApp_Callback(void) -{ - uip_tcp_appstate_t* const AppState = &uip_conn->appstate; - char* AppData = (char*)uip_appdata; - uint16_t AppDataSize = 0; - - if (uip_closed() || uip_aborted() || uip_timedout()) - { - /* Terminated or completed connection - don't send any new data */ - return; - } - else if (uip_connected()) - { - /* New connection - initialize connection state and data pointer to the appropriate HTTP header */ - AppState->SendPos = HTTP200Header; - AppState->CurrentState = WEBSERVER_STATE_SendHeaders; - } - - /* Calculate the maximum segment size and remaining data size */ - uint16_t BytesRemaining = strlen_P(AppState->SendPos); - uint16_t MaxSegSize = uip_mss(); - - /* No more bytes remaining in the current data being sent - progress to next data chunk or - * terminate the connection once all chunks are sent */ - if (!(BytesRemaining)) - { - /* Check which data chunk we are currently sending (header or data) */ - if (AppState->CurrentState == WEBSERVER_STATE_SendHeaders) - { - AppState->SendPos = HTTPPage; - AppState->CurrentState = WEBSERVER_STATE_SendData; - } - else if (AppState->CurrentState == WEBSERVER_STATE_SendData) - { - uip_close(); - AppState->CurrentState = WEBSERVER_STATE_Closed; - } - - return; - } - else if (BytesRemaining > MaxSegSize) - { - /* More bytes remaining to send than the maximum segment size, send next chunk */ - AppDataSize = MaxSegSize; - } - else - { - /* Less bytes than the segment size remaining, send all remaining bytes in the one packet */ - AppDataSize = BytesRemaining; - } - - /* Copy over the next data segment to the application buffer, advance send position pointer */ - strncpy_P(AppData, AppState->SendPos, AppDataSize); - AppState->SendPos += AppDataSize; - - /* Send the data to the requesting host */ - uip_send(AppData, AppDataSize); -} diff --git a/Projects/Webserver/Lib/uIPManagement.c b/Projects/Webserver/Lib/uIPManagement.c new file mode 100644 index 0000000000..dd9f15f420 --- /dev/null +++ b/Projects/Webserver/Lib/uIPManagement.c @@ -0,0 +1,189 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * uIP Managament functions. This file contains the functions and globals needed to maintain the uIP + * stack once an RNDIS device has been attached to the system. + */ + +#define INCLUDE_FROM_UIPMANAGEMENT_C +#include "uIPManagement.h" + +/** Connection timer, to retain the time elapsed since the last time the uIP connections were managed. */ +struct timer ConnectionTimer; + +/** ARP timer, to retain the time elapsed since the ARP cache was last updated. */ +struct timer ARPTimer; + +/** MAC address of the RNDIS device, when enumerated */ +struct uip_eth_addr MACAddress; + + +/** Configures the uIP stack ready for network traffic. */ +void uIPManagement_Init(void) +{ + /* uIP Timing Initialization */ + clock_init(); + timer_set(&ConnectionTimer, CLOCK_SECOND / 2); + timer_set(&ARPTimer, CLOCK_SECOND * 10); + + /* uIP Stack Initialization */ + uip_init(); + + /* DHCP/Server IP Settings Initialization */ + #if defined(ENABLE_DHCP) + DHCPApp_Init(); + #else + uip_ipaddr_t IPAddress, Netmask, GatewayIPAddress; + uip_ipaddr(&IPAddress, DEVICE_IP_ADDRESS[0], DEVICE_IP_ADDRESS[1], DEVICE_IP_ADDRESS[2], DEVICE_IP_ADDRESS[3]); + uip_ipaddr(&Netmask, DEVICE_NETMASK[0], DEVICE_NETMASK[1], DEVICE_NETMASK[2], DEVICE_NETMASK[3]); + uip_ipaddr(&GatewayIPAddress, DEVICE_GATEWAY[0], DEVICE_GATEWAY[1], DEVICE_GATEWAY[2], DEVICE_GATEWAY[3]); + uip_sethostaddr(&IPAddress); + uip_setnetmask(&Netmask); + uip_setdraddr(&GatewayIPAddress); + #endif + + uip_setethaddr(MACAddress); + + /* HTTP Webserver Initialization */ + WebserverApp_Init(); +} + +/** uIP Management function. This function manages the uIP stack when called while an RNDIS device has been + * attached to the system. + */ +void uIPManagement_ManageNetwork(void) +{ + if ((USB_CurrentMode == USB_MODE_HOST) && (USB_HostState == HOST_STATE_Configured)) + { + uIPManagement_ProcessIncommingPacket(); + uIPManagement_ManageConnections(); + } +} + +/** Processes incomming packets to the server from the connected RNDIS device, creating responses as needed. */ +static void uIPManagement_ProcessIncommingPacket(void) +{ + if (RNDIS_Host_IsPacketReceived(&Ethernet_RNDIS_Interface)) + { + LEDs_SetAllLEDs(LEDMASK_USB_BUSY); + + /* Read the incomming packet straight into the UIP packet buffer */ + RNDIS_Host_ReadPacket(&Ethernet_RNDIS_Interface, &uip_buf[0], &uip_len); + + if (uip_len > 0) + { + bool PacketHandled = true; + + struct uip_eth_hdr* EthernetHeader = (struct uip_eth_hdr*)&uip_buf[0]; + if (EthernetHeader->type == HTONS(UIP_ETHTYPE_IP)) + { + /* Filter packet by MAC destination */ + uip_arp_ipin(); + + /* Process incomming packet */ + uip_input(); + + /* Add destination MAC to outgoing packet */ + if (uip_len > 0) + uip_arp_out(); + } + else if (EthernetHeader->type == HTONS(UIP_ETHTYPE_ARP)) + { + /* Process ARP packet */ + uip_arp_arpin(); + } + else + { + PacketHandled = false; + } + + /* If a response was generated, send it */ + if ((uip_len > 0) && PacketHandled) + RNDIS_Host_SendPacket(&Ethernet_RNDIS_Interface, &uip_buf[0], uip_len); + } + + LEDs_SetAllLEDs(LEDMASK_USB_READY); + } +} + +/** Manages the currently open network connections, including TCP and (if enabled) UDP. */ +static void uIPManagement_ManageConnections(void) +{ + /* Manage open connections */ + if (timer_expired(&ConnectionTimer)) + { + timer_reset(&ConnectionTimer); + + LEDs_SetAllLEDs(LEDMASK_USB_BUSY); + + for (uint8_t i = 0; i < UIP_CONNS; i++) + { + /* Run periodic connection management for each TCP connection */ + uip_periodic(i); + + /* If a response was generated, send it */ + if (uip_len > 0) + { + /* Add destination MAC to outgoing packet */ + uip_arp_out(); + + RNDIS_Host_SendPacket(&Ethernet_RNDIS_Interface, &uip_buf[0], uip_len); + } + } + + #if defined(ENABLE_DHCP) + for (uint8_t i = 0; i < UIP_UDP_CONNS; i++) + { + /* Run periodic connection management for each UDP connection */ + uip_udp_periodic(i); + + /* If a response was generated, send it */ + if (uip_len > 0) + { + /* Add destination MAC to outgoing packet */ + uip_arp_out(); + + RNDIS_Host_SendPacket(&Ethernet_RNDIS_Interface, &uip_buf[0], uip_len); + } + } + #endif + + LEDs_SetAllLEDs(LEDMASK_USB_READY); + } + + /* Manage ARP cache refreshing */ + if (timer_expired(&ARPTimer)) + { + timer_reset(&ARPTimer); + uip_arp_timer(); + } +} diff --git a/Projects/Webserver/Lib/uIPManagement.h b/Projects/Webserver/Lib/uIPManagement.h new file mode 100644 index 0000000000..24881556b2 --- /dev/null +++ b/Projects/Webserver/Lib/uIPManagement.h @@ -0,0 +1,73 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Header file for uIPManagement.c. + */ + +#ifndef _UIPMANAGEMENT_H_ +#define _UIPMANAGEMENT_H_ + + /* Includes: */ + #include + + #include + #include + #include + + #include "Lib/DHCPApp.h" + #include "Lib/HTTPServerApp.h" + + /* Macros: */ + /** IP address that the webserver should use once connected to a RNDIS device (when DHCP is disabled). */ + #define DEVICE_IP_ADDRESS (uint8_t[]){192, 168, 1, 10} + + /** Netmask that the webserver should once connected to a RNDIS device (when DHCP is disabled). */ + #define DEVICE_NETMASK (uint8_t[]){255, 255, 255, 0} + + /** IP address of the default gateway the webserver should use when routing outside the local subnet + * (when DHCP is disabled). + */ + #define DEVICE_GATEWAY (uint8_t[]){192, 168, 1, 1} + + /* External Variables: */ + extern struct uip_eth_addr MACAddress; + + /* Function Prototypes: */ + void uIPManagement_Init(void); + void uIPManagement_ManageNetwork(void); + + #if defined(INCLUDE_FROM_UIPMANAGEMENT_C) + static void uIPManagement_ProcessIncommingPacket(void); + static void uIPManagement_ManageConnections(void); + #endif + +#endif diff --git a/Projects/Webserver/Lib/uip/conf/apps-conf.h b/Projects/Webserver/Lib/uip/conf/apps-conf.h index fc9727dcd1..76cd93719e 100644 --- a/Projects/Webserver/Lib/uip/conf/apps-conf.h +++ b/Projects/Webserver/Lib/uip/conf/apps-conf.h @@ -1,10 +1,13 @@ #ifndef __APPS_CONF_H__ #define __APPS_CONF_H__ + #include + typedef struct { uint8_t CurrentState; - char* SendPos; + FIL FileToSend; + bool FileOpen; } uip_tcp_appstate_t; typedef struct diff --git a/Projects/Webserver/USBDeviceMode.c b/Projects/Webserver/USBDeviceMode.c new file mode 100644 index 0000000000..c93ba4632f --- /dev/null +++ b/Projects/Webserver/USBDeviceMode.c @@ -0,0 +1,113 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * USB Device Mode management functions and variables. This file contains the LUFA code required to + * manage the USB Mass Storage device mode. + */ + +#include "USBDeviceMode.h" + +/** LUFA Mass Storage Class driver interface configuration and state information. This structure is + * passed to all Mass Storage Class driver functions, so that multiple instances of the same class + * within a device can be differentiated from one another. + */ +USB_ClassInfo_MS_Device_t Disk_MS_Interface = + { + .Config = + { + .InterfaceNumber = 0, + + .DataINEndpointNumber = MASS_STORAGE_IN_EPNUM, + .DataINEndpointSize = MASS_STORAGE_IO_EPSIZE, + .DataINEndpointDoubleBank = false, + + .DataOUTEndpointNumber = MASS_STORAGE_OUT_EPNUM, + .DataOUTEndpointSize = MASS_STORAGE_IO_EPSIZE, + .DataOUTEndpointDoubleBank = false, + + .TotalLUNs = 1, + }, + }; + + +/** USB device mode management task. This function manages the Mass Storage Device class driver when the device is + * initialized in USB device mode. + */ +void USBDeviceMode_USBTask(void) +{ + if (USB_CurrentMode != USB_MODE_DEVICE) + return; + + MS_Device_USBTask(&Disk_MS_Interface); +} + +/** Event handler for the library USB Connection event. */ +void EVENT_USB_Device_Connect(void) +{ + LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING); +} + +/** Event handler for the library USB Disconnection event. */ +void EVENT_USB_Device_Disconnect(void) +{ + LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); +} + +/** Event handler for the library USB Configuration Changed event. */ +void EVENT_USB_Device_ConfigurationChanged(void) +{ + LEDs_SetAllLEDs(LEDMASK_USB_READY); + + if (!(MS_Device_ConfigureEndpoints(&Disk_MS_Interface))) + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); +} + +/** Event handler for the library USB Unhandled Control Request event. */ +void EVENT_USB_Device_UnhandledControlRequest(void) +{ + MS_Device_ProcessControlRequest(&Disk_MS_Interface); +} + +/** Mass Storage class driver callback function the reception of SCSI commands from the host, which must be processed. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface configuration structure being referenced + */ +bool CALLBACK_MS_Device_SCSICommandReceived(USB_ClassInfo_MS_Device_t* MSInterfaceInfo) +{ + bool CommandSuccess; + + LEDs_SetAllLEDs(LEDMASK_USB_BUSY); + CommandSuccess = SCSI_DecodeSCSICommand(MSInterfaceInfo); + LEDs_SetAllLEDs(LEDMASK_USB_READY); + + return CommandSuccess; +} diff --git a/Projects/Webserver/USBDeviceMode.h b/Projects/Webserver/USBDeviceMode.h new file mode 100644 index 0000000000..d76c667783 --- /dev/null +++ b/Projects/Webserver/USBDeviceMode.h @@ -0,0 +1,56 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Header file for USBDeviceMode.c. + */ + +#ifndef _USBDEVICEMODE_H_ +#define _USBDEVICEMODE_H_ + + /* Includes: */ + #include + + #include "Webserver.h" + #include "Descriptors.h" + #include "Lib/SCSI.h" + + /* Function Prototypes: */ + void USBDeviceMode_USBTask(void); + + void EVENT_USB_Device_Connect(void); + void EVENT_USB_Device_Disconnect(void); + void EVENT_USB_Device_ConfigurationChanged(void); + void EVENT_USB_Device_UnhandledControlRequest(void); + + bool CALLBACK_MS_Device_SCSICommandReceived(USB_ClassInfo_MS_Device_t* MSInterfaceInfo); + +#endif diff --git a/Projects/Webserver/USBHostMode.c b/Projects/Webserver/USBHostMode.c new file mode 100644 index 0000000000..14f33e07cf --- /dev/null +++ b/Projects/Webserver/USBHostMode.c @@ -0,0 +1,178 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * USB Host Mode management functions and variables. This file contains the LUFA code required to + * manage the USB RNDIS host mode. + */ + +#include "USBHostMode.h" + +/** LUFA RNDIS Class driver interface configuration and state information. This structure is + * passed to all RNDIS Class driver functions, so that multiple instances of the same class + * within a device can be differentiated from one another. + */ +USB_ClassInfo_RNDIS_Host_t Ethernet_RNDIS_Interface = + { + .Config = + { + .DataINPipeNumber = 1, + .DataINPipeDoubleBank = false, + + .DataOUTPipeNumber = 2, + .DataOUTPipeDoubleBank = false, + + .NotificationPipeNumber = 3, + .NotificationPipeDoubleBank = false, + + .HostMaxPacketSize = UIP_CONF_BUFFER_SIZE, + }, + }; + + +/** USB host mode management task. This function manages the RNDIS Host class driver and uIP stack when the device is + * initialized in USB host mode. + */ +void USBHostMode_USBTask(void) +{ + if (USB_CurrentMode != USB_MODE_HOST) + return; + + switch (USB_HostState) + { + case HOST_STATE_Addressed: + LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING); + + uint16_t ConfigDescriptorSize; + uint8_t ConfigDescriptorData[512]; + + if (USB_Host_GetDeviceConfigDescriptor(1, &ConfigDescriptorSize, ConfigDescriptorData, + sizeof(ConfigDescriptorData)) != HOST_GETCONFIG_Successful) + { + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); + USB_HostState = HOST_STATE_WaitForDeviceRemoval; + break; + } + + if (RNDIS_Host_ConfigurePipes(&Ethernet_RNDIS_Interface, + ConfigDescriptorSize, ConfigDescriptorData) != RNDIS_ENUMERROR_NoError) + { + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); + USB_HostState = HOST_STATE_WaitForDeviceRemoval; + break; + } + + if (USB_Host_SetDeviceConfiguration(1) != HOST_SENDCONTROL_Successful) + { + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); + USB_HostState = HOST_STATE_WaitForDeviceRemoval; + break; + } + + if (RNDIS_Host_InitializeDevice(&Ethernet_RNDIS_Interface) != HOST_SENDCONTROL_Successful) + { + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); + USB_HostState = HOST_STATE_WaitForDeviceRemoval; + break; + } + + uint32_t PacketFilter = (REMOTE_NDIS_PACKET_DIRECTED | REMOTE_NDIS_PACKET_BROADCAST); + if (RNDIS_Host_SetRNDISProperty(&Ethernet_RNDIS_Interface, OID_GEN_CURRENT_PACKET_FILTER, + &PacketFilter, sizeof(PacketFilter)) != HOST_SENDCONTROL_Successful) + { + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); + USB_HostState = HOST_STATE_WaitForDeviceRemoval; + break; + } + + if (RNDIS_Host_QueryRNDISProperty(&Ethernet_RNDIS_Interface, OID_802_3_CURRENT_ADDRESS, + &MACAddress, sizeof(MACAddress)) != HOST_SENDCONTROL_Successful) + { + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); + USB_HostState = HOST_STATE_WaitForDeviceRemoval; + break; + } + + /* Initialize uIP stack */ + uIPManagement_Init(); + + LEDs_SetAllLEDs(LEDMASK_USB_READY); + USB_HostState = HOST_STATE_Configured; + break; + case HOST_STATE_Configured: + uIPManagement_ManageNetwork(); + + break; + } + + RNDIS_Host_USBTask(&Ethernet_RNDIS_Interface); +} + +/** Event handler for the USB_DeviceAttached event. This indicates that a device has been attached to the host, and + * starts the library USB task to begin the enumeration and USB management process. + */ +void EVENT_USB_Host_DeviceAttached(void) +{ + LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING); +} + +/** Event handler for the USB_DeviceUnattached event. This indicates that a device has been removed from the host, and + * stops the library USB task management process. + */ +void EVENT_USB_Host_DeviceUnattached(void) +{ + LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); +} + +/** Event handler for the USB_DeviceEnumerationComplete event. This indicates that a device has been successfully + * enumerated by the host and is now ready to be used by the application. + */ +void EVENT_USB_Host_DeviceEnumerationComplete(void) +{ + LEDs_SetAllLEDs(LEDMASK_USB_READY); +} + +/** Event handler for the USB_HostError event. This indicates that a hardware error occurred while in host mode. */ +void EVENT_USB_Host_HostError(const uint8_t ErrorCode) +{ + USB_ShutDown(); + + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); + for(;;); +} + +/** Event handler for the USB_DeviceEnumerationFailed event. This indicates that a problem occurred while + * enumerating an attached USB device. + */ +void EVENT_USB_Host_DeviceEnumerationFailed(const uint8_t ErrorCode, const uint8_t SubErrorCode) +{ + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); +} diff --git a/Projects/Webserver/USBHostMode.h b/Projects/Webserver/USBHostMode.h new file mode 100644 index 0000000000..03387edd25 --- /dev/null +++ b/Projects/Webserver/USBHostMode.h @@ -0,0 +1,57 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Header file for USBHostMode.c. + */ + +#ifndef _USBHOSTMODE_H_ +#define _USBHOSTMODE_H_ + + /* Includes: */ + #include + + #include "Webserver.h" + #include "Lib/uIPManagement.h" + + /* External Variables: */ + extern USB_ClassInfo_RNDIS_Host_t Ethernet_RNDIS_Interface; + + /* Function Prototypes: */ + void USBHostMode_USBTask(void); + + void EVENT_USB_Host_HostError(const uint8_t ErrorCode); + void EVENT_USB_Host_DeviceAttached(void); + void EVENT_USB_Host_DeviceUnattached(void); + void EVENT_USB_Host_DeviceEnumerationFailed(const uint8_t ErrorCode, const uint8_t SubErrorCode); + void EVENT_USB_Host_DeviceEnumerationComplete(void); + +#endif diff --git a/Projects/Webserver/Webserver.c b/Projects/Webserver/Webserver.c index ec1dabffb2..47fd9c7e54 100644 --- a/Projects/Webserver/Webserver.c +++ b/Projects/Webserver/Webserver.c @@ -36,36 +36,6 @@ #include "Webserver.h" -/** LUFA RNDIS Class driver interface configuration and state information. This structure is - * passed to all RNDIS Class driver functions, so that multiple instances of the same class - * within a device can be differentiated from one another. - */ -USB_ClassInfo_RNDIS_Host_t Ethernet_RNDIS_Interface = - { - .Config = - { - .DataINPipeNumber = 1, - .DataINPipeDoubleBank = false, - - .DataOUTPipeNumber = 2, - .DataOUTPipeDoubleBank = false, - - .NotificationPipeNumber = 3, - .NotificationPipeDoubleBank = false, - - .HostMaxPacketSize = UIP_CONF_BUFFER_SIZE, - }, - }; - -/** Connection timer, to retain the time elapsed since the last time the uIP connections were managed. */ -struct timer ConnectionTimer; - -/** ARP timer, to retain the time elapsed since the ARP cache was last updated. */ -struct timer ARPTimer; - -/** MAC address of the RNDIS device, when enumerated */ -struct uip_eth_addr MACAddress; - /** Main program entry point. This routine configures the hardware required by the application, then * enters a loop to run the application tasks in sequence. */ @@ -77,177 +47,13 @@ int main(void) for (;;) { - switch (USB_HostState) - { - case HOST_STATE_Addressed: - LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING); - - uint16_t ConfigDescriptorSize; - uint8_t ConfigDescriptorData[512]; + USBDeviceMode_USBTask(); + USBHostMode_USBTask(); - if (USB_Host_GetDeviceConfigDescriptor(1, &ConfigDescriptorSize, ConfigDescriptorData, - sizeof(ConfigDescriptorData)) != HOST_GETCONFIG_Successful) - { - LEDs_SetAllLEDs(LEDMASK_USB_ERROR); - USB_HostState = HOST_STATE_WaitForDeviceRemoval; - break; - } - - if (RNDIS_Host_ConfigurePipes(&Ethernet_RNDIS_Interface, - ConfigDescriptorSize, ConfigDescriptorData) != RNDIS_ENUMERROR_NoError) - { - LEDs_SetAllLEDs(LEDMASK_USB_ERROR); - USB_HostState = HOST_STATE_WaitForDeviceRemoval; - break; - } - - if (USB_Host_SetDeviceConfiguration(1) != HOST_SENDCONTROL_Successful) - { - LEDs_SetAllLEDs(LEDMASK_USB_ERROR); - USB_HostState = HOST_STATE_WaitForDeviceRemoval; - break; - } - - if (RNDIS_Host_InitializeDevice(&Ethernet_RNDIS_Interface) != HOST_SENDCONTROL_Successful) - { - LEDs_SetAllLEDs(LEDMASK_USB_ERROR); - USB_HostState = HOST_STATE_WaitForDeviceRemoval; - break; - } - - uint32_t PacketFilter = (REMOTE_NDIS_PACKET_DIRECTED | REMOTE_NDIS_PACKET_BROADCAST); - if (RNDIS_Host_SetRNDISProperty(&Ethernet_RNDIS_Interface, OID_GEN_CURRENT_PACKET_FILTER, - &PacketFilter, sizeof(PacketFilter)) != HOST_SENDCONTROL_Successful) - { - LEDs_SetAllLEDs(LEDMASK_USB_ERROR); - USB_HostState = HOST_STATE_WaitForDeviceRemoval; - break; - } - - if (RNDIS_Host_QueryRNDISProperty(&Ethernet_RNDIS_Interface, OID_802_3_CURRENT_ADDRESS, - &MACAddress, sizeof(MACAddress)) != HOST_SENDCONTROL_Successful) - { - LEDs_SetAllLEDs(LEDMASK_USB_ERROR); - USB_HostState = HOST_STATE_WaitForDeviceRemoval; - break; - } - - uip_setethaddr(MACAddress); - - LEDs_SetAllLEDs(LEDMASK_USB_READY); - USB_HostState = HOST_STATE_Configured; - break; - case HOST_STATE_Configured: - ProcessIncommingPacket(); - ManageConnections(); - - break; - } - - RNDIS_Host_USBTask(&Ethernet_RNDIS_Interface); USB_USBTask(); } } -/** Processes incomming packets to the server from the connected RNDIS device, creating responses as needed. */ -void ProcessIncommingPacket(void) -{ - if (RNDIS_Host_IsPacketReceived(&Ethernet_RNDIS_Interface)) - { - LEDs_SetAllLEDs(LEDMASK_USB_BUSY); - - /* Read the incomming packet straight into the UIP packet buffer */ - RNDIS_Host_ReadPacket(&Ethernet_RNDIS_Interface, &uip_buf[0], &uip_len); - - if (uip_len > 0) - { - bool PacketHandled = true; - - struct uip_eth_hdr* EthernetHeader = (struct uip_eth_hdr*)&uip_buf[0]; - if (EthernetHeader->type == HTONS(UIP_ETHTYPE_IP)) - { - /* Filter packet by MAC destination */ - uip_arp_ipin(); - - /* Process incomming packet */ - uip_input(); - - /* Add destination MAC to outgoing packet */ - if (uip_len > 0) - uip_arp_out(); - } - else if (EthernetHeader->type == HTONS(UIP_ETHTYPE_ARP)) - { - /* Process ARP packet */ - uip_arp_arpin(); - } - else - { - PacketHandled = false; - } - - /* If a response was generated, send it */ - if ((uip_len > 0) && PacketHandled) - RNDIS_Host_SendPacket(&Ethernet_RNDIS_Interface, &uip_buf[0], uip_len); - } - - LEDs_SetAllLEDs(LEDMASK_USB_READY); - } -} - -/** Manages the currently open network connections, including TCP and (if enabled) UDP. */ -void ManageConnections(void) -{ - /* Manage open connections */ - if (timer_expired(&ConnectionTimer)) - { - timer_reset(&ConnectionTimer); - - LEDs_SetAllLEDs(LEDMASK_USB_BUSY); - - for (uint8_t i = 0; i < UIP_CONNS; i++) - { - /* Run periodic connection management for each TCP connection */ - uip_periodic(i); - - /* If a response was generated, send it */ - if (uip_len > 0) - { - /* Add destination MAC to outgoing packet */ - uip_arp_out(); - - RNDIS_Host_SendPacket(&Ethernet_RNDIS_Interface, &uip_buf[0], uip_len); - } - } - - #if defined(ENABLE_DHCP) - for (uint8_t i = 0; i < UIP_UDP_CONNS; i++) - { - /* Run periodic connection management for each UDP connection */ - uip_udp_periodic(i); - - /* If a response was generated, send it */ - if (uip_len > 0) - { - /* Add destination MAC to outgoing packet */ - uip_arp_out(); - - RNDIS_Host_SendPacket(&Ethernet_RNDIS_Interface, &uip_buf[0], uip_len); - } - } - #endif - - LEDs_SetAllLEDs(LEDMASK_USB_READY); - } - - /* Manage ARP cache refreshing */ - if (timer_expired(&ARPTimer)) - { - timer_reset(&ARPTimer); - uip_arp_timer(); - } -} - /** Configures the board hardware and chip peripherals for the demo's functionality. */ void SetupHardware(void) { @@ -259,71 +65,8 @@ void SetupHardware(void) clock_prescale_set(clock_div_1); /* Hardware Initialization */ + SPI_Init(SPI_SPEED_FCPU_DIV_2 | SPI_SCK_LEAD_FALLING | SPI_SAMPLE_TRAILING | SPI_MODE_MASTER); + Dataflash_Init(); LEDs_Init(); - USB_Init(); - - /* uIP Timing Initialization */ - clock_init(); - timer_set(&ConnectionTimer, CLOCK_SECOND / 2); - timer_set(&ARPTimer, CLOCK_SECOND * 10); - - /* uIP Stack Initialization */ - uip_init(); - - /* DHCP/Server IP Settings Initialization */ - #if defined(ENABLE_DHCP) - DHCPApp_Init(); - #else - uip_ipaddr_t IPAddress, Netmask, GatewayIPAddress; - uip_ipaddr(&IPAddress, DEVICE_IP_ADDRESS[0], DEVICE_IP_ADDRESS[1], DEVICE_IP_ADDRESS[2], DEVICE_IP_ADDRESS[3]); - uip_ipaddr(&Netmask, DEVICE_NETMASK[0], DEVICE_NETMASK[1], DEVICE_NETMASK[2], DEVICE_NETMASK[3]); - uip_ipaddr(&GatewayIPAddress, DEVICE_GATEWAY[0], DEVICE_GATEWAY[1], DEVICE_GATEWAY[2], DEVICE_GATEWAY[3]); - uip_sethostaddr(&IPAddress); - uip_setnetmask(&Netmask); - uip_setdraddr(&GatewayIPAddress); - #endif - - /* HTTP Webserver Initialization */ - WebserverApp_Init(); -} - -/** Event handler for the USB_DeviceAttached event. This indicates that a device has been attached to the host, and - * starts the library USB task to begin the enumeration and USB management process. - */ -void EVENT_USB_Host_DeviceAttached(void) -{ - LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING); -} - -/** Event handler for the USB_DeviceUnattached event. This indicates that a device has been removed from the host, and - * stops the library USB task management process. - */ -void EVENT_USB_Host_DeviceUnattached(void) -{ - LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); -} - -/** Event handler for the USB_DeviceEnumerationComplete event. This indicates that a device has been successfully - * enumerated by the host and is now ready to be used by the application. - */ -void EVENT_USB_Host_DeviceEnumerationComplete(void) -{ - LEDs_SetAllLEDs(LEDMASK_USB_READY); -} - -/** Event handler for the USB_HostError event. This indicates that a hardware error occurred while in host mode. */ -void EVENT_USB_Host_HostError(const uint8_t ErrorCode) -{ - USB_ShutDown(); - - LEDs_SetAllLEDs(LEDMASK_USB_ERROR); - for(;;); -} - -/** Event handler for the USB_DeviceEnumerationFailed event. This indicates that a problem occurred while - * enumerating an attached USB device. - */ -void EVENT_USB_Host_DeviceEnumerationFailed(const uint8_t ErrorCode, const uint8_t SubErrorCode) -{ - LEDs_SetAllLEDs(LEDMASK_USB_ERROR); + USB_Init(USB_MODE_UID); } diff --git a/Projects/Webserver/Webserver.h b/Projects/Webserver/Webserver.h index 13462a2a2d..a9621e32fa 100644 --- a/Projects/Webserver/Webserver.h +++ b/Projects/Webserver/Webserver.h @@ -44,28 +44,14 @@ #include #include + #include + #include #include - #include - #include - #include - #include - - #include "Lib/WebserverApp.h" - #include "Lib/DHCPApp.h" + #include "USBDeviceMode.h" + #include "USBHostMode.h" /* Macros: */ - /** IP address that the webserver should use once connected to a RNDIS device (when DHCP is disabled). */ - #define DEVICE_IP_ADDRESS (uint8_t[]){192, 168, 1, 10} - - /** Netmask that the webserver should once connected to a RNDIS device (when DHCP is disabled). */ - #define DEVICE_NETMASK (uint8_t[]){255, 255, 255, 0} - - /** IP address of the default gateway the webserver should use when routing outside the local subnet - * (when DHCP is disabled). - */ - #define DEVICE_GATEWAY (uint8_t[]){192, 168, 1, 1} - /** LED mask for the library LED driver, to indicate that the USB interface is not ready. */ #define LEDMASK_USB_NOTREADY LEDS_LED1 @@ -80,19 +66,8 @@ /** LED mask for the library LED driver, to indicate that the USB interface is busy. */ #define LEDMASK_USB_BUSY LEDS_LED2 - - /* External Variables: */ - extern struct uip_eth_addr MACAddress; - + /* Function Prototypes: */ void SetupHardware(void); - void ProcessIncommingPacket(void); - void ManageConnections(void); - - void EVENT_USB_Host_HostError(const uint8_t ErrorCode); - void EVENT_USB_Host_DeviceAttached(void); - void EVENT_USB_Host_DeviceUnattached(void); - void EVENT_USB_Host_DeviceEnumerationFailed(const uint8_t ErrorCode, const uint8_t SubErrorCode); - void EVENT_USB_Host_DeviceEnumerationComplete(void); #endif diff --git a/Projects/Webserver/Webserver.txt b/Projects/Webserver/Webserver.txt index e1d298596d..e78cf5effd 100644 --- a/Projects/Webserver/Webserver.txt +++ b/Projects/Webserver/Webserver.txt @@ -41,16 +41,22 @@ * * \section SSec_Description Project Description: * - * Simple HTTP webserver project. This project combines the LUFA library with the uIP TCP/IP full network stack, to create a - * RNDIS host capable of serving out HTTP webpages to up to 10 hosts simultaneously. This project demonstrates how the two - * libraries can be combined into a robust network enabled application, with the addition of a RNDIS network device. + * Simple HTTP webserver project. This project combines the LUFA library with the uIP TCP/IP full network stack and FatFS + * library to create a RNDIS host capable of serving out HTTP webpages to multiple hosts simultaneously. This project + * demonstrates how the libraries can be combined into a robust network enabled application, with the addition of a RNDIS + * network device. * - * To use this project, plug the USB AVR into a RNDIS class device, such as a USB (desktop) modem. If compatible, the project - * will enumerate the device, set the appropriate parameters needed for connectivity and begin listening for new HTTP connections - * on port 80. The device IP, netmask and default gateway IP must be set to values appropriate for the RNDIS device being used - * for this project to work. + * To use this project, plug the USB AVR into a computer, so that it enumerates as a standard Mass Storage device. Load + * HTML files onto the disk, so that they can be served out to clients -- the default file to serve should be called + * index.htm. Filenames must be in 8.3 format for them to be retrieved correctly by the webserver. + + * When attached to a RNDIS class device, such as a USB (desktop) modem. If compatible, the system will enumerate the + * device, set the appropriate parameters needed for connectivity and begin listening for new HTTP connections on port 80. + * The device IP, netmask and default gateway IP must be set to values appropriate for the RNDIS device being used for this + * project to work, if the DHCP client is disabled (see \ref SSec_Options). * - * When properly configured, the webserver can be accessed from any HTTP webrowser by typing in the device's IP address. + * When properly configured, the webserver can be accessed from any HTTP webrowser by typing in the device's static or + * dynamically allocated IP address. * * \section SSec_Options Project Options * @@ -69,17 +75,17 @@ * * * DEVICE_IP_ADDRESS - * Webserver.h + * Lib/uIPManagement.h * IP address that the webserver should use when connected to a RNDIS device (when ENABLE_DHCP is not defined). * * * DEVICE_NETMASK - * Webserver.h + * Lib/uIPManagement.h * Netmask that the webserver should use when connected to a RNDIS device (when ENABLE_DHCP is not defined). * * * DEVICE_GATEWAY - * Webserver.h + * Lib/uIPManagement.h * Default routing gateway that the webserver should use when connected to a RNDIS device (when ENABLE_DHCP * is not defined). * diff --git a/Projects/Webserver/makefile b/Projects/Webserver/makefile index 4a1b85bddd..3c72ad8750 100644 --- a/Projects/Webserver/makefile +++ b/Projects/Webserver/makefile @@ -116,14 +116,31 @@ LUFA_PATH = ../../ # LUFA library compile-time options -LUFA_OPTS += -D USB_HOST_ONLY -LUFA_OPTS += -D USE_STATIC_OPTIONS="(USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)" +LUFA_OPTS = -D FIXED_CONTROL_ENDPOINT_SIZE=8 +LUFA_OPTS += -D FIXED_NUM_CONFIGURATIONS=1 +LUFA_OPTS += -D USE_FLASH_DESCRIPTORS +LUFA_OPTS += -D USE_STATIC_OPTIONS="(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)" # List C source files here. (C dependencies are automatically generated.) SRC = $(TARGET).c \ + Descriptors.c \ + USBDeviceMode.c \ + USBHostMode.c \ + Lib/SCSI.c \ + Lib/uIPManagement.c \ Lib/DHCPApp.c \ - Lib/WebserverApp.c \ + Lib/HTTPServerApp.c \ + Lib/DataflashManager.c \ + Lib/uip/uip.c \ + Lib/uip/uip_arp.c \ + Lib/uip/uiplib.c \ + Lib/uip/psock.c \ + Lib/uip/timer.c \ + Lib/uip/uip-neighbor.c \ + Lib/uip/conf/clock-arch.c \ + Lib/FatFS/diskio.c \ + Lib/FatFS/ff.c \ $(LUFA_PATH)/LUFA/Drivers/USB/LowLevel/DevChapter9.c \ $(LUFA_PATH)/LUFA/Drivers/USB/LowLevel/Endpoint.c \ $(LUFA_PATH)/LUFA/Drivers/USB/LowLevel/Host.c \ @@ -134,17 +151,11 @@ SRC = $(TARGET).c \ $(LUFA_PATH)/LUFA/Drivers/USB/HighLevel/USBInterrupt.c \ $(LUFA_PATH)/LUFA/Drivers/USB/HighLevel/USBTask.c \ $(LUFA_PATH)/LUFA/Drivers/USB/HighLevel/ConfigDescriptor.c \ + $(LUFA_PATH)/LUFA/Drivers/USB/Class/Device/MassStorage.c \ + $(LUFA_PATH)/LUFA/Drivers/USB/Class/Host/MassStorage.c \ $(LUFA_PATH)/LUFA/Drivers/USB/Class/Device/RNDIS.c \ $(LUFA_PATH)/LUFA/Drivers/USB/Class/Host/RNDIS.c \ - Lib/uip/uip.c \ - Lib/uip/uip_arp.c \ - Lib/uip/uiplib.c \ - Lib/uip/psock.c \ - Lib/uip/timer.c \ - Lib/uip/uip-neighbor.c \ - Lib/uip/conf/clock-arch.c \ - - + # List C++ source files here. (C dependencies are automatically generated.) CPPSRC = @@ -176,7 +187,7 @@ DEBUG = dwarf-2 # Each directory must be seperated by a space. # Use forward slashes for directory separators. # For a directory that has spaces, enclose it in quotes. -EXTRAINCDIRS = $(LUFA_PATH)/ Lib/uip/ Lib/uip/conf/ +EXTRAINCDIRS = $(LUFA_PATH)/ Lib/uip/ Lib/uip/conf/ Lib/FatFS/ # Compiler flag to set the C Standard level.