diff --git a/features/TESTS/filesystem/mbr_block_device/main.cpp b/features/TESTS/filesystem/mbr_block_device/main.cpp new file mode 100644 index 00000000000..aaa594464eb --- /dev/null +++ b/features/TESTS/filesystem/mbr_block_device/main.cpp @@ -0,0 +1,226 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "HeapBlockDevice.h" +#include "MBRBlockDevice.h" +#include + +using namespace utest::v1; + +#define BLOCK_COUNT 16 +#define BLOCK_SIZE 512 + +HeapBlockDevice bd(BLOCK_COUNT*BLOCK_SIZE, BLOCK_SIZE); + +// Testing formatting of master boot record +void test_mbr_format() +{ + // Create two partitions splitting device in ~half + int err = MBRBlockDevice::partition(&bd, 1, 0x83, 0, (BLOCK_COUNT/2)*BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + err = MBRBlockDevice::partition(&bd, 2, 0x83, -(BLOCK_COUNT/2)*BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + // Load both partitions, as well as a third to check for invalid partitions + MBRBlockDevice part1(&bd, 1); + err = part1.init(); + TEST_ASSERT_EQUAL(0, err); + + MBRBlockDevice part2(&bd, 2); + err = part2.init(); + TEST_ASSERT_EQUAL(0, err); + + MBRBlockDevice part3(&bd, 3); + err = part3.init(); + TEST_ASSERT_EQUAL(BD_ERROR_INVALID_PARTITION, err); + + // Deinit partitions + err = part1.deinit(); + TEST_ASSERT_EQUAL(0, err); + + err = part2.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + +// Testing mbr attributes +void test_mbr_attr() +{ + // Load partitions + MBRBlockDevice part1(&bd, 1); + int err = part1.init(); + TEST_ASSERT_EQUAL(0, err); + + MBRBlockDevice part2(&bd, 2); + err = part2.init(); + TEST_ASSERT_EQUAL(0, err); + + // Test attributes on partitions + printf("partition 1 partition number: %d\n", part1.get_partition_number()); + printf("partition 1 partition start: 0x%llx\n", part1.get_partition_start()); + printf("partition 1 partition stop: 0x%llx\n", part1.get_partition_stop()); + printf("partition 1 partition type: 0x%02x\n", part1.get_partition_type()); + printf("partition 1 read size: %llu bytes\n", part1.get_read_size()); + printf("partition 1 program size: %llu bytes\n", part1.get_program_size()); + printf("partition 1 erase size: %llu bytes\n", part1.get_erase_size()); + printf("partition 1 size: %llu bytes\n", part1.size()); + TEST_ASSERT_EQUAL(1, part1.get_partition_number()); + TEST_ASSERT_EQUAL(1*BLOCK_SIZE, part1.get_partition_start()); + TEST_ASSERT_EQUAL((BLOCK_COUNT/2)*BLOCK_SIZE, part1.get_partition_stop()); + TEST_ASSERT_EQUAL(0x83, part1.get_partition_type()); + TEST_ASSERT_EQUAL(BLOCK_SIZE, part1.get_read_size()); + TEST_ASSERT_EQUAL(BLOCK_SIZE, part1.get_program_size()); + TEST_ASSERT_EQUAL(BLOCK_SIZE, part1.get_erase_size()); + TEST_ASSERT_EQUAL(((BLOCK_COUNT/2)-1)*BLOCK_SIZE, part1.size()); + + printf("partition 2 partition number: %d\n", part2.get_partition_number()); + printf("partition 2 partition start: 0x%llx\n", part2.get_partition_start()); + printf("partition 2 partition stop: 0x%llx\n", part2.get_partition_stop()); + printf("partition 2 partition type: 0x%02x\n", part2.get_partition_type()); + printf("partition 2 read size: %llu bytes\n", part2.get_read_size()); + printf("partition 2 program size: %llu bytes\n", part2.get_program_size()); + printf("partition 2 erase size: %llu bytes\n", part2.get_erase_size()); + printf("partition 2 size: %llu bytes\n", part2.size()); + TEST_ASSERT_EQUAL(2, part2.get_partition_number()); + TEST_ASSERT_EQUAL((BLOCK_COUNT/2)*BLOCK_SIZE, part2.get_partition_start()); + TEST_ASSERT_EQUAL(BLOCK_COUNT*BLOCK_SIZE, part2.get_partition_stop()); + TEST_ASSERT_EQUAL(0x83, part2.get_partition_type()); + TEST_ASSERT_EQUAL(BLOCK_SIZE, part2.get_read_size()); + TEST_ASSERT_EQUAL(BLOCK_SIZE, part2.get_program_size()); + TEST_ASSERT_EQUAL(BLOCK_SIZE, part2.get_erase_size()); + TEST_ASSERT_EQUAL((BLOCK_COUNT/2)*BLOCK_SIZE, part2.size()); + + // Deinit partitions + err = part1.deinit(); + TEST_ASSERT_EQUAL(0, err); + + err = part2.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + +// Testing mbr read write +void test_mbr_read_write() +{ + // Load partitions + MBRBlockDevice part1(&bd, 1); + int err = part1.init(); + TEST_ASSERT_EQUAL(0, err); + + MBRBlockDevice part2(&bd, 2); + err = part2.init(); + TEST_ASSERT_EQUAL(0, err); + + // Test reading/writing the partitions + uint8_t *write_block = new uint8_t[BLOCK_SIZE]; + uint8_t *read_block = new uint8_t[BLOCK_SIZE]; + + // Fill with random sequence + srand(1); + for (int i = 0; i < BLOCK_SIZE; i++) { + write_block[i] = 0xff & rand(); + } + + // Write, sync, and read the block + err = part1.erase(0, BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + err = part1.program(write_block, 0, BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + err = part1.read(read_block, 0, BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + // Check that the data was unmodified + srand(1); + for (int i = 0; i < BLOCK_SIZE; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]); + } + + // Check with original block device + err = bd.read(read_block, 1*BLOCK_SIZE, BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + // Check that the data was unmodified + srand(1); + for (int i = 0; i < BLOCK_SIZE; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]); + } + + // Test with second slice of block device + srand(1); + for (int i = 0; i < BLOCK_SIZE; i++) { + write_block[i] = 0xff & rand(); + } + + // Write, sync, and read the block + err = part2.erase(0, BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + err = part2.program(write_block, 0, BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + err = part2.read(read_block, 0, BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + // Check that the data was unmodified + srand(1); + for (int i = 0; i < BLOCK_SIZE; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]); + } + + // Check with original block device + err = bd.read(read_block, (BLOCK_COUNT/2)*BLOCK_SIZE, BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + // Check that the data was unmodified + srand(1); + for (int i = 0; i < BLOCK_SIZE; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]); + } + + // Clean up + delete[] write_block; + delete[] read_block; + + err = part1.deinit(); + TEST_ASSERT_EQUAL(0, err); + + err = part2.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(10, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Testing formatting of master boot record", test_mbr_format), + Case("Testing mbr attributes", test_mbr_attr), + Case("Testing mbr read write", test_mbr_read_write), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} diff --git a/features/TESTS/filesystem/multipart_fat_filesystem/main.cpp b/features/TESTS/filesystem/multipart_fat_filesystem/main.cpp new file mode 100644 index 00000000000..0b064a5acc9 --- /dev/null +++ b/features/TESTS/filesystem/multipart_fat_filesystem/main.cpp @@ -0,0 +1,183 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "HeapBlockDevice.h" +#include "FATFileSystem.h" +#include "MBRBlockDevice.h" +#include +#include "mbed_retarget.h" + +using namespace utest::v1; + +#ifndef MBED_EXTENDED_TESTS + #error [NOT_SUPPORTED] Filesystem tests not supported by default +#endif + +// Test block device +#define BLOCK_SIZE 512 +#define BLOCK_COUNT 512 +HeapBlockDevice bd(BLOCK_COUNT*BLOCK_SIZE, BLOCK_SIZE); + + +// Test formatting and partitioning +void test_format() { + // Create two partitions splitting device in ~half + int err = MBRBlockDevice::partition(&bd, 1, 0x83, 0, (BLOCK_COUNT/2)*BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + err = MBRBlockDevice::partition(&bd, 2, 0x83, -(BLOCK_COUNT/2)*BLOCK_SIZE); + TEST_ASSERT_EQUAL(0, err); + + // Load both partitions + MBRBlockDevice part1(&bd, 1); + err = part1.init(); + TEST_ASSERT_EQUAL(0, err); + + MBRBlockDevice part2(&bd, 2); + err = part2.init(); + TEST_ASSERT_EQUAL(0, err); + + // Format both partitions + err = FATFileSystem::format(&part1); + TEST_ASSERT_EQUAL(0, err); + + err = FATFileSystem::format(&part2); + TEST_ASSERT_EQUAL(0, err); + + // Unload the partitions + err = part1.deinit(); + TEST_ASSERT_EQUAL(0, err); + + err = part2.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + + +// Simple multipartition test for reading/writing files +template +void test_read_write() { + // Load both partitions + MBRBlockDevice part1(&bd, 1); + int err = part1.init(); + TEST_ASSERT_EQUAL(0, err); + + MBRBlockDevice part2(&bd, 2); + err = part2.init(); + TEST_ASSERT_EQUAL(0, err); + + // Create fat filesystems on both partitions + FATFileSystem fs1("fat1"); + FATFileSystem fs2("fat2"); + + err = fs1.mount(&part1); + TEST_ASSERT_EQUAL(0, err); + + err = fs2.mount(&part2); + TEST_ASSERT_EQUAL(0, err); + + uint8_t *buffer1 = (uint8_t *)malloc(TEST_SIZE); + TEST_ASSERT(buffer1); + + uint8_t *buffer2 = (uint8_t *)malloc(TEST_SIZE); + TEST_ASSERT(buffer2); + + // Fill with random sequence + srand(1); + + for (int i = 0; i < TEST_SIZE; i++) { + buffer1[i] = 0xff & rand(); + } + + for (int i = 0; i < TEST_SIZE; i++) { + buffer2[i] = 0xff & rand(); + } + + // write and read files on both partitions + File file; + err = file.open(&fs1, "test_read_write.dat", O_WRONLY | O_CREAT); + TEST_ASSERT_EQUAL(0, err); + ssize_t size = file.write(buffer1, TEST_SIZE); + TEST_ASSERT_EQUAL(TEST_SIZE, size); + err = file.close(); + TEST_ASSERT_EQUAL(0, err); + + err = file.open(&fs2, "test_read_write.dat", O_WRONLY | O_CREAT); + TEST_ASSERT_EQUAL(0, err); + size = file.write(buffer2, TEST_SIZE); + TEST_ASSERT_EQUAL(TEST_SIZE, size); + err = file.close(); + TEST_ASSERT_EQUAL(0, err); + + err = file.open(&fs1, "test_read_write.dat", O_RDONLY); + TEST_ASSERT_EQUAL(0, err); + size = file.read(buffer1, TEST_SIZE); + TEST_ASSERT_EQUAL(TEST_SIZE, size); + err = file.close(); + TEST_ASSERT_EQUAL(0, err); + + err = file.open(&fs2, "test_read_write.dat", O_RDONLY); + TEST_ASSERT_EQUAL(0, err); + size = file.read(buffer2, TEST_SIZE); + TEST_ASSERT_EQUAL(TEST_SIZE, size); + err = file.close(); + TEST_ASSERT_EQUAL(0, err); + + // Check that the data was unmodified + srand(1); + + for (int i = 0; i < TEST_SIZE; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), buffer1[i]); + } + + for (int i = 0; i < TEST_SIZE; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), buffer2[i]); + } + + err = fs1.unmount(); + TEST_ASSERT_EQUAL(0, err); + + err = fs2.unmount(); + TEST_ASSERT_EQUAL(0, err); + + err = part1.deinit(); + TEST_ASSERT_EQUAL(0, err); + + err = part2.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(10, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Testing formating", test_format), + Case("Testing read write < block", test_read_write), + Case("Testing read write > block", test_read_write<2*BLOCK_SIZE>), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} diff --git a/features/filesystem/bd/MBRBlockDevice.cpp b/features/filesystem/bd/MBRBlockDevice.cpp new file mode 100644 index 00000000000..ae095ed939a --- /dev/null +++ b/features/filesystem/bd/MBRBlockDevice.cpp @@ -0,0 +1,288 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MBRBlockDevice.h" +#include + + +// On disk structures, all entries are little endian +MBED_PACKED(struct) mbr_entry { + uint8_t status; + uint8_t chs_start[3]; + uint8_t type; + uint8_t chs_stop[3]; + uint32_t lba_offset; + uint32_t lba_size; +}; + +MBED_PACKED(struct) mbr_table { + struct mbr_entry entries[4]; + uint8_t signature[2]; +}; + +// Little-endian conversion, should compile to noop +// if system is little-endian +static inline uint32_t tole32(uint32_t a) +{ + union { + uint32_t u32; + uint8_t u8[4]; + } w; + + w.u8[0] = a >> 0; + w.u8[1] = a >> 8; + w.u8[2] = a >> 16; + w.u8[3] = a >> 24; + + return w.u32; +} + +static inline uint32_t fromle32(uint32_t a) +{ + return tole32(a); +} + +static void tochs(uint32_t lba, uint8_t chs[3]) +{ + uint32_t sector = std::min(lba, 0xfffffd)+1; + chs[0] = (sector >> 6) & 0xff; + chs[1] = ((sector >> 0) & 0x3f) | ((sector >> 16) & 0xc0); + chs[2] = (sector >> 14) & 0xff; +} + + +// Partition after address are turned into absolute +// addresses, assumes bd is initialized +static int partition_absolute( + BlockDevice *bd, int part, uint8_t type, + bd_size_t offset, bd_size_t size) +{ + // Allocate smallest buffer necessary to write MBR + uint32_t buffer_size = std::max(bd->get_program_size(), sizeof(struct mbr_table)); + uint8_t *buffer = new uint8_t[buffer_size]; + + // Check for existing MBR + int err = bd->read(buffer, 512-buffer_size, buffer_size); + if (err) { + delete[] buffer; + return err; + } + + struct mbr_table *table = reinterpret_cast( + &buffer[buffer_size - sizeof(struct mbr_table)]); + if (table->signature[0] != 0x55 || table->signature[1] != 0xaa) { + // Setup default values for MBR + table->signature[0] = 0x55; + table->signature[1] = 0xaa; + memset(table->entries, 0, sizeof(table->entries)); + } + + // Setup new partition + MBED_ASSERT(part >= 1 && part <= 4); + table->entries[part-1].status = 0x00; // inactive (not bootable) + table->entries[part-1].type = type; + + // lba dimensions + uint32_t sector = std::max(bd->get_erase_size(), 512); + uint32_t lba_offset = offset / sector; + uint32_t lba_size = size / sector; + table->entries[part-1].lba_offset = tole32(lba_offset); + table->entries[part-1].lba_size = tole32(lba_size); + + // chs dimensions + tochs(lba_offset, table->entries[part-1].chs_start); + tochs(lba_offset+lba_size-1, table->entries[part-1].chs_stop); + + // Write out MBR + err = bd->erase(0, bd->get_erase_size()); + if (err) { + delete[] buffer; + return err; + } + + err = bd->program(buffer, 512-buffer_size, buffer_size); + delete[] buffer; + return err; +} + +int MBRBlockDevice::partition(BlockDevice *bd, int part, uint8_t type, bd_addr_t start) +{ + int err = bd->init(); + if (err) { + return err; + } + + // Calculate dimensions + bd_size_t offset = ((int64_t)start < 0) ? -start : start; + bd_size_t size = bd->size(); + + if (offset < 512) { + offset += std::max(bd->get_erase_size(), 512); + } + + size -= offset; + + err = partition_absolute(bd, part, type, offset, size); + if (err) { + return err; + } + + err = bd->deinit(); + if (err) { + return err; + } + + return 0; +} + +int MBRBlockDevice::partition(BlockDevice *bd, int part, uint8_t type, + bd_addr_t start, bd_addr_t stop) +{ + int err = bd->init(); + if (err) { + return err; + } + + // Calculate dimensions + bd_size_t offset = ((int64_t)start < 0) ? -start : start; + bd_size_t size = ((int64_t)stop < 0) ? -stop : stop; + + if (offset < 512) { + offset += std::max(bd->get_erase_size(), 512); + } + + size -= offset; + + err = partition_absolute(bd, part, type, offset, size); + if (err) { + return err; + } + + err = bd->deinit(); + if (err) { + return err; + } + + return 0; +} + +MBRBlockDevice::MBRBlockDevice(BlockDevice *bd, int part) + : _bd(bd), _part(part) +{ + MBED_ASSERT(_part >= 1 && _part <= 4); +} + +int MBRBlockDevice::init() +{ + // Allocate smallest buffer necessary to write MBR + uint32_t buffer_size = std::max(_bd->get_read_size(), sizeof(struct mbr_table)); + uint8_t *buffer = new uint8_t[buffer_size]; + + int err = _bd->read(buffer, 512-buffer_size, buffer_size); + if (err) { + delete[] buffer; + return err; + } + + // Check for valid table + struct mbr_table *table = reinterpret_cast( + &buffer[buffer_size - sizeof(struct mbr_table)]); + if (table->signature[0] != 0x55 || table->signature[1] != 0xaa) { + delete[] buffer; + return BD_ERROR_INVALID_MBR; + } + + // Check for valid entry + if (table->entries[_part-1].type == 0x00) { + delete[] buffer; + return BD_ERROR_INVALID_PARTITION; + } + + // Get partition attributes + bd_size_t sector = std::max(_bd->get_erase_size(), 512); + _type = table->entries[_part-1].type; + _offset = fromle32(table->entries[_part-1].lba_offset) * sector; + _size = fromle32(table->entries[_part-1].lba_size) * sector; + + // Check that block addresses are valid + MBED_ASSERT(_bd->is_valid_erase(_offset, _size)); + + delete[] buffer; + return 0; +} + +int MBRBlockDevice::deinit() +{ + return _bd->deinit(); +} + +int MBRBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size) +{ + MBED_ASSERT(is_valid_read(addr, size)); + return _bd->read(b, addr + _offset, size); +} + +int MBRBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size) +{ + MBED_ASSERT(is_valid_program(addr, size)); + return _bd->program(b, addr + _offset, size); +} + +int MBRBlockDevice::erase(bd_addr_t addr, bd_size_t size) +{ + MBED_ASSERT(is_valid_erase(addr, size)); + return _bd->erase(addr + _offset, size); +} + +bd_size_t MBRBlockDevice::get_read_size() const +{ + return _bd->get_read_size(); +} + +bd_size_t MBRBlockDevice::get_program_size() const +{ + return _bd->get_program_size(); +} + +bd_size_t MBRBlockDevice::get_erase_size() const +{ + return _bd->get_erase_size(); +} + +bd_size_t MBRBlockDevice::size() const +{ + return _size; +} + +bd_size_t MBRBlockDevice::get_partition_start() const +{ + return _offset; +} + +bd_size_t MBRBlockDevice::get_partition_stop() const +{ + return _offset+_size; +} + +uint8_t MBRBlockDevice::get_partition_type() const +{ + return _type; +} + +int MBRBlockDevice::get_partition_number() const +{ + return _part; +} diff --git a/features/filesystem/bd/MBRBlockDevice.h b/features/filesystem/bd/MBRBlockDevice.h new file mode 100644 index 00000000000..48964fba1ce --- /dev/null +++ b/features/filesystem/bd/MBRBlockDevice.h @@ -0,0 +1,227 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MBED_MBR_BLOCK_DEVICE_H +#define MBED_MBR_BLOCK_DEVICE_H + +#include "BlockDevice.h" +#include "mbed.h" + + +/** Additional error codes used for MBR records + */ +enum { + BD_ERROR_INVALID_MBR = -3101, + BD_ERROR_INVALID_PARTITION = -3102, +}; + + +/** Block device for managing a Master Boot Record + * https://en.wikipedia.org/wiki/Master_boot_record + * + * Here is an example of partitioning a heap backed block device + * @code + * #include "mbed.h" + * #include "HeapBlockDevice.h" + * #include "MBRBlockDevice.h" + * + * // Create a block device with 64 blocks of size 512 + * HeapBlockDevice mem(64*512, 512); + * + * // Partition into two partitions with ~half the blocks + * MBRBlockDevice::partition(&mem, 1, 0x83, 0*512, 32*512); + * MBRBlockDevice::partition(&mem, 2, 0x83, 32*512); + * + * // Create a block device that maps to the first 32 blocks (excluding MBR block) + * MBRBlockDevice part1(&mem, 1); + * + * // Create a block device that maps to the last 32 blocks + * MBRBlockDevice part2(&mem, 2); + * @endcode + * + * Here is a more realistic example where the MBRBlockDevice is used + * to partition a region of space on an SD card. When plugged into a computer, + * the partitions will be recognized appropriately. + * @code + * #include "mbed.h" + * #include "SDBlockDevice.h" + * #include "MBRBlockDevice.h" + * #include "FATFileSystem.h" + * + * // Create an SD card + * SDBlockDevice sd(s0, s1, s2, s3); + * + * // Create a partition with 1 GB of space + * MBRBlockDevice::partition(&sd, 1, 0x83, 0, 1024*1024); + * + * // Create the block device that represents the partition + * MBRBlockDevice part1(&sd, 1); + * + * // Format the partition with a FAT filesystem + * FATFileSystem::format(&part1); + * + * // Create the FAT filesystem instance, files can now be written to + * // the FAT filesystem in partition 1 + * FATFileSystem fat("fat", &part1); + * @endcode + */ +class MBRBlockDevice : public BlockDevice +{ +public: + /** Format the MBR to contain the following partition + * + * @param bd Block device to partition + * @param part Partition to use, 1-4 + * @param type 8-bit partition type to identitfy partition's contents + * @param start Start block address to map to block 0 of partition, + * negative addresses are calculated from the end of the + * underlying block devices. Block 0 is implicitly ignored + * from the range to store the MBR. + * @return 0 on success or a negative error code on failure + * @note This is the same as partition(bd, part, type, start, bd->size()) + */ + static int partition(BlockDevice *bd, int part, uint8_t type, bd_addr_t start); + + /** Format the MBR to contain the following partition + * + * @param bd Block device to partition + * @param part Partition to use, 1-4 + * @param type 8-bit partition type to identitfy partition's contents + * @param start Start block address to map to block 0 of partition, + * negative addresses are calculated from the end of the + * underlying block devices. Block 0 is implicitly ignored + * from the range to store the MBR. + * @param stop End block address to mark the end of the partition, + * this block is not mapped, negative addresses are calculated + * from the end of the underlying block device. + * @return 0 on success or a negative error code on failure + */ + static int partition(BlockDevice *bd, int part, uint8_t type, bd_addr_t start, bd_addr_t stop); + + /** Lifetime of the block device + * + * @param bd Block device to back the MBRBlockDevice + * @param part Partition to use, 1-4 + */ + MBRBlockDevice(BlockDevice *bd, int part); + + /** Lifetime of the block device + */ + virtual ~MBRBlockDevice() {}; + + /** Initialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int deinit(); + + /** Read blocks from a block device + * + * @param buffer Buffer to read blocks into + * @param addr Address of block to begin reading from + * @param size Size to read in bytes, must be a multiple of read block size + * @return 0 on success, negative error code on failure + */ + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); + + /** Program blocks to a block device + * + * The blocks must have been erased prior to being programmed + * + * @param buffer Buffer of data to write to blocks + * @param addr Address of block to begin writing to + * @param size Size to write in bytes, must be a multiple of program block size + * @return 0 on success, negative error code on failure + */ + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); + + /** Erase blocks on a block device + * + * The state of an erased block is undefined until it has been programmed + * + * @param addr Address of block to begin erasing + * @param size Size to erase in bytes, must be a multiple of erase block size + * @return 0 on success, negative error code on failure + */ + virtual int erase(bd_addr_t addr, bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual bd_size_t get_read_size() const; + + /** Get the size of a programable block + * + * @return Size of a programable block in bytes + * @note Must be a multiple of the read size + */ + virtual bd_size_t get_program_size() const; + + /** Get the size of a eraseable block + * + * @return Size of a eraseable block in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size() const; + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size() const; + + /** Get the offset of the partition on the underlying block device + * @return Offset of the partition on the underlying device + */ + virtual bd_addr_t get_partition_start() const; + + /** Get size of partition on underlying block device + * @return Size of the partition on the underlying device + */ + virtual bd_addr_t get_partition_stop() const; + + /** Get 8-bit type of the partition + * @return 8-bit type of partition assigned during format + */ + virtual uint8_t get_partition_type() const; + + /** Get the partition number + * @return The partition number, 1-4 + */ + virtual int get_partition_number() const; + +protected: + BlockDevice *_bd; + bd_size_t _offset; + bd_size_t _size; + uint8_t _type; + uint8_t _part; +}; + + +#endif diff --git a/features/filesystem/fat/ChaN/ff.cpp b/features/filesystem/fat/ChaN/ff.cpp index 886ed2ec0d8..db660f7e830 100644 --- a/features/filesystem/fat/ChaN/ff.cpp +++ b/features/filesystem/fat/ChaN/ff.cpp @@ -4123,7 +4123,7 @@ FRESULT f_mkfs ( n_vol = LD_DWORD(tbl + 12); /* Volume size */ } else { /* Create a partition in this function */ - if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < ((sfd) ? 64 : 128)) return FR_DISK_ERR; b_vol = (sfd) ? 0 : 63; /* Volume start sector */ n_vol -= b_vol; /* Volume size */ diff --git a/features/filesystem/fat/ChaN/ffconf.h b/features/filesystem/fat/ChaN/ffconf.h index f8da280725a..b1e5a616210 100644 --- a/features/filesystem/fat/ChaN/ffconf.h +++ b/features/filesystem/fat/ChaN/ffconf.h @@ -140,7 +140,7 @@ / Drive/Volume Configurations /---------------------------------------------------------------------------*/ -#define _VOLUMES 1 +#define _VOLUMES 4 /* Number of volumes (logical drives) to be used. */ diff --git a/features/filesystem/fat/FATFileSystem.cpp b/features/filesystem/fat/FATFileSystem.cpp index ebecdaad34f..e9e369f845e 100644 --- a/features/filesystem/fat/FATFileSystem.cpp +++ b/features/filesystem/fat/FATFileSystem.cpp @@ -252,7 +252,8 @@ int FATFileSystem::mount(BlockDevice *bd, bool force) { _id = i; _ffs[_id] = bd; _fsid[0] = '0' + _id; - _fsid[1] = '\0'; + _fsid[1] = ':'; + _fsid[2] = '\0'; debug_if(FFS_DBG, "Mounting [%s] on ffs drive [%s]\n", getName(), _fsid); FRESULT res = f_mount(&_fs, _fsid, force); unlock(); @@ -290,7 +291,7 @@ int FATFileSystem::format(BlockDevice *bd, int allocation_unit) { // Logical drive number, Partitioning rule, Allocation unit size (bytes per cluster) fs.lock(); - FRESULT res = f_mkfs(fs._fsid, 0, allocation_unit); + FRESULT res = f_mkfs(fs._fsid, 1, allocation_unit); fs.unlock(); if (res != FR_OK) { return fat_error_remap(res); @@ -377,7 +378,9 @@ int FATFileSystem::file_open(fs_file_t *file, const char *path, int flags) { FIL *fh = new FIL; char *buffer = new char[strlen(_fsid) + strlen(path) + 3]; - sprintf(buffer, "%s:/%s", _fsid, path); + strcpy(buffer, _fsid); + strcat(buffer, "/"); + strcat(buffer, path); /* POSIX flags -> FatFS open mode */ BYTE openmode; diff --git a/features/filesystem/fat/FATFileSystem.h b/features/filesystem/fat/FATFileSystem.h index 1b39a795ceb..8bf6022e0e7 100644 --- a/features/filesystem/fat/FATFileSystem.h +++ b/features/filesystem/fat/FATFileSystem.h @@ -227,7 +227,7 @@ class FATFileSystem : public FileSystem { private: FATFS _fs; // Work area (file system object) for logical drive - char _fsid[2]; + char _fsid[sizeof("0:")]; int _id; protected: