diff options
author | thirdpin <n_lazareva@mail.ru> | 2016-07-19 18:52:24 +0300 |
---|---|---|
committer | thirdpin <n_lazareva@mail.ru> | 2016-07-19 18:52:24 +0300 |
commit | 6f994b871c9f673a4185a2fc6d824d8151d84359 (patch) | |
tree | 890356ed75c66576b274bbf925cfb2215bb6cd2a | |
parent | 26ba57fcb574c0ff313ff78210c2eed71a3b37b1 (diff) |
Add lib openfat, SST25 driver, dummy file system
34 files changed, 2672 insertions, 361 deletions
diff --git a/emb/pastilda/.cproject b/emb/pastilda/.cproject index 4047913..5b58df7 100644 --- a/emb/pastilda/.cproject +++ b/emb/pastilda/.cproject @@ -56,11 +56,13 @@ <option id="ilg.gnuarmeclipse.managedbuild.cross.option.c.compiler.include.paths.1095580488" name="Include paths (-I)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.c.compiler.include.paths" useByScannerDiscovery="false" valueType="includePath"> <listOptionValue builtIn="false" value=""../..\..\lib\libopencm3\include""/> <listOptionValue builtIn="false" value=""../..\..\lib\libopencm3\lib""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}}""/> <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/app}""/> - <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/hw}""/> - <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/hw/usb_device}""/> - <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/hw/usb_host}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/usb/usb_device}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/usb/usb_host}""/> <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/lib/libusbhost}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/lib/libopenfat/openfat}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/lib/libopenfat}""/> <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/lib/libopencm3_cpp_extensions}""/> </option> <inputType id="ilg.gnuarmeclipse.managedbuild.cross.tool.c.compiler.input.338584563" superClass="ilg.gnuarmeclipse.managedbuild.cross.tool.c.compiler.input"/> @@ -73,11 +75,15 @@ <option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.include.paths.1410012800" name="Include paths (-I)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.include.paths" useByScannerDiscovery="false" valueType="includePath"> <listOptionValue builtIn="false" value=""../..\..\lib\libopencm3\include""/> <listOptionValue builtIn="false" value=""../..\..\lib\libopencm3\lib""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/flash/drv}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/flash}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}}""/> <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/app}""/> - <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/hw}""/> - <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/hw/usb_device}""/> - <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/hw/usb_host}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/usb/usb_device}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/usb/usb_host}""/> <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/lib/libusbhost}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/lib/libopenfat}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/lib/libopenfat/openfat}""/> <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/lib/libopencm3_cpp_extensions}""/> </option> <inputType id="ilg.gnuarmeclipse.managedbuild.cross.tool.cpp.compiler.input.583307617" superClass="ilg.gnuarmeclipse.managedbuild.cross.tool.cpp.compiler.input"/> @@ -88,7 +94,7 @@ <tool id="ilg.gnuarmeclipse.managedbuild.cross.tool.cpp.linker.775878399" name="Cross ARM C++ Linker" superClass="ilg.gnuarmeclipse.managedbuild.cross.tool.cpp.linker"> <option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.linker.gcsections.631623800" name="Remove unused sections (-Xlinker --gc-sections)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.linker.gcsections" value="true" valueType="boolean"/> <option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.linker.scriptfile.1994171085" name="Script files (-T)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.linker.scriptfile" valueType="stringList"> - <listOptionValue builtIn="false" value="../stm32f407vg.ld"/> + <listOptionValue builtIn="false" value="../stm32f405rg.ld"/> </option> <option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.linker.nostart.933236374" name="Do not use standard start files (-nostartfiles)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.linker.nostart" value="true" valueType="boolean"/> <option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.linker.libs.730199662" name="Libraries (-l)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.linker.libs" valueType="libs"> diff --git a/emb/pastilda/app/app.cpp b/emb/pastilda/app/app.cpp index 68192be..8ea95f3 100644 --- a/emb/pastilda/app/app.cpp +++ b/emb/pastilda/app/app.cpp @@ -23,27 +23,63 @@ #include "stdio.h" using namespace Application; -#ifdef DEBUG - #define DEBUG_PRINT(x) printf(x) -#else - #define DEBUG_PRINT(x) do {} while (0) -#endif - App *app_pointer; App::App() { app_pointer = this; + clock_setup(); systick_init(); _leds_api = new LEDS_api(); - delay_ms(1000); - - usb_dispatcher = new USB_dispatcher(); + _flash = new FlashMemory(); + usb_host = new USB_host(redirect, control_interception); + usb_composite = new USB_composite(_flash->flash_blocks(), _flash->flash_read, _flash->flash_write); } void App::process() { _leds_api->toggle(); - usb_dispatcher->process(); + usb_host->poll(); +} + +void App::redirect(uint8_t *data, uint8_t len) +{ + app_pointer->usb_composite->usb_send_packet(data, len); +} + +void App::control_interception() +{ + memset(app_pointer->key, 0, 8); + app_pointer->key[2] = KEY_W; + app_pointer->key[3] = KEY_O; + app_pointer->key[4] = KEY_N; + app_pointer->key[5] = KEY_D; + app_pointer->key[6] = KEY_E; + app_pointer->key[7] = KEY_R; + app_pointer->usb_composite->usb_send_packet(app_pointer->key, 8); + + app_pointer->key[2] = 0; + app_pointer->key[3] = 0; + app_pointer->key[4] = 0; + app_pointer->key[5] = 0; + app_pointer->key[6] = 0; + app_pointer->key[7] = 0; + app_pointer->usb_composite->usb_send_packet(app_pointer->key, 8); + + app_pointer->key[2] = KEY_SPACEBAR; + app_pointer->key[3] = KEY_W; + app_pointer->key[4] = KEY_O; + app_pointer->key[5] = KEY_M; + app_pointer->key[6] = KEY_A; + app_pointer->key[7] = KEY_N; + app_pointer->usb_composite->usb_send_packet(app_pointer->key, 8); + + app_pointer->key[2] = 0; + app_pointer->key[3] = 0; + app_pointer->key[4] = 0; + app_pointer->key[5] = 0; + app_pointer->key[6] = 0; + app_pointer->key[7] = 0; + app_pointer->usb_composite->usb_send_packet(app_pointer->key, 8); } diff --git a/emb/pastilda/app/app.h b/emb/pastilda/app/app.h index de77511..6f21d59 100644 --- a/emb/pastilda/app/app.h +++ b/emb/pastilda/app/app.h @@ -23,12 +23,18 @@ #define APP_H #include <string.h> +#include <usb/usb_device/usbd_composite.h> +#include <usb/usb_host/usbh_host.h> #include "clock.h" #include "gpio_ext.h" #include "systick_ext.h" #include "leds.h" -#include "usb_dispatcher.h" -#include "spi_ext.h" +#include "flash_memory.h" + +extern "C" { +#include "keyboard.h" +} + using namespace LEDS_API; using namespace GPIO_CPP_Extension; @@ -42,9 +48,15 @@ namespace Application App(); void process(); + static void redirect(uint8_t *data, uint8_t len); + static void control_interception(); + private: LEDS_api *_leds_api; - USB_dispatcher *usb_dispatcher; + FlashMemory *_flash; + USB_composite *usb_composite; + USB_host *usb_host; + uint8_t key[8]; }; } #endif diff --git a/emb/pastilda/app/usb_dispatcher.cpp b/emb/pastilda/app/usb_dispatcher.cpp deleted file mode 100644 index 72614d3..0000000 --- a/emb/pastilda/app/usb_dispatcher.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of the pastilda project. - * hosted at http://github.com/thirdpin/pastilda - * - * Copyright (C) 2016 Third Pin LLC - * Written by Anastasiia Lazareva <a.lazareva@thirdpin.ru> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "usb_dispatcher.h" - -USB_dispatcher *dispatcher_pointer; - -USB_dispatcher::USB_dispatcher() -{ - dispatcher_pointer = this; - usb_composite = new USB_composite(); - usb_host = new USB_host(redirect, control_interception); - _interception_enabled = false; -} - -void USB_dispatcher::redirect(uint8_t *data, uint8_t len) -{ - dispatcher_pointer->usb_composite->usb_send_packet(data, len); -} - -void USB_dispatcher::control_interception() -{ - memset(dispatcher_pointer->key, 0, 8); - dispatcher_pointer->key[2] = KEY_W; - dispatcher_pointer->key[3] = KEY_O; - dispatcher_pointer->key[4] = KEY_N; - dispatcher_pointer->key[5] = KEY_D; - dispatcher_pointer->key[6] = KEY_E; - dispatcher_pointer->key[7] = KEY_R; - dispatcher_pointer->usb_composite->usb_send_packet(dispatcher_pointer->key, 8); - - dispatcher_pointer->key[2] = 0; - dispatcher_pointer->key[3] = 0; - dispatcher_pointer->key[4] = 0; - dispatcher_pointer->key[5] = 0; - dispatcher_pointer->key[6] = 0; - dispatcher_pointer->key[7] = 0; - dispatcher_pointer->usb_composite->usb_send_packet(dispatcher_pointer->key, 8); - - dispatcher_pointer->key[2] = KEY_SPACEBAR; - dispatcher_pointer->key[3] = KEY_W; - dispatcher_pointer->key[4] = KEY_O; - dispatcher_pointer->key[5] = KEY_M; - dispatcher_pointer->key[6] = KEY_A; - dispatcher_pointer->key[7] = KEY_N; - dispatcher_pointer->usb_composite->usb_send_packet(dispatcher_pointer->key, 8); - - dispatcher_pointer->key[2] = 0; - dispatcher_pointer->key[3] = 0; - dispatcher_pointer->key[4] = 0; - dispatcher_pointer->key[5] = 0; - dispatcher_pointer->key[6] = 0; - dispatcher_pointer->key[7] = 0; - dispatcher_pointer->usb_composite->usb_send_packet(dispatcher_pointer->key, 8); -} diff --git a/emb/pastilda/app/usb_dispatcher.h b/emb/pastilda/app/usb_dispatcher.h deleted file mode 100644 index d9d55a2..0000000 --- a/emb/pastilda/app/usb_dispatcher.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of the pastilda project. - * hosted at http://github.com/thirdpin/pastilda - * - * Copyright (C) 2016 Third Pin LLC - * Written by Anastasiia Lazareva <a.lazareva@thirdpin.ru> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef USB_DISPATCHER_H -#define USB_DISPATCHER_H - -#include "usbh_host.h" -#include "usbd_composite.h" -extern "C" -{ -#include "keyboard.h" -} - -class USB_dispatcher -{ -public: - USB_composite *usb_composite; - USB_host *usb_host; - uint8_t key[8]; - - USB_dispatcher(); - - static void redirect(uint8_t *data, uint8_t len); - static void control_interception(); - void do_work(); - void process() - { - usb_host->poll(); - } -private: - bool _interception_enabled; - - -}; - -#endif diff --git a/emb/pastilda/hw/clock.h b/emb/pastilda/clock.h index 57ea91a..57ea91a 100644 --- a/emb/pastilda/hw/clock.h +++ b/emb/pastilda/clock.h diff --git a/emb/pastilda/flash/drv/SST25.cpp b/emb/pastilda/flash/drv/SST25.cpp new file mode 100644 index 0000000..954188a --- /dev/null +++ b/emb/pastilda/flash/drv/SST25.cpp @@ -0,0 +1,240 @@ +/* + * This file is part of the pastilda project. + * hosted at http://github.com/thirdpin/pastilda + * + * Copyright (C) 2016 Third Pin LLC + * Written by Anastasiia Lazareva <a.lazareva@thirdpin.ru> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "SST25.h" + +SST25::SST25(SPI_ext *spi, Pinout nss_pin) +{ + _spi = spi; + + _nss = new GPIO_ext(nss_pin); + _nss->mode_setup(GPIO_CPP_Extension::Mode::OUTPUT, GPIO_CPP_Extension::PullMode::NO_PULL); + _nss->set_output_options(GPIO_CPP_Extension::OutputType::PUSH_PULL, GPIO_CPP_Extension::Speed::FAST_50MHz); + _nss->set(); +} + +int SST25::read_sectors(uint32_t sector, uint32_t count, void *buf) +{ + if(sector >= (SECTOR_COUNT)) { + return (0); + } + + uint32_t address = (sector * SECTOR_SIZE); + uint32_t data_len = SECTOR_SIZE * count; + + while ((address + data_len - 1) > (MEMORY_SIZE - 1)) { + count -= 1; + data_len = SECTOR_SIZE * count; + } + + read(address, data_len, (uint8_t*)buf); + return (count); +} +int SST25::write_sectors(uint32_t sector, uint32_t count, const void *buf) +{ + if(sector >= (SECTOR_COUNT)) { + return (0); + } + + uint32_t start_address = (sector * SECTOR_SIZE); + uint32_t data_len = SECTOR_SIZE * count; + + for(int i = 0; i < count; i++) + { + erase_sector(sector + i); + uint32_t address = ((sector + i) * SECTOR_SIZE); + + for (int j = 0; j < PAGE_COUNT_IN_SECTOR; j++) { + page_program(address, (uint8_t*)buf, PAGE_SIZE); + address += PAGE_SIZE; + buf += PAGE_SIZE; + } + } + return (count); +} +void SST25::disable_wtite_protection() +{ + enable_write_status_register(); + write_status_register(DISABLE_WRITE_PROTECTION); +} + +void SST25::read(uint32_t address, uint32_t count, uint8_t* buf) +{ + wait_write_complete(); + select_device(); + uint8_t tx[4] = {OPCODE_READ, ((address >> 16) & 0xFF), ((address >> 8) & 0xFF), (address & 0xFF)}; + write_buffer(tx, 4); + uint8_t rx[count]; + read_buffer(rx, count); + release_device(); + memcpy(buf, rx, count); + //return (rx); +} +void SST25::read_high_speed(uint32_t address, uint32_t count, uint8_t* buf) +{ + wait_write_complete(); + select_device(); + uint8_t tx[5] = {OPCODE_FAST_READ, ((address >> 16) & 0xFF), ((address >> 8) & 0xFF), (address & 0xFF), DUMMY_BYTE}; + write_buffer(tx, 5); + uint8_t rx[count]; + read_buffer(rx, count); + release_device(); + memcpy(buf, rx, count); + //return (rx); +} +void SST25::page_program(uint32_t address, uint8_t *data, uint16_t count) +{ + wait_write_complete(); + enable_write(); + select_device(); + uint16_t total_size = (4 + count); + uint8_t tx[total_size] = {OPCODE_PAGE_PROGRAM, ((address >> 16) & 0xFF), ((address >> 8) & 0xFF), (address & 0xFF)}; + for (int i = 0, j = 4; i < count; i++, j++) { + tx[j] = data[i]; + } + write_buffer(tx, total_size); + release_device(); +} +void SST25::erase_sector(uint32_t sector) +{ + wait_write_complete(); + enable_write(); + uint32_t address = (sector * SECTOR_SIZE); + select_device(); + uint8_t tx[4] = {OPCODE_SECTOR_ERASE, ((address >> 16) & 0xFF), ((address >> 8) & 0xFF), (address & 0xFF)}; + write_buffer(tx, 4); + release_device(); +} +void SST25::erase_block_32K(uint32_t start_sector) +{ + wait_write_complete(); + enable_write(); + uint32_t address = (start_sector * SECTOR_SIZE); + select_device(); + uint8_t tx[4] = {OPCODE_BLOCK_ERASE_32K, ((address >> 16) & 0xFF), ((address >> 8) & 0xFF), (address & 0xFF)}; + write_buffer(tx, 4); + release_device(); +} +void SST25::erase_block_64K(uint32_t start_sector) +{ + wait_write_complete(); + enable_write(); + uint32_t address = (start_sector * SECTOR_SIZE); + select_device(); + uint8_t tx[4] = {OPCODE_BLOCK_ERASE_64K, ((address >> 16) & 0xFF), ((address >> 8) & 0xFF), (address & 0xFF)}; + write_buffer(tx, 4); + release_device(); +} +void SST25::erase_full_chip() +{ + wait_write_complete(); + enable_write(); + select_device(); + _spi->send(OPCODE_CHIP_ERASE); + release_device(); +} +uint8_t SST25::read_status_register() +{ + select_device(); + uint8_t tx[1] = {OPCODE_RDSR}; + write_buffer(tx, 1); + uint8_t rx[1]; + read_buffer(rx, 1); + release_device(); + return (rx[0]); +} +void SST25::enable_write() +{ + wait_write_complete(); + select_device(); + _spi->send(OPCODE_WREN); + release_device(); +} +void SST25::disable_write() +{ + wait_write_complete(); + select_device(); + _spi->send(OPCODE_WRDI); + release_device(); +} +void SST25::enable_write_status_register() +{ + //wait_write_complete(); + select_device(); + _spi->send(OPCODE_EWSR); + release_device(); +} +void SST25::write_status_register(uint8_t data) +{ + //wait_write_complete(); + select_device(); + uint8_t tx[2] = {OPCODE_WRSR, data}; + write_buffer(tx, 2); + release_device(); +} +uint16_t SST25::read_id() +{ + select_device(); + uint8_t tx[4] = {OPCODE_RDID_ALT, 0, 0, 0}; + write_buffer(tx, 4); + uint8_t rx[2]; + read_buffer(rx, 2); + release_device(); + return ((rx[0] << 8) | rx[1]); +} +uint32_t SST25::read_jedec_id() +{ + select_device(); + uint8_t tx[1] = {OPCODE_JEDEC_ID}; + write_buffer(tx, 1); + uint8_t rx[3]; + read_buffer(rx, 3); + release_device(); + return ((rx[0] << 16) | (rx[1] << 8) | rx[3]); +} + +void SST25::select_device() +{ + _nss->clear(); + while(_spi->get_flag_status(Flag::BUSY_FLAG)); +} +void SST25::release_device() +{ + _nss->set(); +} +void SST25::write_buffer(const uint8_t *buf, int len) +{ + while(len--) + _spi->send(*buf++); +} +void SST25::read_buffer(uint8_t *buf, int len) +{ + uint8_t res = _spi->read(); + while(len--) + *buf++ = _spi->read(); +} +void SST25::wait_write_complete() +{ + uint8_t status = read_status_register(); + while (status & SST25_SR_BUSY) { + status = read_status_register(); + } +} diff --git a/emb/pastilda/flash/drv/SST25.h b/emb/pastilda/flash/drv/SST25.h new file mode 100644 index 0000000..3b38fb4 --- /dev/null +++ b/emb/pastilda/flash/drv/SST25.h @@ -0,0 +1,125 @@ +/* + * This file is part of the pastilda project. + * hosted at http://github.com/thirdpin/pastilda + * + * Copyright (C) 2016 Third Pin LLC + * Written by Anastasiia Lazareva <a.lazareva@thirdpin.ru> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef SST25_H +#define SST25_H + +#include <string.h> + +#include "spi_ext.h" +#include "gpio_ext.h" + +using namespace SPI_CPP_Extension; + +constexpr uint8_t OPCODE_READ = 0x03; /* Read data bytes 3 0 >=1 */ +constexpr uint8_t OPCODE_FAST_READ_DUAL_IO = 0xBB; +constexpr uint8_t OPCODE_FAST_READ_DUAL_OUTPUT = 0x3B; +constexpr uint8_t OPCODE_FAST_READ = 0x0B; /* Higher speed read 3 1 >=1 */ +constexpr uint8_t OPCODE_SECTOR_ERASE = 0x20; /* 4Kb Sector erase 3 0 0 */ +constexpr uint8_t OPCODE_BLOCK_ERASE_32K = 0x52; /* 32Kbit block Erase 3 0 0 */ +constexpr uint8_t OPCODE_BLOCK_ERASE_64K = 0xD8; /* 64Kbit block Erase 3 0 0 */ +constexpr uint8_t OPCODE_CHIP_ERASE = 0xC7; /* Chip erase 0 0 0 */ +constexpr uint8_t OPCODE_CHIP_ERASE_ALT = 0x60; /* Chip erase (alternate) 0 0 0 */ +constexpr uint8_t OPCODE_PAGE_PROGRAM = 0x02; /* Byte program 3 0 1 */ +constexpr uint8_t OPCODE_PAGE_PROGRAM_DUAL = 0xA2; /* Auto address increment 3 0 >=2 */ +constexpr uint8_t OPCODE_RDSR = 0x05; /* Read status register 0 0 >=1 */ +constexpr uint8_t OPCODE_EWSR = 0x50; /* Write enable status 0 0 0 */ +constexpr uint8_t OPCODE_WRSR = 0x01; /* Write Status Register 0 0 1 */ +constexpr uint8_t OPCODE_WREN = 0x06; /* Write Enable 0 0 0 */ +constexpr uint8_t OPCODE_WRDI = 0x04; /* Write Disable 0 0 0 */ +constexpr uint8_t OPCODE_RDID = 0xAB; /* Read Identification 0 0 >=1 */ +constexpr uint8_t OPCODE_RDID_ALT = 0x90; /* Read Identification (alt) 0 0 >=1 */ +constexpr uint8_t OPCODE_JEDEC_ID = 0x9F; /* JEDEC ID read 0 0 >=3 */ +constexpr uint8_t OPCODE_EHLD = 0xAA; +constexpr uint8_t OPCODE_READ_SID = 0x88; +constexpr uint8_t OPCODE_PROGRAM_SID = 0xA5; +constexpr uint8_t OPCODE_LOCKOUT_SID = 0x85; + +constexpr uint8_t SST25_MANUFACTURER = 0xBF; /* SST manufacturer ID */ +constexpr uint8_t SST25_DEVICE_ID = 0x4B; /* SSTVF032B device ID */ + +constexpr uint8_t SST25_JEDEC_MANUFACTURER = 0xBF; /* SST manufacturer ID */ +constexpr uint8_t SST25_JEDEC_MEMORY_TYPE = 0x25; /* SST25 memory type */ +constexpr uint8_t SST25_JEDEC_MEMORY_CAPACITY = 0x4B; /* SST25VF032B memory capacity */ + +constexpr uint8_t SST25_SR_BUSY = (0x1 << 0); /* Bit 0: Write in progress */ +constexpr uint8_t SST25_SR_WEL = (0x1 << 1); /* Bit 1: Write enable latch bit */ +constexpr uint8_t SST25_SR_BP_MASK = (0xF << 2); +constexpr uint8_t SST25_SR_BP_NONE = (0x0 << 2);/* Unprotected */ +constexpr uint8_t SST25_SR_BP_UPPER128 = (0x1 << 2); /* Upper 64th */ +constexpr uint8_t SST25_SR_BP_UPPER64 = (0x2 << 2); /* Upper 32nd */ +constexpr uint8_t SST25_SR_BP_UPPER32 = (0x3 << 2); /* Upper 16th */ +constexpr uint8_t SST25_SR_BP_UPPER16 = (0x4 << 2); /* Upper 8th */ +constexpr uint8_t SST25_SR_BP_UPPER8 = (0x5 << 2); /* Upper quarter */ +constexpr uint8_t SST25_SR_BP_UPPER4 = (0x6 << 2); /* Upper half */ +constexpr uint8_t SST25_SR_BP_UPPER2 = (0x7 << 2); /* All sectors */ +constexpr uint8_t SST25_SR_BP_ALL = (0x8 << 2); /* All sectors */ +constexpr uint8_t SST25_SR_SEC = (0x1 << 6); /* Bit 6: Auto Address increment programming */ +constexpr uint8_t SST25_SR_BPL = (0x1 << 7); /* Bit 7: Status register write protect */ + +constexpr uint8_t SST25_ERASED_STATE = 0xFF; /* State of FLASH when erased */ +constexpr uint8_t DUMMY_BYTE = 0xFE; + +constexpr uint16_t DISABLE_WRITE_PROTECTION = 0x00; +constexpr uint16_t SECTOR_SIZE = 4096; +constexpr uint32_t MEMORY_SIZE = 0x800000; +constexpr uint16_t SECTOR_COUNT = MEMORY_SIZE / SECTOR_SIZE; +constexpr uint16_t PAGE_SIZE = 256; +constexpr uint16_t PAGE_COUNT_IN_SECTOR = 16; + + +class SST25 +{ +public: + SST25(SPI_ext *spi, Pinout nss_pin); + int read_sectors(uint32_t sector, uint32_t count, void *buf); + int write_sectors(uint32_t sector, uint32_t count, const void *buf); + void disable_wtite_protection(); + + void read(uint32_t address, uint32_t count, uint8_t* buf); + void read_high_speed(uint32_t address, uint32_t count, uint8_t* buf); + void page_program(uint32_t address, uint8_t *data, uint16_t count); + void erase_sector(uint32_t sector); + void erase_block_32K(uint32_t start_sector); + void erase_block_64K(uint32_t start_sector); + void erase_full_chip(); + ///TODO: 3 SID functions + uint8_t read_status_register(); + void enable_write(); + void disable_write(); + void enable_write_status_register(); + void write_status_register(uint8_t data); + ///TODO: enable HOLD + uint16_t read_id(); + uint32_t read_jedec_id(); + +private: + SPI_ext *_spi; + GPIO_ext *_nss; + + void select_device(); + void release_device(); + void write_buffer(const uint8_t *buf, int len); + void read_buffer(uint8_t *buf, int len); + void wait_write_complete(); +}; + +#endif diff --git a/emb/pastilda/flash/flash_memory.cpp b/emb/pastilda/flash/flash_memory.cpp new file mode 100644 index 0000000..2afa1d8 --- /dev/null +++ b/emb/pastilda/flash/flash_memory.cpp @@ -0,0 +1,218 @@ +/* + * This file is part of the pastilda project. + * hosted at http://github.com/thirdpin/pastilda + * + * Copyright (C) 2016 Third Pin LLC + * Written by Anastasiia Lazareva <a.lazareva@thirdpin.ru> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "flash_memory.h" + +FlashMemory *flash_pointer; + +FlashMemory::FlashMemory() +{ + flash_pointer = this; + + SPI_CPP_Extension::SPI_Conf sst25_conf = { 1, PA7, PA6, PA5 }; + _spi = new SPI_ext(sst25_conf); + + _spi->reset(); + _spi->disable(); + _spi->set_master_mode(); + _spi->set_baudrate_prescaler(BAUDRATE_FPCLK_DIV_2); + _spi->set_standard_mode(MODE_0); + _spi->set_data_drame_format(DFF_8BIT); + _spi->set_bit_position(MSB_FIRST); + _spi->enable_ss_output(); + _spi->set_software_slave_management(State::ENABLE); + _spi->set_nss(HIGH); + _spi->enable(); + + _sst25 = new SST25(_spi, PA4); + _sst25->disable_wtite_protection(); + + FatState state = _get_fat_state(); + if (state == FatState::FAT_ERROR) { + _set_fat_system_region(); + } +} + +uint16_t FlashMemory::get_sector_size(const struct block_device *dev) +{ + (void)dev; + return (FAKE_SECTOR_SIZE); +} + +int FlashMemory::read_sectors(const struct block_device *dev, uint32_t sector, uint32_t count, void *buf) +{ + (void)dev; + uint32_t end_sector = sector + count - 1; + if (end_sector >= FAKE_SECTOR_COUNT) { + return (0); + } + + uint32_t real_start_sector = sector / 8; + uint32_t real_end_sector = end_sector / 8; + uint32_t real_sector_count = real_end_sector - real_start_sector + 1; + uint32_t real_start_sector_part = (sector % 8); + + uint32_t data_size = real_sector_count * SECTOR_SIZE; + uint8_t data[data_size]; + + flash_pointer->_sst25->read_sectors(real_start_sector, real_sector_count, data); + + uint32_t start_address = real_start_sector_part * FAKE_SECTOR_SIZE; + uint32_t end_address = start_address + (count * FAKE_SECTOR_SIZE); + + for (int i = start_address, j = 0; i < end_address; i++, j++) { + ((uint8_t*)buf)[j] = data[i]; + } + + return (count); +} + +int FlashMemory::write_sectors(const struct block_device *dev, uint32_t sector, uint32_t count, const void *buf) +{ + (void)dev; + uint32_t end_sector = (sector + count - 1); + if (end_sector >= FAKE_SECTOR_COUNT) { + return (0); + } + + uint32_t real_start_sector = sector / 8; + uint32_t real_end_sector = end_sector / 8; + uint32_t real_sector_count = real_end_sector - real_start_sector + 1; + uint32_t real_start_sector_part = (sector % 8); + + uint32_t copy_size = real_sector_count * SECTOR_SIZE; + uint8_t sector_copy[copy_size]; + + flash_pointer->_sst25->read_sectors(real_start_sector, real_sector_count, sector_copy); + + uint32_t start_address = real_start_sector_part * FAKE_SECTOR_SIZE; + uint32_t end_address = start_address + (count * FAKE_SECTOR_SIZE); + + for (int i = start_address, j = 0; i < end_address; i++, j++) { + sector_copy[i] = ((uint8_t*)buf)[j]; + } + + flash_pointer->_sst25->write_sectors(real_start_sector, real_sector_count, sector_copy); + return (count); +} + +int FlashMemory::flash_read(uint32_t lba, uint8_t *copy_to) +{ + memset(copy_to, 0, FAKE_SECTOR_SIZE); + if (lba >= FAKE_SECTOR_COUNT) { + return (1); + } + else { + read_sectors(0, lba, 1, copy_to); + return (0); + } +} + +void FlashMemory::erase_chip() +{ + _sst25->erase_full_chip(); +} +int FlashMemory::flash_write(uint32_t lba, const uint8_t *copy_from) +{ + if (lba >= FAKE_SECTOR_COUNT) { + return (1); + } + else { + write_sectors(0, lba, 1, copy_from); + return (0); + } +} +int FlashMemory::flash_blocks(void) +{ + return (FAKE_SECTOR_COUNT); +} +FatState FlashMemory::_get_fat_state() +{ + uint8_t buf[2]; + _sst25->read(510, 2, buf); + uint16_t signature = ((buf[0] << 8) | buf[1]); + if (signature == BOOT_SIGNATURE) { + return (FatState::FAT_READY); + } + + return (FatState::FAT_ERROR); +} +void FlashMemory::_set_fat_system_region() +{ + _sst25->erase_full_chip(); + _set_boot_region(); + _set_fat_region(); + _set_root_dir_region(); +} + +void FlashMemory::_set_boot_region() +{ + uint8_t copy_to[BOOT_SIZE]; + memset(copy_to, 0, BOOT_SIZE); + + uint8_t BootSector[] = { + 0xEB, 0x58, 0x90, // code to jump to the bootstrap code + 0x57, 0x49, 0x4E, 0x49, 0x4D, 0x41, 0x47, 0x45, // OEM ID + 0x00, 0x02, // bytes per sector + 0x08, // sectors per cluster (0x04) + 0x01, 0x00, // # of reserved sectors (1 boot sector) + 0x02, // FAT copies (2) + 0x00, 0x02, // root entries (512) + 0x00, 0x40, // total number of sectors + 0xF8, // media descriptor (0xF8 = Fixed disk) + 0x08, 0x00, // sectors per FAT (16) + 0x00, 0x00, // sectors per track (0) + 0x00, 0x00, // number of heads (0) + 0x00, 0x00, 0x00, 0x00, // hidden sectors (0) + 0x00, 0x00, 0x00, 0x00, // large number of sectors (0) + 0x80, // drive number (0) + 0x00, // reserved + 0x29, // extended boot signature + 0x10, 0x5C, 0xD1, 0x34, // volume serial number + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // volume label + 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20 // filesystem type + }; + + memcpy(copy_to, BootSector, sizeof(BootSector)); + copy_to[BOOT_SIZE - 2] = 0x55; + copy_to[BOOT_SIZE - 1] = 0xAA; + + write_sectors(0, BOOT_SECTOR, BOOT_SECTOR_COUNT, copy_to); +} +void FlashMemory::_set_fat_region() +{ + uint8_t copy_to[FAT_SIZE]; + + memset(copy_to, 0, FAT_SIZE); + copy_to[0] = 0xF8; + copy_to[1] = 0xFF; + copy_to[2] = 0xFF; + copy_to[3] = 0xFF; + + write_sectors(0, FAT1_SECTOR, FAT_SECTOR_COUNT, copy_to); + write_sectors(0, FAT2_SECTOR, FAT_SECTOR_COUNT, copy_to); +} +void FlashMemory::_set_root_dir_region() +{ + uint8_t copy_to[ROOT_SIZE]; + memset(copy_to, 0, ROOT_SIZE); + write_sectors(0, 17, ROOT_SECTOR_COUNT, copy_to); +} diff --git a/emb/pastilda/flash/flash_memory.h b/emb/pastilda/flash/flash_memory.h new file mode 100644 index 0000000..681ae22 --- /dev/null +++ b/emb/pastilda/flash/flash_memory.h @@ -0,0 +1,102 @@ +/* + * This file is part of the pastilda project. + * hosted at http://github.com/thirdpin/pastilda + * + * Copyright (C) 2016 Third Pin LLC + * Written by Anastasiia Lazareva <a.lazareva@thirdpin.ru> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef FLASH_MEMORY_H +#define FLASH_MEMORY_H + +#include <string.h> + +extern "C" +{ +#include "blockdev.h" +#include "openfat.h" +#include "mbr.h" +} + +#include "spi_ext.h" +#include "SST25.h" + +using namespace SPI_CPP_Extension; + +#define WBVAL(x) ((x) & 0xFF), (((x) >> 8) & 0xFF) +#define QBVAL(x) ((x) & 0xFF), (((x) >> 8) & 0xFF), (((x) >> 16) & 0xFF), (((x) >> 24) & 0xFF) + +constexpr uint16_t FAKE_SECTOR_SIZE = 512; +constexpr uint16_t FAKE_SECTOR_COUNT = 16384; //(MEMORY_SIZE / FAKE_SECTOR_SIZE); +constexpr uint16_t BOOT_SIGNATURE = 0x55AA; + +constexpr uint16_t BYTES_PER_SECTOR = 512; +constexpr uint16_t SECTORS_PER_CLUSTER = 4; +constexpr uint16_t RESERVED_SECTORS = 1; +constexpr uint16_t FAT_COPIES = 2; +constexpr uint16_t ROOT_ENTRIES = 512; + +constexpr uint16_t BOOT_SIZE = 512; +constexpr uint16_t BOOT_SECTOR = 0; +constexpr uint16_t BOOT_SECTOR_COUNT = 1; + +constexpr uint16_t FAT_SIZE = 4096; +constexpr uint16_t FAT_SECTOR_COUNT = 8; +constexpr uint16_t FAT1_SECTOR = 1; +constexpr uint16_t FAT2_SECTOR = 1 + FAT_SECTOR_COUNT; + +constexpr uint16_t ROOT_SIZE = 16384; +constexpr uint16_t ROOT_SECTOR_COUNT = 32; + +typedef enum { + FAT_READY, + FAT_ERROR +}FatState; + +class FlashMemory +{ +public: + FlashMemory(); + static uint16_t get_sector_size(const struct block_device *dev); + static int read_sectors(const struct block_device *dev, uint32_t sector, uint32_t count, void *buf); + static int write_sectors(const struct block_device *dev, uint32_t sector, uint32_t count, const void *buf); + + void erase_chip(); + + static int flash_read(uint32_t lba, uint8_t *copy_to); + static int flash_write(uint32_t lba, const uint8_t *copy_from); + static int flash_blocks(void); + + void print_tree(); + +private: + SST25 *_sst25; + SPI_ext *_spi; + + struct block_device dev; + struct block_mbr_partition part; + struct fat_vol_handle vol; + struct fat_file_handle dir; + struct fat_file_handle file; + + FatState _get_fat_state(); + void _set_fat_system_region(); + void _set_boot_region(); + void _set_fat_region(); + void _set_root_dir_region(); +}; + +#endif diff --git a/emb/pastilda/hw/usb_device/ramdisk.c b/emb/pastilda/hw/usb_device/ramdisk.c deleted file mode 100644 index 15ea8a1..0000000 --- a/emb/pastilda/hw/usb_device/ramdisk.c +++ /dev/null @@ -1,160 +0,0 @@ -#include <string.h> -#include "ramdisk.h" - -#define WBVAL(x) ((x) & 0xFF), (((x) >> 8) & 0xFF) -#define QBVAL(x) ((x) & 0xFF), (((x) >> 8) & 0xFF),\ - (((x) >> 16) & 0xFF), (((x) >> 24) & 0xFF) - -// filesystem size is 512kB (1024 * SECTOR_SIZE) -#define SECTOR_COUNT 1024 -#define SECTOR_SIZE 512 -#define BYTES_PER_SECTOR 512 -#define SECTORS_PER_CLUSTER 4 -#define RESERVED_SECTORS 1 -#define FAT_COPIES 2 -#define ROOT_ENTRIES 512 -#define ROOT_ENTRY_LENGTH 32 -#define FILEDATA_START_CLUSTER 3 -#define DATA_REGION_SECTOR (RESERVED_SECTORS + FAT_COPIES + \ - (ROOT_ENTRIES * ROOT_ENTRY_LENGTH) / BYTES_PER_SECTOR) -#define FILEDATA_START_SECTOR (DATA_REGION_SECTOR + \ - (FILEDATA_START_CLUSTER - 2) * SECTORS_PER_CLUSTER) - -// filesize is 64kB (128 * SECTOR_SIZE) -#define FILEDATA_SECTOR_COUNT 128 - -uint8_t BootSector[] = { - 0xEB, 0x3C, 0x90, // code to jump to the bootstrap code - 'm', 'k', 'd', 'o', 's', 'f', 's', 0x00, // OEM ID - WBVAL(BYTES_PER_SECTOR), // bytes per sector - SECTORS_PER_CLUSTER, // sectors per cluster - WBVAL(RESERVED_SECTORS), // # of reserved sectors (1 boot sector) - FAT_COPIES, // FAT copies (2) - WBVAL(ROOT_ENTRIES), // root entries (512) - WBVAL(SECTOR_COUNT), // total number of sectors - 0xF8, // media descriptor (0xF8 = Fixed disk) - 0x01, 0x00, // sectors per FAT (1) - 0x20, 0x00, // sectors per track (32) - 0x40, 0x00, // number of heads (64) - 0x00, 0x00, 0x00, 0x00, // hidden sectors (0) - 0x00, 0x00, 0x00, 0x00, // large number of sectors (0) - 0x00, // drive number (0) - 0x00, // reserved - 0x29, // extended boot signature - 0x69, 0x17, 0xAD, 0x53, // volume serial number - 'R', 'A', 'M', 'D', 'I', 'S', 'K', ' ', ' ', ' ', ' ', // volume label - 'F', 'A', 'T', '1', '2', ' ', ' ', ' ' // filesystem type -}; - -uint8_t FatSector[] = { - 0xF8, 0xFF, 0xFF, 0x00, 0x40, 0x00, 0x05, 0x60, 0x00, 0x07, 0x80, 0x00, - 0x09, 0xA0, 0x00, 0x0B, 0xC0, 0x00, 0x0D, 0xE0, 0x00, 0x0F, 0x00, 0x01, - 0x11, 0x20, 0x01, 0x13, 0x40, 0x01, 0x15, 0x60, 0x01, 0x17, 0x80, 0x01, - 0x19, 0xA0, 0x01, 0x1B, 0xC0, 0x01, 0x1D, 0xE0, 0x01, 0x1F, 0x00, 0x02, - 0x21, 0x20, 0x02, 0x23, 0x40, 0x02, 0x25, 0x60, 0x02, 0x27, 0x80, 0x02, - 0x29, 0xA0, 0x02, 0x2B, 0xC0, 0x02, 0x2D, 0xE0, 0x02, 0x2F, 0x00, 0x03, - 0x31, 0x20, 0x03, 0x33, 0x40, 0x03, 0x35, 0x60, 0x03, 0x37, 0x80, 0x03, - 0x39, 0xA0, 0x03, 0x3B, 0xC0, 0x03, 0x3D, 0xE0, 0x03, 0x3F, 0x00, 0x04, - 0x41, 0x20, 0x04, 0x43, 0x40, 0x04, 0x45, 0x60, 0x04, 0x47, 0x80, 0x04, - 0x49, 0xA0, 0x04, 0x4B, 0xC0, 0x04, 0x4D, 0xE0, 0x04, 0x4F, 0x00, 0x05, - 0x51, 0x20, 0x05, 0x53, 0x40, 0x05, 0x55, 0x60, 0x05, 0x57, 0x80, 0x05, - 0x59, 0xA0, 0x05, 0x5B, 0xC0, 0x05, 0x5D, 0xE0, 0x05, 0x5F, 0x00, 0x06, - 0x61, 0x20, 0x06, 0x63, 0x40, 0x06, 0x65, 0x60, 0x06, 0x67, 0x80, 0x06, - 0x69, 0xA0, 0x06, 0x6B, 0xC0, 0x06, 0x6D, 0xE0, 0x06, 0x6F, 0x00, 0x07, - 0x71, 0x20, 0x07, 0x73, 0x40, 0x07, 0x75, 0x60, 0x07, 0x77, 0x80, 0x07, - 0x79, 0xA0, 0x07, 0x7B, 0xC0, 0x07, 0x7D, 0xE0, 0x07, 0x7F, 0x00, 0x08, - 0x81, 0x20, 0x08, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; - -uint8_t DirSector[] = { - // long filename entry - 0x41, // sequence number - WBVAL('r'), WBVAL('a'), WBVAL('m'), WBVAL('d'), WBVAL('i'), // five name characters in UTF-16 - 0x0F, // attributes - 0x00, // type - 0x00, // checksum of DOS filename (computed in ramdisk_init) - WBVAL('s'), WBVAL('k'), WBVAL('.'), WBVAL('d'), WBVAL('a'), WBVAL('t'), // six name characters in UTF-16 - 0x00, 0x00, // first cluster - WBVAL(0), WBVAL(0), // two name characters in UTF-16 - // actual entry - 'R', 'A', 'M', 'D', 'I', 'S', 'K', ' ', // filename - 'D', 'A', 'T', // extension - 0x20, // attribute byte - 0x00, // reserved for Windows NT - 0x00, // creation millisecond - 0xCE, 0x01, // creation time - 0x86, 0x41, // creation date - 0x86, 0x41, // last access date - 0x00, 0x00, // reserved for FAT32 - 0xCE, 0x01, // last write time - 0x86, 0x41, // last write date - WBVAL(FILEDATA_START_CLUSTER), // start cluster - QBVAL(FILEDATA_SECTOR_COUNT * SECTOR_SIZE) // file size in bytes -}; - -static uint8_t ramdata[FILEDATA_SECTOR_COUNT * SECTOR_SIZE]; - -int ramdisk_init(void) -{ - uint32_t i = 0; - - // compute checksum in the directory entry - uint8_t chk = 0; - for (i = 32; i < 43; i++) { - chk = (((chk & 1) << 7) | ((chk & 0xFE) >> 1)) + DirSector[i]; - } - DirSector[13] = chk; - - // fill ramdata - const uint8_t text[] = "USB Mass Storage Class example. "; - i = 0; - while (i < sizeof(ramdata)) { - ramdata[i] = text[i % (sizeof(text) -1)]; - i++; - } - return (0); -} - -int ramdisk_read(uint32_t lba, uint8_t *copy_to) -{ - memset(copy_to, 0, SECTOR_SIZE); - switch (lba) { - case 0: // sector 0 is the boot sector - memcpy(copy_to, BootSector, sizeof(BootSector)); - copy_to[SECTOR_SIZE - 2] = 0x55; - copy_to[SECTOR_SIZE - 1] = 0xAA; - break; - case 1: // sector 1 is FAT 1st copy - case 2: // sector 2 is FAT 2nd copy - memcpy(copy_to, FatSector, sizeof(FatSector)); - break; - case 3: // sector 3 is the directory entry - memcpy(copy_to, DirSector, sizeof(DirSector)); - break; - default: - // ignore reads outside of the data section - if (lba >= FILEDATA_START_SECTOR && lba < FILEDATA_START_SECTOR + FILEDATA_SECTOR_COUNT) { - memcpy(copy_to, ramdata + (lba - FILEDATA_START_SECTOR) * SECTOR_SIZE, SECTOR_SIZE); - } - break; - } - return (0); -} - -int ramdisk_write(uint32_t lba, const uint8_t *copy_from) -{ - (void)lba; - (void)copy_from; - // ignore writes - return (0); -} - -int ramdisk_blocks(void) -{ - return (SECTOR_COUNT); -} diff --git a/emb/pastilda/hw/usb_device/ramdisk.h b/emb/pastilda/hw/usb_device/ramdisk.h deleted file mode 100644 index 852dc92..0000000 --- a/emb/pastilda/hw/usb_device/ramdisk.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef RAMDISK_H -#define RAMDISK_H - -#include <stdint.h> -#include <libopencm3/cm3/common.h> - -BEGIN_DECLS -extern int ramdisk_init(void); -extern int ramdisk_read(uint32_t lba, uint8_t *copy_to); -extern int ramdisk_write(uint32_t lba, const uint8_t *copy_from); -extern int ramdisk_blocks(void); -END_DECLS - -#endif diff --git a/emb/pastilda/hw/keyboard.h b/emb/pastilda/keyboard.h index 7a59e41..7a59e41 100644 --- a/emb/pastilda/hw/keyboard.h +++ b/emb/pastilda/keyboard.h diff --git a/emb/pastilda/lib/libopenfat/bpb.h b/emb/pastilda/lib/libopenfat/bpb.h new file mode 100644 index 0000000..6593f14 --- /dev/null +++ b/emb/pastilda/lib/libopenfat/bpb.h @@ -0,0 +1,146 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Boot Sector / BIOS Parameter Block definitions. + * Convenience functions for calculations. + */ + +#ifndef __BPB_H +#define __BPB_H + +#include <stdint.h> +#include "openfat/leaccess.h" + +/* Boot sector fields common to FAT12/FAT16/FAT32 */ +struct bpb_common { + uint8_t boot_jmp[3]; + char oem_name[8]; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sector_count; + uint8_t num_fats; + uint16_t root_entry_count; + uint16_t total_sectors_16; + uint8_t media; + uint16_t fat_size_16; + uint16_t sectors_per_track; + uint16_t num_heads; + uint32_t hidden_sectors; + uint32_t total_sectors_32; +} __attribute__((packed)); + +/* Boot sector fields only in FAT12/FAT16 */ +struct bpb_fat12_16 { + struct bpb_common common; + uint8_t drive_num; + uint8_t Reserved1; + uint8_t boot_sig; + uint32_t volume_id; + char volume_label[11]; + char fs_type[8]; +} __attribute__((packed)); + +/* Boot sector fields only in FAT32 */ +struct bpb_fat32 { + struct bpb_common common; + uint32_t fat_size_32; + uint16_t ext_flags; + uint16_t fs_version; + uint32_t root_cluster; + uint16_t fs_info; + uint16_t bk_boot_sec; + uint8_t Reserved[12]; + uint8_t drive_num; + uint8_t Reserved1; + uint8_t boot_sig; + uint32_t volume_id; + char volume_label[11]; + char fs_type[8]; +} __attribute__((packed)); + + +static inline uint32_t +_bpb_root_dir_sectors(struct bpb_common *bpb) +{ + return ((__get_le16(&bpb->root_entry_count) * 32) + + (__get_le16(&bpb->bytes_per_sector) - 1)) / + __get_le16(&bpb->bytes_per_sector); +} + +static inline uint32_t +_bpb_fat_size(struct bpb_common *bpb) +{ + uint32_t fat_size = __get_le16(&bpb->fat_size_16); + if(fat_size == 0) + fat_size = __get_le32(&((struct bpb_fat32 *)bpb)->fat_size_32); + + return fat_size; +} + +static inline uint32_t +_bpb_first_data_sector(struct bpb_common *bpb) +{ + return __get_le16(&bpb->reserved_sector_count) + + (bpb->num_fats * _bpb_fat_size(bpb)) + + _bpb_root_dir_sectors(bpb); +} + +static inline uint32_t +_bpb_first_sector_of_cluster(struct bpb_common *bpb, uint32_t n) +{ + return ((n - 2) * bpb->sectors_per_cluster) + + _bpb_first_data_sector(bpb); +} + +enum fat_type { + FAT_TYPE_FAT12 = 12, + FAT_TYPE_FAT16 = 16, + FAT_TYPE_FAT32 = 32, +}; + +static inline uint32_t _bpb_cluster_count(struct bpb_common *bpb) +{ + uint32_t tot_sec = __get_le16(&bpb->total_sectors_16); + if(tot_sec == 0) + tot_sec = __get_le32(&bpb->total_sectors_32); + + uint32_t data_sec = tot_sec - + __get_le16(&bpb->reserved_sector_count) - + (bpb->num_fats * _bpb_fat_size(bpb)) - + _bpb_root_dir_sectors(bpb); + + return data_sec / bpb->sectors_per_cluster; +} + +/* FAT type is determined by count of clusters */ +static inline enum fat_type +fat_type(struct bpb_common *bpb) +{ + uint32_t cluster_count = _bpb_cluster_count(bpb); + if(cluster_count < 4085) { + return FAT_TYPE_FAT12; + } else if(cluster_count < 65525) { + return FAT_TYPE_FAT16; + } + return FAT_TYPE_FAT32; +} + +#endif + diff --git a/emb/pastilda/lib/libopenfat/direntry.c b/emb/pastilda/lib/libopenfat/direntry.c new file mode 100644 index 0000000..46dfc35 --- /dev/null +++ b/emb/pastilda/lib/libopenfat/direntry.c @@ -0,0 +1,237 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* FAT Directory . + */ + +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include "openfat.h" + +#include "openfat/leaccess.h" +#include "openfat/blockdev.h" +#include "fat_core.h" +#include "direntry.h" + +#define LONG_NAME_SUPPORT + +#ifdef LONG_NAME_SUPPORT +uint8_t _fat_dirent_chksum(uint8_t *dosname) +{ + uint8_t sum = 0; + int i; + + for (i = 0; i < 11; i++) + sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + *dosname++; + + return sum; +} + +/* Used to convert W95 UTF-16 filenames to ascii. + * 0 means terminating null reached. + * 1 means converted to end on input. + * 2 means conversion error. + */ +static int ascii_from_utf16(char *ascii, const uint16_t *utf16, int count) +{ + uint16_t tmp; + while(count--) { + tmp = __get_le16(utf16++); + if(tmp > 127) + return 2; + *ascii++ = tmp; + if(tmp == 0) + return 0; + } + return 1; +} +#endif + +int fat_readdir(struct fat_file_handle *h, struct dirent *ent) +{ +#ifdef LONG_NAME_SUPPORT + uint16_t csum = -1; +#endif + struct fat_sdirent fatent; + int i, j; + + while(fat_read(h, &fatent, sizeof(fatent)) == sizeof(fatent)) { + + if(fatent.name[0] == 0) + return -1; /* Empty entry, end of directory */ + if(fatent.name[0] == (char)0xe5) + continue; /* Deleted entry */ + if(fatent.attr == FAT_ATTR_VOLUME_ID) + continue; /* Ignore volume id entry */ + if(fatent.attr == FAT_ATTR_LONG_NAME) { +#ifdef LONG_NAME_SUPPORT + struct fat_ldirent *ld = (void*)&fatent; + if(ld->ord & FAT_LAST_LONG_ENTRY) { + memset(ent->d_name, 0, sizeof(ent->d_name)); + csum = ld->checksum; + } + if(csum != ld->checksum) /* Abandon orphaned entry */ + csum = -1; + + i = ((ld->ord & 0x3f) - 1) * 13; + + /* If entries can't be converted to ASCII, abandon + * the long filename. DOS 8.3 name will be returned. + * Not pretty... */ + switch(ascii_from_utf16(&ent->d_name[i], ld->name1, 5)) + { case 0: continue; case 2: csum = -1; } + switch(ascii_from_utf16(&ent->d_name[i+5], ld->name2, 6)) + { case 0: continue; case 2: csum = -1; } + switch(ascii_from_utf16(&ent->d_name[i+11], ld->name3, 2)) + { case 0: continue; case 2: csum = -1; } +#endif + continue; + } +#ifdef LONG_NAME_SUPPORT + if(csum != _fat_dirent_chksum((uint8_t*)fatent.name)) + ent->d_name[0] = 0; + + if(ent->d_name[0] == 0) { +#endif + for(i = 0, j = 0; i < 11; i++, j++) { + ent->d_name[j] = tolower(fatent.name[i]); + if(fatent.name[i] == ' ') { + ent->d_name[j] = '.'; + while((fatent.name[++i] == ' ') && (i < 11)); + } + } + if(ent->d_name[j-1] == '.') + ent->d_name[j-1] = 0; + + ent->d_name[j] = 0; +#ifdef LONG_NAME_SUPPORT + } +#endif + /* Non-standard */ + ent->fat_attr = fatent.attr; + memcpy(ent->fat_sname, fatent.name, 11); + + return 0; + } + return -1; +} + +/* Seek to a place in the directory suitable for writing 'entries' new + * directory entries. Called when creating files. */ +int _fat_dir_seek_empty(struct fat_file_handle *dir, int entries) +{ + uint32_t pos = 0; + struct fat_sdirent ent; + int i = 0; + + fat_lseek(dir, 0, SEEK_SET); + + while(fat_read(dir, &ent, sizeof(ent)) == sizeof(ent)) { + if(ent.name[0] == 0) /* Empty entry, end of directory */ + break; + if(ent.name[0] == (char)0xe5) { /* Deleted entry */ + i++; + if(i == entries) + break; + continue; + } + i = 0; + pos = dir->position; + } + + fat_lseek(dir, pos, SEEK_SET); + return 0; +} + +static int fat_comparesfn(const char * name, const char *fatname) +{ + char canonname[11]; + int i; + + memset(canonname, ' ', sizeof(canonname)); + if(name[0] == '.') { + /* Special case: + * Only legal names are '.' and '..' */ + memcpy(canonname, name, strlen(name)); + name += strlen(name); + } else for(i = 0; (i < 11) && *name && (*name != '/'); i++) { + if(*name == '.') { + if(i < 8) continue; + if(i == 8) name++; + } + canonname[i] = toupper(*name++); + } + return ((*name == 0) || (*name == '/')) && !memcmp(canonname, fatname, 11); +} + +int fat_open(struct fat_vol_handle *vol, const char *name, int flags, + struct fat_file_handle *file) +{ + struct fat_file_handle *dir = (struct fat_file_handle*)&vol->cwd; + struct dirent dirent; + + /* FIXME: Implement flags O_RDONLY, O_WRONLY, O_RDWR. */ + + if(strcmp(name, ".") == 0) { + /* Special case needed for root dir with no '.' entry */ + memcpy(file, &vol->cwd, sizeof(*file)); + return 0; + } + + fat_lseek(dir, 0, SEEK_SET); + while(fat_readdir(dir, &dirent) == 0) { + + /* Check for name match */ + if((strcmp(name, dirent.d_name) == 0) || + fat_comparesfn(name, dirent.fat_sname)) { + /* reread on-disk directory entry */ + struct fat_sdirent fatent; + uint32_t sector; + uint16_t offset; + /* Rewind directory one entry */ + fat_lseek(dir, -sizeof(fatent), SEEK_CUR); + _fat_file_sector_offset(dir, §or, &offset); + if(fat_read(dir, &fatent, sizeof(fatent)) != 32) + return -EIO; + + _fat_file_init(dir->fat, &fatent, file); + file->flags = flags; + if(!(fatent.attr & FAT_ATTR_DIRECTORY)) { + file->dirent_sector = sector; + file->dirent_offset = offset; + } else if(!file->first_cluster) { + /* Check for special case of root dir */ + _fat_file_root(dir->fat, file); + } + return 0; + } + } + return -ENOENT; +} + +int fat_chdir(struct fat_vol_handle *vol, const char *name) +{ + return fat_open(vol, name, 0, &vol->cwd); +} + diff --git a/emb/pastilda/lib/libopenfat/direntry.h b/emb/pastilda/lib/libopenfat/direntry.h new file mode 100644 index 0000000..3ea00cb --- /dev/null +++ b/emb/pastilda/lib/libopenfat/direntry.h @@ -0,0 +1,62 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* FAT Directory entry definitions. + */ +#ifndef __DIRENTRY_H +#define __DIRENTRY_H + +#include <stdint.h> + +struct fat_sdirent { + char name[11]; /* DOS file name 8.3 */ + uint8_t attr; + uint8_t Reserved; + uint8_t create_time_fine; + uint16_t create_time; + uint16_t create_date; + uint16_t access_date; + uint16_t cluster_hi; + uint16_t write_time; + uint16_t write_date; + uint16_t cluster_lo; + uint32_t size; +} __attribute__((packed)); + + +#define FAT_LAST_LONG_ENTRY 0x40 + +/* W95 long file name entries. Characters are in UTF-16. */ +struct fat_ldirent { + uint8_t ord; + uint16_t name1[5]; + uint8_t attr; + uint8_t type; + uint8_t checksum; + uint16_t name2[6]; + uint16_t cluster_lo; + uint16_t name3[2]; +} __attribute__((packed)); + +uint8_t _fat_dirent_chksum(uint8_t *dosname); +int _fat_dir_seek_empty(struct fat_file_handle *dir, int entries); + +#endif + diff --git a/emb/pastilda/lib/libopenfat/fat_core.c b/emb/pastilda/lib/libopenfat/fat_core.c new file mode 100644 index 0000000..61f2143 --- /dev/null +++ b/emb/pastilda/lib/libopenfat/fat_core.c @@ -0,0 +1,231 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* FAT Filesystem core implementation + */ + +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "openfat.h" + +#include "openfat/blockdev.h" +#include "openfat/leaccess.h" +#include "bpb.h" +#include "fat_core.h" + +/* Build time configuration */ +#define MAX_SECTOR_SIZE 512 + +uint8_t _fat_sector_buf[MAX_SECTOR_SIZE]; +struct _fat_cache _fat_cache; + +int fat_vol_init(const struct block_device *dev, struct fat_vol_handle *h) +{ + struct bpb_common *bpb = (void *)&_fat_sector_buf; + + memset(h, 0, sizeof(*h)); + h->dev = dev; + + FAT_GET_SECTOR(h, 0); + + h->type = fat_type(bpb); + h->cluster_count = _bpb_cluster_count(bpb); + h->bytes_per_sector = __get_le16(&bpb->bytes_per_sector); + h->sectors_per_cluster = bpb->sectors_per_cluster; + h->first_data_sector = _bpb_first_data_sector(bpb); + h->reserved_sector_count = __get_le16(&bpb->reserved_sector_count); + h->num_fats = bpb->num_fats; + h->fat_size = _bpb_fat_size(bpb); + h->last_cluster_alloc = 2; + if(h->type == FAT_TYPE_FAT32) { + struct bpb_fat32 *bpb32 = (void *)&_fat_sector_buf; + h->fat32.root_cluster = __get_le32(&bpb32->root_cluster); + } else { + h->fat12_16.root_sector_count = _bpb_root_dir_sectors(bpb); + h->fat12_16.root_first_sector = _bpb_first_data_sector(bpb) - + h->fat12_16.root_sector_count; + } + _fat_file_root(h, &h->cwd); + + return 0; +} + +uint32_t _fat_get_next_cluster(const struct fat_vol_handle *h, uint32_t cluster) +{ + uint32_t offset; + uint32_t sector; + + if(h->type == FAT_TYPE_FAT12) + offset = cluster + (cluster / 2); + else if(h->type == FAT_TYPE_FAT16) + offset = cluster * 2; + else if(h->type == FAT_TYPE_FAT32) + offset = cluster * 4; + + sector = h->reserved_sector_count + (offset / h->bytes_per_sector); + offset %= h->bytes_per_sector; + + FAT_GET_SECTOR(h, sector); + + if(h->type == FAT_TYPE_FAT12) { + uint32_t next; + if(offset == (uint32_t)h->bytes_per_sector - 1) { + /* Fat entry is over sector boundary */ + next = _fat_sector_buf[offset]; + FAT_GET_SECTOR(h, sector + 1); + next += _fat_sector_buf[0] << 8; + } else { + next = __get_le16((uint16_t*)(_fat_sector_buf + offset)); + } + if(cluster & 1) + return next >> 4; + else + return next & 0xFFF; + } else if(h->type == FAT_TYPE_FAT16) { + return __get_le16((uint16_t*)(_fat_sector_buf + offset)); + } else if(h->type == FAT_TYPE_FAT32) { + return __get_le32((uint32_t*)(_fat_sector_buf + offset)) & 0x0FFFFFFF; + } + /* We shouldn't get here... */ + return 0; +} + +void _fat_file_root(struct fat_vol_handle *fat, + struct fat_file_handle *h) +{ + memset(h, 0, sizeof(*h)); + h->fat = fat; + + if(fat->type == FAT_TYPE_FAT32) { + h->first_cluster = fat->fat32.root_cluster; + } else { + /* FAT12/FAT16 root directory */ + h->root_flag = 1; + h->first_cluster = fat->fat12_16.root_first_sector; + h->size = h->fat->fat12_16.root_sector_count * h->fat->bytes_per_sector; + } + h->cur_cluster = h->first_cluster; +} + +void _fat_file_init(struct fat_vol_handle *fat, + const struct fat_sdirent *dirent, + struct fat_file_handle *h) +{ + memset(h, 0, sizeof(*h)); + h->fat = fat; + h->first_cluster = ((uint32_t)__get_le16(&dirent->cluster_hi) << 16) | + __get_le16(&dirent->cluster_lo); + h->size = __get_le32(&dirent->size); + h->cur_cluster = h->first_cluster; +} + +off_t fat_lseek(struct fat_file_handle *h, off_t offset, int whence) +{ + h->cur_cluster = h->first_cluster; + + switch(whence) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += h->position; + break; + case SEEK_END: + offset += h->size; + break; + default: + return -1; + } + + if(h->size && ((uint32_t)offset > h->size)) + offset = h->size; + + h->position = offset; + + if(h->root_flag) { /* FAT12/16 root dir isn't a cluster chain */ + return h->position; + } + + /* Iterate over cluster chain to find cluster */ + while(offset >= (h->fat->sectors_per_cluster * h->fat->bytes_per_sector)) { + h->cur_cluster = _fat_get_next_cluster(h->fat, h->cur_cluster); + offset -= h->fat->sectors_per_cluster * h->fat->bytes_per_sector; + } + + return h->position; +} + +void _fat_file_sector_offset(struct fat_file_handle *h, uint32_t *sector, + uint16_t *offset) +{ + if(h->root_flag) { + /* FAT12/FAT16 root directory */ + *sector = h->cur_cluster + + (h->position / h->fat->bytes_per_sector); + } else { + *sector = fat_first_sector_of_cluster(h->fat, h->cur_cluster); + *sector += (h->position / h->fat->bytes_per_sector) % + h->fat->sectors_per_cluster; + } + *offset = h->position % h->fat->bytes_per_sector; +} + +#define MIN(x, y) (((x) < (y))?(x):(y)) +int fat_read(struct fat_file_handle *h, void *buf, int size) +{ + int i; + uint32_t sector; + uint16_t offset; + + _fat_file_sector_offset(h, §or, &offset); + + /* Don't read past end of file */ + if(h->size && ((h->position + size) > h->size)) + size = h->size - h->position; + + for(i = 0; i < size; ) { + uint16_t chunk = MIN(h->fat->bytes_per_sector - offset, size - i); + FAT_GET_SECTOR(h->fat, sector); + memcpy(buf + i, _fat_sector_buf + offset, chunk); + h->position += chunk; + i += chunk; + if((h->position % h->fat->bytes_per_sector) != 0) + /* we didn't read until the end of the sector... */ + break; + offset = 0; + sector++; + if(h->root_flag) /* FAT12/16 isn't a cluster chain */ + continue; + if((sector % h->fat->sectors_per_cluster) == 0) { + /* Go to next cluster... */ + h->cur_cluster = _fat_get_next_cluster(h->fat, + h->cur_cluster); + if(h->cur_cluster == fat_eoc(h->fat)) + return i; + sector = fat_first_sector_of_cluster(h->fat, + h->cur_cluster); + } + } + + return i; +} + diff --git a/emb/pastilda/lib/libopenfat/fat_core.h b/emb/pastilda/lib/libopenfat/fat_core.h new file mode 100644 index 0000000..4c681b3 --- /dev/null +++ b/emb/pastilda/lib/libopenfat/fat_core.h @@ -0,0 +1,108 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* FAT Filesystem core implementation, private interface + */ +#ifndef __FAT_CORE_H +#define __FAT_CORE_H + +#include "bpb.h" +#include "direntry.h" + +extern uint8_t _fat_sector_buf[]; + +extern struct _fat_cache { + /* Working sector buffer, use _fat_sector_buf by default. */ + uint8_t *buf; + + /* Sector and block device for current contents of buf. */ + const void *bldev; + uint32_t sector; + + /* Non-zero if buffer is out-of-sync with the physical medium. */ + uint8_t dirty; +} _fat_cache; + +static inline uint32_t +fat_eoc(const struct fat_vol_handle *fat) +{ + switch (fat->type) { + case FAT_TYPE_FAT12: + return 0x0FF8; + case FAT_TYPE_FAT16: + return 0xFFF8; + case FAT_TYPE_FAT32: + return 0x0FFFFFF8; + } + return -1; +} + +static inline uint32_t +fat_first_sector_of_cluster(const struct fat_vol_handle *fat, uint32_t n) +{ + return ((n - 2) * fat->sectors_per_cluster) + fat->first_data_sector; +} + +uint32_t +_fat_get_next_cluster(const struct fat_vol_handle *h, uint32_t cluster); + +void _fat_file_root(struct fat_vol_handle *fat, struct fat_file_handle *h); +void _fat_file_init(struct fat_vol_handle *fat, const struct fat_sdirent *, + struct fat_file_handle *h); + +void _fat_file_sector_offset(struct fat_file_handle *h, uint32_t *sector, + uint16_t *offset); + +int _fat_dir_create_file(struct fat_vol_handle *vol, const char *name, + uint8_t attr, struct fat_file_handle *file); + +#define FAT_FLUSH_SECTOR() do {\ + if(_fat_cache.dirty) \ + if(block_write_sectors(_fat_cache.bldev, _fat_cache.sector, \ + 1, _fat_sector_buf) != 1) \ + return -EIO; \ + _fat_cache.dirty = 0; \ +} while(0) + +#define FAT_GET_SECTOR(fat, sectorn) do {\ + if((_fat_cache.bldev==(fat)->dev) && (_fat_cache.sector==(sectorn)))\ + break; \ +\ + FAT_FLUSH_SECTOR(); \ +\ + _fat_cache.bldev = (fat)->dev; \ + _fat_cache.sector = (sectorn); \ +\ + if(block_read_sectors((fat)->dev, (sectorn), 1, _fat_sector_buf) != 1)\ + return -EIO; \ +} while(0) + +#define FAT_PUT_SECTOR(fat, sectorn) do {\ + if((_fat_cache.bldev!=(fat)->dev) || (_fat_cache.sector!=(sectorn)))\ + FAT_FLUSH_SECTOR(); \ +\ + _fat_cache.bldev = (fat)->dev; \ + _fat_cache.sector = (sectorn); \ + _fat_cache.dirty = 1; \ +} while(0) + + +#endif + diff --git a/emb/pastilda/lib/libopenfat/mbr.c b/emb/pastilda/lib/libopenfat/mbr.c new file mode 100644 index 0000000..6697746 --- /dev/null +++ b/emb/pastilda/lib/libopenfat/mbr.c @@ -0,0 +1,71 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Master boot record. + */ + +#include <stdint.h> + +#include "openfat/leaccess.h" +#include "openfat/blockdev.h" +#include "openfat/mbr.h" + +/* In fat_core.c */ +extern uint8_t _fat_sector_buf[]; + +static int mbr_read_sectors(const struct block_device *dev, + uint32_t sector, uint32_t count, void *buf) +{ + struct block_mbr_partition *part = (void*)dev; + + return block_read_sectors(part->whole, + part->first_lba + sector, count, buf); +} + +static int mbr_write_sectors(const struct block_device *dev, + uint32_t sector, uint32_t count, const void *buf) +{ + struct block_mbr_partition *part = (void*)dev; + + return block_write_sectors(part->whole, + part->first_lba + sector, count, buf); +} + +int mbr_partition_init(struct block_mbr_partition *part, + struct block_device *whole, uint8_t part_index) +{ + struct mbr_partition *part_table = (void*)&_fat_sector_buf[446]; + /* Read MBR from whole device */ + if(block_read_sectors(whole, 0, 1, _fat_sector_buf) != 1) + return -1; + + part->whole = whole; + + part->first_lba = __get_le32(&part_table[part_index].first_lba); + part->sector_count = __get_le32(&part_table[part_index].sector_count); + + part->bldev.get_sector_size = whole->get_sector_size; + part->bldev.read_sectors = mbr_read_sectors; + part->bldev.write_sectors = mbr_write_sectors; + + return 0; +} + + diff --git a/emb/pastilda/lib/libopenfat/openfat.h b/emb/pastilda/lib/libopenfat/openfat.h new file mode 100644 index 0000000..ec4b856 --- /dev/null +++ b/emb/pastilda/lib/libopenfat/openfat.h @@ -0,0 +1,220 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file openfat.h + * \brief FAT Filesystem implementation, public interface. + */ + +#ifndef __OPENFAT_H +#define __OPENFAT_H + +#include <stdint.h> + +#include <sys/stat.h> + +#include "openfat/blockdev.h" + +/* Forward declarations of private structures. */ + +/** \brief Structure used internally for FAT volume state. + * Do not access directly. Structure has no public fields. + */ +typedef struct fat_vol_handle FatVol; + +/** \brief Structure used internally for FAT file state. + * Do not access directly. Structure has no public fields. + */ +typedef struct fat_file_handle FatFile; + +/** \brief Mount a FAT volume. + * + * Initialise a handle for access to a FAT filesystem on the specified + * block device. + * \param dev Pointer to block device to mount. + * \param vol Pointer to filesystem handle to initialise. + * \return 0 on success. + */ +int __attribute__((warn_unused_result)) +fat_vol_init(const struct block_device *dev, FatVol *vol); + +/** \brief Change current working directory. + * \param vol Pointer to FAT volume handle. + * \param name Directory name to change to. Relative to the current dir. + * \return 0 on success. + */ +int fat_chdir(FatVol *vol, const char *name); +/** \brief Create a new directory. + * \param vol Pointer to FAT volume handle. + * \param name Directory name to create. + * \return 0 on success. + */ +int fat_mkdir(FatVol *vol, const char *name); +/** \brief Remove an empty directory. + * \param vol Pointer to FAT volume handle. + * \param name Directory name to remove. + * \return 0 on success. + */ +int fat_rmdir(FatVol *vol, const char *name); /* TODO */ + +/** \brief Open an existing file. + * The option O_ASYNC may be passed in flags to surpress the automatic + * updating of the files directory entry on writes. fat_file_sync() must be + * called explicitly to update it in this case. + * + * This function uses a ::dirent on the stack for iterating over the directory. + * This is a fairly large structure. + * + * \param vol Pointer to FAT volume handle. + * \param name File name in current directory to open. + * \param flags O_RDONLY, O_WRONLY, or O_RDWR. (currently not implemented) + * \param file Pointer to file handle to initialise. + * \return 0 on success. + */ +int __attribute__((warn_unused_result)) +fat_open(FatVol *vol, const char *name, int flags, FatFile *file); + +/** \brief Create a new file. + * \see fat_open() + * + * \param vol Pointer to FAT volume handle. + * \param name File name in current directory to create. + * \param flags O_RDONLY, O_WRONLY, or O_RDWR. (currently not implemented) + * \param file Pointer to file handle to initialise. + * \return 0 on success. + */ +int __attribute__((warn_unused_result)) +fat_create(FatVol *vol, const char *name, int flags, FatFile *file); + +#define O_ASYNC 020000 +/** \brief Update an open file's directory entry. + * This must be called explicitly if a file is opened with the flag O_ASYNC. + * In this case, updates to the directory entry are surpressed on writes to + * improve performance. + * \param file Pointer to file handle from which to read. + * \return 0 on success. + */ +int fat_file_sync(FatFile *h); + +/** \brief Read from an open file. + * \param file Pointer to file handle from which to read. + * \param buf Buffer into which to read. + * \param size Number of bytes to read. + * \return Number of bytes read on success, negative on error. + */ +int __attribute__((warn_unused_result)) +fat_read(FatFile *file, void *buf, int size); + +/** \brief Write to an open file. + * \param file Pointer to file handle into which to write. + * \param buf Buffer from which to write. + * \param size Number of bytes to write. + * \return Number of bytes written on success, negative on error. + */ +int __attribute__((warn_unused_result)) +fat_write(FatFile *file, const void *buf, int size); + +/** \brief Sets the position in an open file. + * \param file Pointer to file handle to change. + * \param offset Offset into target file. + * \param whence One of SEEK_SET, SEEK_CUR, SEEK_END. See Unix documentation. + * \return New file position on success, negative on error. + */ +off_t fat_lseek(FatFile *file, off_t offset, int whence); + +/** \brief Unlink/delete a file. + * \param vol Pointer to FAT volume handle. + * \param name Name of file in current directory to unlink. + * \return 0 on success. + */ +int fat_unlink(FatVol *vol, const char *name); + +#define FAT_ATTR_READ_ONLY 0x01 +#define FAT_ATTR_HIDDEN 0x02 +#define FAT_ATTR_SYSTEM 0x04 +#define FAT_ATTR_VOLUME_ID 0x08 +#define FAT_ATTR_DIRECTORY 0x10 +#define FAT_ATTR_ARCHIVE 0x20 +#define FAT_ATTR_LONG_NAME 0x0F + +/** \brief Public directory entry structure, returned by fat_readdir() */ +struct dirent { + char d_name[256]; /**< Long file name, POSIX standard. */ + /* Non-standard */ + uint8_t fat_attr; /**< FAT file attributes, non-standard */ + char fat_sname[11]; /**< DOS short filename, non-standard */ +}; + +/** \brief Read a directory entry. + * The dirent structure is filled in with the details of the next file in + * the directory stream pointed to by dir. + * \param dir Pointer to file handle for the directory to be read. + * \param ent Pointer to dirent structure for the read entry. + * \return 0 on success. + */ +int fat_readdir(FatFile *dir, struct dirent *ent); + + +/* Everything below is private. Applications should not direcly access + * anything here. + */ + +struct fat_file_handle { + struct fat_vol_handle *fat; + /* Fields from dir entry */ + uint32_t size; + uint32_t first_cluster; + /* Internal state information */ + uint32_t position; + uint32_t cur_cluster; /* This is used for sector on FAT12/16 root */ + uint8_t root_flag; /* Flag to mark root directory on FAT12/16 */ + int flags; + /* Reference to dirent */ + uint32_t dirent_sector; + uint16_t dirent_offset; +}; + +struct fat_vol_handle { + const struct block_device *dev; + /* FAT type: 12, 16 or 32 */ + int type; + /* Useful fields from BPB */ + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sector_count; + uint8_t num_fats; + /* Fields calcuated from BPB */ + uint32_t first_data_sector; + uint32_t cluster_count; + uint32_t fat_size; + union { + struct { + uint32_t root_cluster; + } fat32; + struct { + uint16_t root_sector_count; + uint16_t root_first_sector; + } fat12_16; + }; + /* Internal state */ + uint32_t last_cluster_alloc; + struct fat_file_handle cwd; +}; + +#endif diff --git a/emb/pastilda/lib/libopenfat/openfat/blockdev.h b/emb/pastilda/lib/libopenfat/openfat/blockdev.h new file mode 100644 index 0000000..ce8b15a --- /dev/null +++ b/emb/pastilda/lib/libopenfat/openfat/blockdev.h @@ -0,0 +1,76 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file blockdev.h + * \brief Block device abstraction. + * Must be implemented by application for a physical device. + */ + +#ifndef __BLOCKDEV_H +#define __BLOCKDEV_H + +#include <stdint.h> + +/** \brief Structure representing an abstract block device. + * This abstraction must be provided by the application. + */ +struct block_device { + /* Info about the device */ + /** \brief Method to get sector size. */ + uint16_t (*get_sector_size)(const struct block_device *dev); + /* ... more to be added as needed ... */ + + /* Actions on the device */ + /** \brief Method to read sectors. */ + int (*read_sectors)(const struct block_device *dev, + uint32_t sector, uint32_t count, void *buf); + /** \brief Method to write sectors. */ + int (*write_sectors)(const struct block_device *dev, + uint32_t sector, uint32_t count, const void *buf); + /* ... more to be added as needed ... */ + + /* May be private fields here ... */ +}; + +/* Convenient wrapper functions */ +static inline uint16_t +block_get_sector_size(const struct block_device *dev) +{ + return dev->get_sector_size(dev); +} + +/* Returns the number of sectors read or negative on error */ +static inline int __attribute__((warn_unused_result)) +block_read_sectors(const struct block_device *dev, + uint32_t sector, uint32_t count, void *buf) +{ + return dev->read_sectors(dev, sector, count, buf); +} + +/* Returns the number of sectors written or negative on error */ +static inline int __attribute__((warn_unused_result)) +block_write_sectors(const struct block_device *dev, + uint32_t sector, uint32_t count, const void *buf) +{ + return dev->write_sectors(dev, sector, count, buf); +} + +#endif + diff --git a/emb/pastilda/lib/libopenfat/openfat/leaccess.h b/emb/pastilda/lib/libopenfat/openfat/leaccess.h new file mode 100644 index 0000000..6a48955 --- /dev/null +++ b/emb/pastilda/lib/libopenfat/openfat/leaccess.h @@ -0,0 +1,67 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Macros for structure access. Host system may be little or big endian, + * and may not be capable of misaligned access. Always use these for + * access to the on-disk FAT structures. + */ + +#ifndef __LEACCESS_H +#define __LEACCESS_H + +#include <stdint.h> + +static inline uint16_t __get_le16(const uint16_t *p) +{ + return *(uint8_t *)p + (*((uint8_t *)p + 1) << 8); +} + +static inline void __put_le16(uint16_t *p, uint16_t v) +{ + *(uint8_t *)p = v & 0xff; + *((uint8_t *)p + 1) = v >> 8; +} + +static inline uint32_t __get_le32(const uint32_t *p) +{ + return __get_le16((uint16_t *)p) + + (uint32_t)(__get_le16((const uint16_t *)p + 1) << 16); +} + +static inline void __put_le32(uint32_t *p, uint32_t v) +{ + __put_le16((uint16_t *)p, v & 0xffff); + __put_le16((uint16_t *)p + 1, v >> 16); +} + +#ifdef __TEST__ +#include <stdio.h> +#include <assert.h> +int main(void) +{ + const char testdata[] = "\x01\x23\x45\x67"; + printf("%08X\n", __get_le32((uint32_t *)testdata)); + assert(__get_le32((uint32_t *)testdata) == 0x67452301); + return 0; +} +#endif + +#endif + diff --git a/emb/pastilda/lib/libopenfat/openfat/mbr.h b/emb/pastilda/lib/libopenfat/openfat/mbr.h new file mode 100644 index 0000000..a9f41f9 --- /dev/null +++ b/emb/pastilda/lib/libopenfat/openfat/mbr.h @@ -0,0 +1,64 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file mbr.h + * \brief PC Disk Partition Table implementation. + * This module provides interpretation of the PC Master Boot Record + * partition table, and provides a block device implementation for + * partitions given an implementation for the whole disk using wrapper + * functions. + */ + +#ifndef __MBR_H +#define __MBR_H + +#include <stdint.h> + +#include "blockdev.h" + +struct mbr_partition { + uint8_t bootable; + uint8_t first_chs[3]; + uint8_t type; + uint8_t last_chs[3]; + uint32_t first_lba; + uint32_t sector_count; +} __attribute__((packed)); + +/** \brief Structure representing block device for a PC disk partition. + * Don't access directly. No public fields. */ +struct block_mbr_partition { + struct block_device bldev; + struct block_device *whole; + uint32_t first_lba; + uint32_t sector_count; +}; + +/** \brief Initialise a partition block device. + * \param part Pointer to partition block device to initialize. + * \param whole Pointer to block device for whole media. + * \param part_index Partition index (0-3). + * \return 0 on success. + */ +int mbr_partition_init(struct block_mbr_partition *part, + struct block_device *whole, uint8_t part_index); + +#endif + diff --git a/emb/pastilda/lib/libopenfat/openfat/unixlike.h b/emb/pastilda/lib/libopenfat/openfat/unixlike.h new file mode 100644 index 0000000..cfba7cd --- /dev/null +++ b/emb/pastilda/lib/libopenfat/openfat/unixlike.h @@ -0,0 +1,47 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Unix-like API for FAT filesystem access. + */ + +#ifndef __UNIXLIKE_H +#define __UNIXLIKE_H + +#include <openfat.h> + +#include <stdlib.h> + +FatVol * ufat_mount(struct block_device *dev); +static inline void ufat_umount(FatVol *vol) { free(vol); } + +FatFile * ufat_open(FatVol *vol, const char *path, int flags); +static inline void ufat_close(FatFile *file) { free(file); } + +int ufat_stat(FatFile *file, struct stat *stat); + +#define ufat_read fat_read +#define ufat_write fat_write + +#define ufat_chdir fat_chdir +#define ufat_mkdir fat_mkdir + +#endif + diff --git a/emb/pastilda/lib/libopenfat/unixlike.c b/emb/pastilda/lib/libopenfat/unixlike.c new file mode 100644 index 0000000..ec2e908 --- /dev/null +++ b/emb/pastilda/lib/libopenfat/unixlike.c @@ -0,0 +1,105 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "openfat.h" + +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include "openfat/blockdev.h" +#include "fat_core.h" +#include "direntry.h" + +struct fat_vol_handle * +ufat_mount(struct block_device *dev) +{ + struct fat_vol_handle *vol = malloc(sizeof(*vol)); + + if(fat_vol_init(dev, vol)) { + free(vol); + return NULL; + } + + return vol; +} + +struct fat_file_handle * +ufat_open(struct fat_vol_handle *fat, const char *path, int flags) +{ + struct fat_file_handle oldcwd; + + if(!path || (path[0] == 0)) + return NULL; + + struct fat_file_handle *h = malloc(sizeof(*h)); + + if(path[0] == '/') { + _fat_file_root(fat, h); + path++; + } else { + memcpy(h, &fat->cwd, sizeof(*h)); + } + + memcpy(&oldcwd, &fat->cwd, sizeof(oldcwd)); + while(path && *path) { + memcpy(&fat->cwd, h, sizeof(*h)); + if(fat_open(fat, path, flags, h)) { + free(h); + memcpy(&fat->cwd, &oldcwd, sizeof(oldcwd)); + return NULL; + } + path = strchr(path, '/'); + if(path) path++; + }; + + memcpy(&fat->cwd, &oldcwd, sizeof(oldcwd)); + + return h; +} + +int ufat_stat(struct fat_file_handle *h, struct stat *st) +{ + struct fat_sdirent *fatent; + + memset(st, 0, sizeof(*st)); + + if(h->dirent_sector == 0) { + /* Root directory */ + st->st_mode = S_IFDIR; + return 0; + } + + /* Read direntry sector */ + if(block_read_sectors(h->fat->dev, h->dirent_sector, 1, _fat_sector_buf) != 1) + return -1; + fatent = (void*)&_fat_sector_buf[h->dirent_offset]; + + /* TODO: Fill in timestamps */ + + if(fatent->attr & FAT_ATTR_DIRECTORY) { + st->st_mode = S_IFDIR; + } else { + st->st_size = __get_le32(&fatent->size); + } + + return 0; +} + diff --git a/emb/pastilda/lib/libopenfat/write.c b/emb/pastilda/lib/libopenfat/write.c new file mode 100644 index 0000000..6e9e62f --- /dev/null +++ b/emb/pastilda/lib/libopenfat/write.c @@ -0,0 +1,453 @@ +/* + * This file is part of the openfat project. + * + * Copyright (C) 2011 Department of Physics, University of Otago + * Written by Gareth McMullin <gareth@blacksphere.co.nz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* FAT Filesystem write support implementation + */ +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "openfat.h" + +#include "fat_core.h" + +static uint32_t fat_find_free_cluster(struct fat_vol_handle *h) +{ + uint32_t i; + + for(i = h->last_cluster_alloc; i < h->cluster_count; i++) { + if(_fat_get_next_cluster(h, i) == 0) { + h->last_cluster_alloc = i; + return i; + } + } + + for(i = 2; i < h->last_cluster_alloc; i++) { + if(_fat_get_next_cluster(h, i) == 0) { + h->last_cluster_alloc = i; + return i; + } + } + + return 0; /* No free clusters */ +} + +static int +fat32_set_next_cluster(const struct fat_vol_handle *h, uint8_t fat, + uint32_t cluster, uint32_t next) +{ + uint32_t offset = cluster * 4; + uint32_t sector; + + sector = h->reserved_sector_count + (fat * h->fat_size) + + (offset / h->bytes_per_sector); + offset %= h->bytes_per_sector; + + FAT_GET_SECTOR(h, sector); + + /* Preserve high nybble */ + next &= 0x0FFFFFFF; + next |= __get_le32((uint32_t*)(_fat_sector_buf + offset)) & 0xF0000000; + __put_le32((uint32_t*)(_fat_sector_buf + offset), next); + + FAT_PUT_SECTOR(h, sector); + + return 0; +} + +static int +fat16_set_next_cluster(const struct fat_vol_handle *h, uint8_t fat, + uint16_t cluster, uint16_t next) +{ + uint32_t offset = cluster * 2; + uint32_t sector; + + sector = h->reserved_sector_count + (fat * h->fat_size) + + (offset / h->bytes_per_sector); + offset %= h->bytes_per_sector; + + FAT_GET_SECTOR(h, sector); + __put_le16((uint16_t*)(_fat_sector_buf + offset), next); + FAT_PUT_SECTOR(h, sector); + + return 0; +} + +static int +fat12_set_next_cluster(const struct fat_vol_handle *h, uint8_t fat, + uint16_t cluster, uint16_t next) +{ + uint32_t offset = cluster + (cluster / 2); + uint32_t sector; + sector = h->reserved_sector_count + (fat * h->fat_size) + + (offset / h->bytes_per_sector); + offset %= h->bytes_per_sector; + + FAT_GET_SECTOR(h, sector); + if(offset == (uint32_t)h->bytes_per_sector - 1) { + if(cluster & 1) { + next <<= 4; + _fat_sector_buf[offset] &= 0x0F; + _fat_sector_buf[offset] |= next & 0xF0; + FAT_PUT_SECTOR(h, sector); + sector++; + FAT_GET_SECTOR(h, sector); + _fat_sector_buf[0] = next >> 8; + } else { + _fat_sector_buf[offset] = next & 0xFF; + FAT_PUT_SECTOR(h, sector); + sector++; + FAT_GET_SECTOR(h, sector); + _fat_sector_buf[0] &= 0xF0; + _fat_sector_buf[0] |= (next >> 8) & 0x0F; + } + } else { + if(cluster & 1) { + next <<= 4; + next |= __get_le16((uint16_t*)(_fat_sector_buf + offset)) & 0xF; + } else { + next &= 0x0FFF; + next |= __get_le16((uint16_t*)(_fat_sector_buf + offset)) & 0xF000; + } + __put_le16((uint16_t*)(_fat_sector_buf + offset), next); + } + FAT_PUT_SECTOR(h, sector); + + return 0; +} + +static int +fat_set_next_cluster(const struct fat_vol_handle *h, + uint32_t cluster, uint32_t next) +{ + int ret = 0; + for(int i = 0; i < h->num_fats; i++) { + switch(h->type) { + case FAT_TYPE_FAT12: + ret |= fat12_set_next_cluster(h, i, cluster, next); + break; + case FAT_TYPE_FAT16: + ret |= fat16_set_next_cluster(h, i, cluster, next); + break; + case FAT_TYPE_FAT32: + ret |= fat32_set_next_cluster(h, i, cluster, next); + break; + } + } + return ret; +} + +static int32_t fat_alloc_next_cluster(struct fat_vol_handle *h, + uint32_t cluster, int clear) +{ + /* Return next if already allocated */ + uint32_t next = _fat_get_next_cluster(h, cluster); + + if(next != fat_eoc(h)) + return next; + + /* Find free cluster to link to */ + next = fat_find_free_cluster(h); + + if(!next) /* No more free clusters */ + return 0; + + /* Write end of chain marker in new cluster */ + fat_set_next_cluster(h, next, fat_eoc(h)); + /* Add new cluster to chain */ + fat_set_next_cluster(h, cluster, next); + + if(clear) { + /* Zero new cluster */ + uint32_t sector = fat_first_sector_of_cluster( + h, next); + FAT_FLUSH_SECTOR(); + memset(_fat_sector_buf, 0, h->bytes_per_sector); + for(int i = 0; i < h->sectors_per_cluster; i++) { + /* How do we report failure here? + * The cluster has already been allocated. + */ + int discard = block_write_sectors(h->dev, sector + i, 1, + _fat_sector_buf); + (void)discard; + } + } + + return next; +} + +int fat_file_sync(struct fat_file_handle *h) +{ + struct fat_sdirent *dirent; + /* Update directory entry with new size */ + FAT_GET_SECTOR(h->fat, h->dirent_sector); + dirent = (void*)&_fat_sector_buf[h->dirent_offset]; + __put_le32(&dirent->size, h->size); + __put_le16(&dirent->cluster_hi, h->first_cluster >> 16); + __put_le16(&dirent->cluster_lo, h->first_cluster & 0xFFFF); + FAT_PUT_SECTOR(h->fat, h->dirent_sector); + FAT_FLUSH_SECTOR(); + return 0; +} + +#define MIN(x, y) (((x) < (y))?(x):(y)) +int fat_write(struct fat_file_handle *h, const void *buf, int size) +{ + int i; + uint32_t sector; + uint16_t offset; + + if(!h->cur_cluster && size) { + /* File was empty, allocate first cluster. */ + h->first_cluster = fat_find_free_cluster(h->fat); + if(!h->first_cluster) + return 0; + h->cur_cluster = h->first_cluster; + /* Write end of chain marker in new cluster */ + fat_set_next_cluster(h->fat, h->cur_cluster, fat_eoc(h->fat)); + /* Directory entry will be updated with size after the + * file write is done. + */ + } + + /* Don't write past end of FAT12/FAT16 root directory! */ + if(h->root_flag && ((h->position + size) > h->size)) + size = h->size - h->position; + + _fat_file_sector_offset(h, §or, &offset); + + for(i = 0; i < size; ) { + uint16_t chunk = MIN(h->fat->bytes_per_sector - offset, + size - i); + if(chunk < h->fat->bytes_per_sector) + FAT_GET_SECTOR(h->fat, sector); + else + FAT_FLUSH_SECTOR(); + + memcpy(_fat_sector_buf + offset, buf + i, chunk); + FAT_PUT_SECTOR(h->fat, sector); + h->position += chunk; + i += chunk; + if((h->position % h->fat->bytes_per_sector) != 0) + /* we didn't write until the end of the sector... */ + break; + offset = 0; + sector++; + if(h->root_flag) /* FAT12/16 isn't a cluster chain */ + continue; + if((sector % h->fat->sectors_per_cluster) == 0) { + /* Go to next cluster... */ + uint32_t next_cluster = fat_alloc_next_cluster(h->fat, + h->cur_cluster, h->size == 0); + if(!next_cluster) + break; + h->cur_cluster = next_cluster; + sector = fat_first_sector_of_cluster(h->fat, + h->cur_cluster); + } + } + + if(h->dirent_sector && (h->position > h->size)) { + /* Update directory entry with new size */ + h->size = h->position; + if(!(h->flags & O_ASYNC)) + fat_file_sync(h); + } + + return i; +} + +static int fat_chain_unlink(const struct fat_vol_handle *vol, uint32_t cluster) +{ + int ret = 0; + while(cluster && (cluster != fat_eoc(vol))) { + uint32_t next = _fat_get_next_cluster(vol, cluster); + ret |= fat_set_next_cluster(vol, cluster, 0); + cluster = next; + } + return ret; +} + +int fat_unlink(struct fat_vol_handle *vol, const char *name) +{ + struct fat_file_handle h; + int ret; + + ret = fat_open(vol, name, 0, &h); + if(ret) + return ret; + + /* Don't try to unlink directories, use fat_rmdir() instead. */ + if(!h.dirent_sector) + return -EISDIR; + + /* Free up cluster chain */ + fat_chain_unlink(vol, h.first_cluster); + + /* Mark directory entry as deleted */ + FAT_GET_SECTOR(vol, h.dirent_sector); + _fat_sector_buf[h.dirent_offset] = 0xE5; + FAT_PUT_SECTOR(vol, h.dirent_sector); + + /* FIXME: Remove long name entries. */ + + return 0; +} + +/* Build a short name for a long name. n is used if name is too long. */ +static void build_short_name(uint8_t *sname, const char *name, int n) +{ + int i, j; + + memset(sname, ' ', 11); + for(i = 0; (i < 8) && name[i] && (name[i] != '.'); i++) + sname[i] = toupper(name[i]); + + char *suffix = strrchr(name, '.'); + if(suffix) for(j = 1; (j < 4) && suffix[j]; j++) + sname[j+7] = toupper(suffix[j]); + + if(((i == 8) && (name[i] != '.')) || + ((suffix - name) != i)) { + if(i > 6) + i = 6; + sname[i] = '~'; + sname[i+1] = '0' + (n % 10); + } +} + +/* Create a new zero-length file */ +int _fat_dir_create_file(struct fat_vol_handle *vol, const char *name, + uint8_t attr, struct fat_file_handle *file) +{ + /* Check if file already exists */ + if(!fat_open(vol, name, 0, file)) + return -1; /* File exists */ + + /* Attempt to construct a short name for the file */ + uint8_t sname[12]; + sname[11] = 0; /* fat_open() needs terminating null */ + for(int i = 1; i < 10; i++) { + build_short_name(sname, name, i); + if(fat_open(vol, (char*)sname, 0, file)) + break; /* We have a winner */ + sname[0] = ' '; + } + + if(sname[0] == ' ') + return -1; /* Couldn't find a short name */ + + /* Find usable space in parent directory */ + _fat_dir_seek_empty(&vol->cwd, (strlen(name) / 13) + 2); + + /* Create long name directory entries */ + struct fat_ldirent ld; + int last = 1; + memset(&ld, 0, sizeof(ld)); + ld.attr = FAT_ATTR_LONG_NAME; + for(int i = strlen(name) / 13; i >= 0; i--) { + ld.ord = i + 1; + if(last) { + ld.ord |= FAT_LAST_LONG_ENTRY; + last = 0; + } + int j; + for(j = 0; j < 5; j++) + __put_le16(&ld.name1[j], name[i*13 + j]); + for(j = 0; j < 6; j++) + __put_le16(&ld.name2[j], name[i*13 + j + 5]); + for(j = 0; j < 2; j++) + __put_le16(&ld.name3[j], name[i*13 + j + 11]); + ld.checksum = _fat_dirent_chksum(sname); + if(fat_write(&vol->cwd, &ld, sizeof(ld)) != sizeof(ld)) + return -1; + } + + /* Create short name entry */ + struct fat_sdirent fatent; + memset(&fatent, 0, sizeof(fatent)); + fatent.attr = attr; + memcpy(&fatent.name, sname, 11); + /* TODO: Insert timestamp */ + if(attr == FAT_ATTR_DIRECTORY) { + /* Allocate a cluster for directories */ + uint32_t cluster = fat_find_free_cluster(vol); + if(!cluster) + return -1; + fat_set_next_cluster(vol, cluster, fat_eoc(vol)); + __put_le16(&fatent.cluster_hi, cluster >> 16); + __put_le16(&fatent.cluster_lo, cluster & 0xFFFF); + } + if(fat_write(&vol->cwd, &fatent, sizeof(fatent)) != sizeof(fatent)) + return -1; + + return fat_open(vol, name, 0, file); +} + +int fat_mkdir(struct fat_vol_handle *vol, const char *name) +{ + int ret; + struct fat_file_handle dir; + struct fat_sdirent fatent; + ret = _fat_dir_create_file(vol, name, FAT_ATTR_DIRECTORY, &dir); + if(ret) + return ret; + + FAT_FLUSH_SECTOR(); + /* Clear out cluster */ + memset(_fat_sector_buf, 0, vol->bytes_per_sector); + uint32_t sector = fat_first_sector_of_cluster(vol, dir.first_cluster); + for(int i = 0; i < vol->sectors_per_cluster; i++) + FAT_PUT_SECTOR(vol, sector + i); + + memset(&fatent, 0, sizeof(fatent)); + fatent.attr = FAT_ATTR_DIRECTORY; + memset(fatent.name, ' ', 11); + + /* Create '.' entry */ + fatent.name[0] = '.'; + __put_le16(&fatent.cluster_hi, dir.first_cluster >> 16); + __put_le16(&fatent.cluster_lo, dir.first_cluster & 0xFFFF); + ret = fat_write(&dir, &fatent, sizeof(fatent)); + if(ret < 0) + return ret; + + /* Create '..' entry */ + fatent.name[1] = '.'; + if((!vol->cwd.root_flag) && + (vol->fat32.root_cluster != vol->cwd.first_cluster)) { + __put_le16(&fatent.cluster_hi, vol->cwd.first_cluster >> 16); + __put_le16(&fatent.cluster_lo, vol->cwd.first_cluster & 0xFFFF); + } else { + fatent.cluster_hi = 0; + fatent.cluster_lo = 0; + } + ret = fat_write(&dir, &fatent, sizeof(fatent)); + return (ret < 0) ? ret : 0; +} + +int fat_create(struct fat_vol_handle *vol, const char *name, int flags, + struct fat_file_handle *file) +{ + int ret = _fat_dir_create_file(vol, name, FAT_ATTR_ARCHIVE, file); + file->flags = flags; + return ret; +} + diff --git a/emb/pastilda/main.cpp b/emb/pastilda/main.cpp index 3268a72..27a191d 100644 --- a/emb/pastilda/main.cpp +++ b/emb/pastilda/main.cpp @@ -2,17 +2,8 @@ #include "stdio.h" using namespace Application; -#ifdef DEBUG - #define DEBUG_PRINT(x) printf(x) -#else - #define DEBUG_PRINT(x) do {} while (0) -#endif - -extern "C" void initialise_monitor_handles(void); - int main() { - //initialise_monitor_handles(); App *app = new App(); while(1) { app->process(); diff --git a/emb/pastilda/stm32f407vg.ld b/emb/pastilda/stm32f405rg.ld index c5e1bf8..c5e1bf8 100644 --- a/emb/pastilda/stm32f407vg.ld +++ b/emb/pastilda/stm32f405rg.ld diff --git a/emb/pastilda/hw/usb_device/usbd_composite.cpp b/emb/pastilda/usb/usb_device/usbd_composite.cpp index aac38db..6136594 100644 --- a/emb/pastilda/hw/usb_device/usbd_composite.cpp +++ b/emb/pastilda/usb/usb_device/usbd_composite.cpp @@ -19,12 +19,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "usbd_composite.h" +#include <usb/usb_device/usbd_composite.h> using namespace GPIO_CPP_Extension; USB_composite *usb_pointer; -USB_composite::USB_composite() +USB_composite::USB_composite(const uint32_t block_count, + int (*read_block)(uint32_t lba, uint8_t *copy_to), + int (*write_block)(uint32_t lba, const uint8_t *copy_from)) { usb_pointer = this; descriptors = new UsbCompositeDescriptors(); @@ -38,17 +40,15 @@ USB_composite::USB_composite() uf_p.set_af(AF_Number::AF10); uf_m.set_af(AF_Number::AF10); - my_usb_device = usbd_init(&otgfs_usb_driver, &(descriptors->dev), - &(descriptors->config_descr), (const char**)descriptors->usb_strings, 3, + my_usb_device = usbd_init(&otgfs_usb_driver, &(UsbCompositeDescriptors::dev), + &(UsbCompositeDescriptors::config_descr), (const char**)UsbCompositeDescriptors::usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer)); usbd_register_set_config_callback(my_usb_device, USB_set_config_callback); nvic_enable_irq(NVIC_OTG_FS_IRQ); - ramdisk_init(); - usb_msc_init(my_usb_device, Endpoint::E_MASS_STORAGE_IN, 64, Endpoint::E_MASS_STORAGE_OUT, 64, - "ThirdPin", "Pastilda", "0.00", ramdisk_blocks(), ramdisk_read, ramdisk_write); + "ThirdPin", "Pastilda", "0.00", block_count, read_block, write_block); } void USB_composite::usb_send_packet(const void *buf, int len) @@ -61,6 +61,15 @@ void USB_OTG_IRQ() usbd_poll(usb_pointer->my_usb_device); } +void USB_composite::hid_set_config(usbd_device *usbd_dev, uint16_t wValue) +{ + (void)wValue; + (void)usbd_dev; + + usbd_ep_setup(usbd_dev, Endpoint::E_KEYBOARD, USB_ENDPOINT_ATTR_INTERRUPT, 8, 0); + usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_RECIPIENT, USB_control_callback ); +} + int USB_composite::hid_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)) { @@ -145,7 +154,7 @@ int USB_control_callback(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete) { - return( usb_pointer->hid_control_request(usbd_dev, req, buf, len, complete)); + return(usb_pointer->hid_control_request(usbd_dev, req, buf, len, complete)); } diff --git a/emb/pastilda/hw/usb_device/usbd_composite.h b/emb/pastilda/usb/usb_device/usbd_composite.h index 0fa239f..ac83f3b 100644 --- a/emb/pastilda/hw/usb_device/usbd_composite.h +++ b/emb/pastilda/usb/usb_device/usbd_composite.h @@ -19,13 +19,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef USB_MSC_H -#define USB_MSC_H +#ifndef USB_COMPOSITE_H +#define USB_COMPOSITE_H -extern "C" -{ -#include "ramdisk.h" -} #include "usbd_composite_desc.h" #include "systick_ext.h" #include "gpio_ext.h" @@ -56,25 +52,15 @@ public: uint8_t usb_ready = 0; usbd_device *my_usb_device; - USB_composite(); + USB_composite(const uint32_t block_count, + int (*read_block)(uint32_t lba, uint8_t *copy_to), + int (*write_block)(uint32_t lba, const uint8_t *copy_from)); void usb_send_packet(const void *buf, int len); - void usb_poll() - { - usbd_poll(my_usb_device); - } - int hid_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, - void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)); - - void hid_set_config(usbd_device *usbd_dev, uint16_t wValue) - { - (void)wValue; - (void)usbd_dev; + void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)); - usbd_ep_setup(usbd_dev, Endpoint::E_KEYBOARD, USB_ENDPOINT_ATTR_INTERRUPT, 8, 0); - usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_RECIPIENT, USB_control_callback ); - } + void hid_set_config(usbd_device *usbd_dev, uint16_t wValue); }; #endif diff --git a/emb/pastilda/hw/usb_device/usbd_composite_desc.cpp b/emb/pastilda/usb/usb_device/usbd_composite_desc.cpp index daf025a..c1018cc 100644 --- a/emb/pastilda/hw/usb_device/usbd_composite_desc.cpp +++ b/emb/pastilda/usb/usb_device/usbd_composite_desc.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "usbd_composite_desc.h" +#include <usb/usb_device/usbd_composite_desc.h> constexpr uint8_t UsbCompositeDescriptors::keyboard_report_descriptor[]; constexpr UsbCompositeDescriptors::type_hid_function UsbCompositeDescriptors::keyboard_hid_function; diff --git a/emb/pastilda/hw/usb_device/usbd_composite_desc.h b/emb/pastilda/usb/usb_device/usbd_composite_desc.h index 2c09aa0..2c09aa0 100644 --- a/emb/pastilda/hw/usb_device/usbd_composite_desc.h +++ b/emb/pastilda/usb/usb_device/usbd_composite_desc.h diff --git a/emb/pastilda/hw/usb_host/usbh_host.cpp b/emb/pastilda/usb/usb_host/usbh_host.cpp index b38a7e4..4a8f559 100644 --- a/emb/pastilda/hw/usb_host/usbh_host.cpp +++ b/emb/pastilda/usb/usb_host/usbh_host.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "usbh_host.h" +#include <usb/usb_host/usbh_host.h> constexpr hid_kbd_config_t USB_host::kbd_config; constexpr usbh_dev_driver_t* USB_host::device_drivers[]; diff --git a/emb/pastilda/hw/usb_host/usbh_host.h b/emb/pastilda/usb/usb_host/usbh_host.h index 868026b..868026b 100644 --- a/emb/pastilda/hw/usb_host/usbh_host.h +++ b/emb/pastilda/usb/usb_host/usbh_host.h |