Code import

This commit is contained in:
2017-12-15 23:52:14 +01:00
commit 56a935cdd0
103 changed files with 14510 additions and 0 deletions
+245
View File
@@ -0,0 +1,245 @@
/**
* @file file_stream.c
* @brief Implementation of file_stream.h
*
* DAPLink Interface Firmware
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <platform/status_led.h>
#include "platform.h"
#include "utils/ini_parser.h"
#include "framework/settings.h"
#include "file_stream.h"
#include "vfs_manager.h"
typedef enum {
STREAM_STATE_CLOSED,
STREAM_STATE_OPEN,
STREAM_STATE_END,
STREAM_STATE_ERROR
} stream_state_t;
typedef bool (*stream_detect_cb_t)(const uint8_t *data, uint32_t size);
typedef error_t (*stream_open_cb_t)(void *state);
typedef error_t (*stream_write_cb_t)(void *state, const uint8_t *data, uint32_t size);
typedef error_t (*stream_close_cb_t)(void *state);
typedef struct {
stream_detect_cb_t detect;
stream_open_cb_t open;
stream_write_cb_t write;
stream_close_cb_t close;
} stream_t;
typedef struct {
size_t file_pos;
} conf_state_t;
typedef union {
conf_state_t conf;
} shared_state_t;
static bool detect_conf(const uint8_t *data, uint32_t size);
static error_t open_conf(void *state);
static error_t write_conf(void *state, const uint8_t *data, uint32_t size);
static error_t close_conf(void *state);
stream_t stream[] = {
{detect_conf, open_conf, write_conf, close_conf}, // STREAM_TYPE_CONF
};
COMPILER_ASSERT(ELEMENTS_IN_ARRAY(stream) == STREAM_TYPE_COUNT);
// STREAM_TYPE_NONE must not be included in count
COMPILER_ASSERT(STREAM_TYPE_NONE > STREAM_TYPE_COUNT);
static shared_state_t shared_state;
static stream_state_t stream_state = STREAM_STATE_CLOSED;
static stream_t *current_stream = 0;
stream_type_t stream_start_identify(const uint8_t *data, uint32_t size)
{
stream_type_t i;
for (i = STREAM_TYPE_START; i < STREAM_TYPE_COUNT; i++) {
if (stream[i].detect(data, size)) {
return i;
}
}
return STREAM_TYPE_NONE;
}
// Identify the file type from its extension
stream_type_t stream_type_from_name(const vfs_filename_t filename)
{
// 8.3 file names must be in upper case
if (0 == strncmp("INI", &filename[8], 3)) {
// This is used only to verify we identified the file correctly (?)
return STREAM_TYPE_CONF;
} else {
return STREAM_TYPE_NONE;
}
}
error_t stream_open(stream_type_t stream_type)
{
error_t status;
// Stream must not be open already
if (stream_state != STREAM_STATE_CLOSED) {
vfs_printf("!! Stream is not closed, cant open");
assert_param(0);
return E_INTERNAL;
}
// Stream must be of a supported type
if (stream_type >= STREAM_TYPE_COUNT) {
vfs_printf("!! Stream bad type");
assert_param(0);
return E_INTERNAL;
}
StatusLed_On(STATUS_DISK_BUSY);
// TODO create a thread...?
// Initialize all variables
memset(&shared_state, 0, sizeof(shared_state));
stream_state = STREAM_STATE_OPEN;
current_stream = &stream[stream_type];
// Initialize the specified stream
status = current_stream->open(&shared_state);
if (E_SUCCESS != status) {
stream_state = STREAM_STATE_ERROR;
vfs_printf("!! not success");
}
return status;
}
error_t stream_write(const uint8_t *data, uint32_t size)
{
error_t status;
// Stream must be open already
if (stream_state != STREAM_STATE_OPEN) {
vfs_printf("!! Stream is not open, cant write");
assert_param(0);
return E_INTERNAL;
}
// Check thread after checking state since the stream thread is
// set only if stream_open has been called
//stream_thread_assert(); // ???
// Write to stream
status = current_stream->write(&shared_state, data, size);
if (E_SUCCESS_DONE == status) {
vfs_printf("Stream DONE");
stream_state = STREAM_STATE_END;
} else if ((E_SUCCESS_DONE_OR_CONTINUE == status) || (E_SUCCESS == status)) {
// Stream should remain in the open state
assert_param(STREAM_STATE_OPEN == stream_state);
vfs_printf("Stream may close or get more data.,,");
} else {
stream_state = STREAM_STATE_ERROR;
vfs_printf("!! FAIL in stream");
}
return status;
}
error_t stream_close(void)
{
error_t status;
// Stream must not be closed already
if (STREAM_STATE_CLOSED == stream_state) {
vfs_printf("!! Stream already closed");
assert_param(0);
return E_INTERNAL;
}
// Check thread after checking state since the stream thread is
// set only if stream_open has been called
// stream_thread_assert(); // ???
// Close stream
StatusLed_Off(STATUS_DISK_BUSY);
status = current_stream->close(&shared_state);
stream_state = STREAM_STATE_CLOSED;
return status;
}
static bool detect_conf(const uint8_t *data, uint32_t size)
{
// Here we have received the first sector of a potential INI file (assuming it's a whole sector, since
// this is called from the MSC driver).
//
// We can start parsing and look for the first section or some other marker. The file name is yet unknown
// and may not be known for a while - we cannot use that to detect anything, unless we buffer the entire file
// (a bad idea)
// TODO detect config file
return data[0] == '#'; // here we just assume everything is INI
}
static void iniparser_cb(const char *section, const char *key, const char *value, void *userData)
{
settings_read_ini(section, key, value);
}
static error_t open_conf(void *state)
{
conf_state_t *conf = state;
conf->file_pos = 0;
vfs_printf("\r\n---- INI OPEN! ----");
settings_read_ini_begin();
ini_parse_begin(iniparser_cb, NULL);
return E_SUCCESS;
}
static error_t write_conf(void *state, const uint8_t *data, uint32_t size)
{
conf_state_t *conf = state;
conf->file_pos += size;
vfs_printf("Writing INI - RX %d bytes", size);
vfs_printf_nonl("\033[92m", 5);
vfs_printf_nonl((const char *) data, size);
vfs_printf_nonl("\033[0m\r\n", 6);
ini_parse((const char *) data, size);
return E_SUCCESS_DONE_OR_CONTINUE; // indicate we don't really know if it's over or not
// TODO use some marker for EOF in the actual config files
}
static error_t close_conf(void *state)
{
conf_state_t *conf = state;
vfs_printf("Close INI, total bytes = %d", conf->file_pos);
ini_parse_end();
settings_read_ini_end();
// force a full remount to have the changes be visible
vfs_mngr_fs_remount(true);
return E_SUCCESS;
}
+61
View File
@@ -0,0 +1,61 @@
/**
* @file file_stream.h
* @brief Different file stream parsers that are supported
*
* DAPLink Interface Firmware
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef VFS_FILE_STREAM_H
#define VFS_FILE_STREAM_H
#include "platform.h"
#include "virtual_fs.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
STREAM_TYPE_START = 0,
STREAM_TYPE_CONF = STREAM_TYPE_START,
// STREAM_TYPE_HEX,
// Add new stream types here
STREAM_TYPE_COUNT,
STREAM_TYPE_NONE
} stream_type_t;
// Stateless function to identify a filestream by its contents
stream_type_t stream_start_identify(const uint8_t *data, uint32_t size);
// Stateless function to identify a filestream by its name
stream_type_t stream_type_from_name(const vfs_filename_t filename);
error_t stream_open(stream_type_t stream_type);
error_t stream_write(const uint8_t *data, uint32_t size);
error_t stream_close(void);
#ifdef __cplusplus
}
#endif
#endif//VFS_FILE_STREAM_H
+949
View File
@@ -0,0 +1,949 @@
/**
* @file vfs_manager.c
* @brief Implementation of vfs_manager.h
*
* DAPLink Interface Firmware
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <task_main.h>
#include "platform.h"
#include "task_main.h"
#include "virtual_fs.h"
#include "vfs_manager.h"
#include "file_stream.h"
#define INVALID_TIMEOUT_MS 0xFFFFFFFF
#define MAX_EVENT_TIME_MS 60000
#define CONNECT_DELAY_MS 0
#define RECONNECT_DELAY_MS 2500 // Must be above 1s for windows (more for linux)
// TRANSFER_IN_PROGRESS
#define DISCONNECT_DELAY_TRANSFER_TIMEOUT_MS 500 // was 20000 - this is triggered after a partial file upload
// TRANSFER_CAN_BE_FINISHED
#define DISCONNECT_DELAY_TRANSFER_IDLE_MS 500
// TRANSFER_NOT_STARTED || TRASNFER_FINISHED
#define DISCONNECT_DELAY_MS 500
// Make sure none of the delays exceed the max time
COMPILER_ASSERT(CONNECT_DELAY_MS < MAX_EVENT_TIME_MS);
COMPILER_ASSERT(RECONNECT_DELAY_MS < MAX_EVENT_TIME_MS);
COMPILER_ASSERT(DISCONNECT_DELAY_TRANSFER_TIMEOUT_MS < MAX_EVENT_TIME_MS);
COMPILER_ASSERT(DISCONNECT_DELAY_TRANSFER_IDLE_MS < MAX_EVENT_TIME_MS);
COMPILER_ASSERT(DISCONNECT_DELAY_MS < MAX_EVENT_TIME_MS);
volatile vfs_info_t vfs_info;
typedef enum {
TRANSFER_NOT_STARTED,
TRANSFER_IN_PROGRESS,
TRANSFER_CAN_BE_FINISHED,
TRASNFER_FINISHED,
} transfer_state_t;
typedef struct {
vfs_file_t file_to_program; // A pointer to the directory entry of the file being programmed
vfs_sector_t start_sector; // Start sector of the file being programmed
vfs_sector_t file_next_sector; // Expected next sector of the file
vfs_sector_t last_ooo_sector; // Last out of order sector within the file
uint32_t size_processed; // The number of bytes processed by the stream
uint32_t file_size; // Size of the file indicated by root dir. Only allowed to increase
uint32_t size_transferred; // The number of bytes transferred
transfer_state_t transfer_state;// Transfer state
bool stream_open; // State of the stream
bool stream_started; // Stream processing started. This only gets reset remount
bool stream_finished; // Stream processing is done. This only gets reset remount
bool stream_optional_finish; // True if the stream processing can be considered done
bool file_info_optional_finish; // True if the file transfer can be considered done
bool transfer_timeout; // Set if the transfer was finished because of a timeout. This only gets reset remount
stream_type_t stream; // Current stream or STREAM_TYPE_NONE is stream is closed. This only gets reset remount
} file_transfer_state_t;
typedef enum {
VFS_MNGR_STATE_DISCONNECTED,
VFS_MNGR_STATE_RECONNECTING,
VFS_MNGR_STATE_CONNECTED
} vfs_mngr_state_t;
static const file_transfer_state_t default_transfer_state = {
VFS_FILE_INVALID,
VFS_INVALID_SECTOR,
VFS_INVALID_SECTOR,
VFS_INVALID_SECTOR,
0,
0,
0,
TRANSFER_NOT_STARTED,
false,
false,
false,
false,
false,
false,
STREAM_TYPE_NONE,
};
//static uint32_t usb_buffer[VFS_SECTOR_SIZE / sizeof(uint32_t)];
static error_t fail_reason = E_SUCCESS;
static file_transfer_state_t file_transfer_state;
// These variables can be access from multiple threads
// so access to them must be synchronized
static vfs_mngr_state_t vfs_state;
static vfs_mngr_state_t vfs_state_next;
static bool vfs_next_remount_full = false;
static uint32_t time_usb_idle;
static osStaticMutexDef_t vfsMutexControlBlock;
static osSemaphoreId vfsMutexHandle = NULL;
// Synchronization functions
static void sync_init(void);
static void sync_assert_usb_thread(void);
static void sync_lock(void);
static void sync_unlock(void);
static bool changing_state(void);
static void build_filesystem(void);
static void file_change_handler(const vfs_filename_t filename, vfs_file_change_t change, vfs_file_t file, vfs_file_t new_file_data);
static void file_data_handler(uint32_t sector, const uint8_t *buf, uint32_t num_of_sectors);
static bool ready_for_state_change(void);
static void abort_remount(void);
static void transfer_update_file_info(vfs_file_t file, uint32_t start_sector, uint32_t size, stream_type_t stream);
static void transfer_reset_file_info(void);
static void transfer_stream_open(stream_type_t stream, uint32_t start_sector);
static void transfer_stream_data(uint32_t sector, const uint8_t *data, uint32_t size);
static void transfer_update_state(error_t status);
void vfs_mngr_fs_enable(bool enable)
{
sync_lock();
vfs_printf("Enable = %d", enable);
if (enable) {
if (VFS_MNGR_STATE_DISCONNECTED == vfs_state_next) {
vfs_printf(" Switch to Connected");
vfs_state_next = VFS_MNGR_STATE_CONNECTED;
} else {
vfs_printf(" no switch");
};
} else {
vfs_printf(" Switch to DISconnected");
vfs_state_next = VFS_MNGR_STATE_DISCONNECTED;
}
sync_unlock();
}
void vfs_mngr_fs_remount(bool force_full)
{
sync_lock();
// Only start a remount if in the connected state and not in a transition
if (!changing_state() && (VFS_MNGR_STATE_CONNECTED == vfs_state)) {
vfs_state_next = VFS_MNGR_STATE_RECONNECTING;
}
vfs_next_remount_full |= force_full;
sync_unlock();
}
void vfs_mngr_init(bool enable)
{
vfs_printf("vfs_mngr_init");
sync_assert_usb_thread();
build_filesystem();
if (enable) {
vfs_state = VFS_MNGR_STATE_CONNECTED;
vfs_state_next = VFS_MNGR_STATE_CONNECTED;
vfs_info.MediaReady = 1;
} else {
vfs_state = VFS_MNGR_STATE_DISCONNECTED;
vfs_state_next = VFS_MNGR_STATE_DISCONNECTED;
vfs_info.MediaReady = 0;
}
vfs_info.MediaChanged = 0;
}
void vfs_mngr_periodic(uint32_t elapsed_ms)
{
bool change_state;
vfs_mngr_state_t vfs_state_local;
vfs_mngr_state_t vfs_state_local_prev;
sync_assert_usb_thread();
sync_lock();
// Return immediately if the desired state has been reached
if (!changing_state()) {
sync_unlock();
return;
}
change_state = ready_for_state_change();
if (time_usb_idle < MAX_EVENT_TIME_MS) {
time_usb_idle += elapsed_ms;
}
if (!change_state) {
sync_unlock();
return;
}
vfs_printf("vfs_mngr_periodic()\r\n");
vfs_printf(" time_usb_idle=%i\r\n", time_usb_idle);
vfs_printf(" transfer_state=%i\r\n", file_transfer_state.transfer_state);
// Transition to new state
vfs_state_local_prev = vfs_state;
vfs_state = vfs_state_next;
switch (vfs_state) {
case VFS_MNGR_STATE_RECONNECTING:
// Transition back to the connected state
vfs_state_next = VFS_MNGR_STATE_CONNECTED;
break;
default:
// No state change logic required in other states
break;
}
vfs_state_local = vfs_state;
time_usb_idle = 0;
sync_unlock();
// Processing when leaving a state
vfs_printf(" state %i->%i\r\n", vfs_state_local_prev, vfs_state_local);
bool want_notify_only = false; // Use this if the transfer timed out and we dont need full reconnect
switch (vfs_state_local_prev) {
case VFS_MNGR_STATE_DISCONNECTED:
// No action needed
break;
case VFS_MNGR_STATE_RECONNECTING:
// No action needed
break;
case VFS_MNGR_STATE_CONNECTED:
// Close ongoing transfer if there is one
if (file_transfer_state.transfer_state != TRASNFER_FINISHED) {
vfs_printf(" transfer timeout\r\n");
file_transfer_state.transfer_timeout = true;
transfer_update_state(E_SUCCESS);
want_notify_only = true;
}
assert_param(TRASNFER_FINISHED == file_transfer_state.transfer_state);
vfs_user_disconnecting();
break;
}
// Maybe make this configurable?
//want_notify_only = false;
// Processing when entering a state
switch (vfs_state_local) {
case VFS_MNGR_STATE_DISCONNECTED:
vfs_printf("+DISCON");
if (want_notify_only && !vfs_next_remount_full) {
vfs_info.MediaChanged = 1;
vfs_printf("Notify media change");
} else {
vfs_info.MediaReady = 0;
vfs_printf("Reconnect mass storage");
}
vfs_next_remount_full = false;
break;
case VFS_MNGR_STATE_RECONNECTING:
vfs_printf("+RECON");
if (want_notify_only && !vfs_next_remount_full) {
vfs_info.MediaChanged = 1;
vfs_printf("Notify media change");
} else {
vfs_info.MediaReady = 0;
vfs_printf("Reconnect mass storage");
}
vfs_next_remount_full = false;
break;
case VFS_MNGR_STATE_CONNECTED:
vfs_printf("+CONNECTED");
build_filesystem();
vfs_info.MediaReady = 1;
break;
}
return;
}
error_t vfs_mngr_get_transfer_status(void)
{
sync_assert_usb_thread();
return fail_reason;
}
void vfs_if_usbd_msc_init(void)
{
sync_init();
build_filesystem();
vfs_state = VFS_MNGR_STATE_DISCONNECTED;
vfs_state_next = VFS_MNGR_STATE_DISCONNECTED;
time_usb_idle = 0;
vfs_info.MediaReady = 0;
vfs_info.MediaChanged = 0;
vfs_printf("vfs_if_usbd_msc_init");
}
void vfs_if_usbd_msc_read_sect(uint32_t sector, uint8_t *buf, uint32_t num_of_sectors)
{
sync_assert_usb_thread();
// /* this is very spammy */ vfs_printf("\033[35mREAD @ %d, len %d\033[0m", sector, num_of_sectors);
// dont proceed if we're not ready
if (!vfs_info.MediaReady) {
vfs_printf("Not Rdy");
return;
}
// indicate msc activity
// main_blink_msc_led(MAIN_LED_OFF);
vfs_read(sector, buf, num_of_sectors);
}
void vfs_if_usbd_msc_write_sect(uint32_t sector, uint8_t *buf, uint32_t num_of_sectors)
{
sync_assert_usb_thread();
vfs_printf("\033[32mWRITE @ %d, len %d\033[0m", sector, num_of_sectors);
if (buf[0] == 0xF8 && buf[1] == 0xFF && buf[2] == 0xFF && buf[3] == 0xFF) {
vfs_printf("Discard write of F8,FF,FF,FF");
return;
}
if (!vfs_info.MediaReady) {
vfs_printf("Not Rdy");
return;
}
// Restart the disconnect counter on every packet
// so the device does not detach in the middle of a
// transfer.
time_usb_idle = 0;
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) {
vfs_printf("Xfer done.");
return;
}
// indicate msc activity
// main_blink_msc_led(MAIN_LED_OFF);
vfs_printf("call vfs_write");
vfs_write(sector, buf, num_of_sectors);
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) {
vfs_printf("Xfer done now.");
return;
}
file_data_handler(sector, buf, num_of_sectors);
}
static void sync_init(void)
{
osMutexStaticDef(vfsMutex, &vfsMutexControlBlock);
vfsMutexHandle = osMutexCreate(osMutex(vfsMutex));
}
static inline void sync_assert_usb_thread(void)
{
assert_param(osThreadGetId() == tskMainHandle);
}
static void sync_lock(void)
{
assert_param(osOK == osMutexWait(vfsMutexHandle, 100));
}
static void sync_unlock(void)
{
assert_param(osOK == osMutexRelease(vfsMutexHandle));
}
static bool changing_state(void)
{
return vfs_state != vfs_state_next;
}
static void build_filesystem(void)
{
// Update anything that could have changed file system state
file_transfer_state = default_transfer_state;
vfs_user_build_filesystem();
vfs_set_file_change_callback(file_change_handler);
// Set mass storage parameters
vfs_info.MemorySize = vfs_get_total_size();
vfs_info.BlockSize = VFS_SECTOR_SIZE;
vfs_info.BlockGroup = 1;
vfs_info.BlockCount = vfs_info.MemorySize / vfs_info.BlockSize;
// vfs_info.BlockBuf = (uint8_t *) usb_buffer;
}
static void switch_to_new_file(stream_type_t stream, uint32_t start_sector, bool andReopen)
{ // This should close the stream
vfs_printf("****** NEW FILE STREAM! *******");
if (!file_transfer_state.transfer_state) {
file_transfer_state.transfer_timeout = true;
transfer_update_state(E_SUCCESS);
} else {
if (file_transfer_state.stream_open) {
stream_close();
file_transfer_state.stream_open = false;
}
}
if (andReopen) {
// and we start anew
file_transfer_state.start_sector = VFS_INVALID_SECTOR; // pretend we have no srtart sector yet!
transfer_stream_open(stream, start_sector);
}
}
// Callback to handle changes to the root directory. Should be used with vfs_set_file_change_callback
static void file_change_handler(const vfs_filename_t filename, vfs_file_change_t change, vfs_file_t file, vfs_file_t new_file_data)
{
vfs_printf("\033[33m@file_change_handler\033[0m (name=%*s, file=%p, ftp=%p, change=%i)\r\n", 11, filename, file, change);
vfs_user_file_change_handler(filename, change, file, new_file_data);
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) {
// If the transfer is finished stop further processing
vfs_printf("> Transfer is finished.");
return;
}
if (VFS_FILE_CHANGED == change) {
vfs_printf("> Change");
if (file == file_transfer_state.file_to_program) {
vfs_printf(" Stream is open, continue");
stream_type_t stream;
uint32_t size = vfs_file_get_size(new_file_data);
vfs_sector_t sector = vfs_file_get_start_sector(new_file_data);
stream = stream_type_from_name(filename);
transfer_update_file_info(file, sector, size, stream);
} else {
vfs_printf(" No stream.");
}
}
if (VFS_FILE_CREATED == change) {
stream_type_t stream;
vfs_printf("> Created");
if (STREAM_TYPE_NONE != stream_type_from_name(filename)) {
vfs_printf(" Stream is open, continue");
// Check for a know file extension to detect the current file being
// transferred. Ignore hidden files since MAC uses hidden files with
// the same extension to keep track of transfer info in some cases.
if (!(VFS_FILE_ATTR_HIDDEN & vfs_file_get_attr(new_file_data))) {
stream = stream_type_from_name(filename);
uint32_t size = vfs_file_get_size(new_file_data);
vfs_sector_t sector = vfs_file_get_start_sector(new_file_data);
transfer_update_file_info(file, sector, size, stream);
}
} else {
vfs_printf(" No matching stream found!");
}
}
if (VFS_FILE_DELETED == change) {
vfs_printf("> Deleted");
if (file == file_transfer_state.file_to_program) {
vfs_printf(" Deleted transferred file!");
// The file that was being transferred has been deleted
transfer_reset_file_info();
} else {
vfs_printf("Delete other file");
}
}
}
// Handler for file data arriving over USB. This function is responsible
// for detecting the start of a BIN/HEX file and performing programming
static void file_data_handler(uint32_t sector, const uint8_t *buf, uint32_t num_of_sectors)
{
stream_type_t stream;
uint32_t size;
vfs_printf("\033[33m@file_data_handler\033[0m (sec=%d, num=%d)", sector, num_of_sectors);
if (sector <= 1) {
vfs_printf("Discard write to sector %d", sector);
return;
}
// this is the key for starting a file write - we dont care what file types are sent
// just look for something unique (NVIC table, hex, srec, etc) until root dir is updated
if (!file_transfer_state.stream_started) {
vfs_printf("Stream not started yet");
// look for file types we can program
stream = stream_start_identify((uint8_t *) buf, VFS_SECTOR_SIZE * num_of_sectors);
if (STREAM_TYPE_NONE != stream) {
vfs_printf("Opening a stream...");
transfer_stream_open(stream, sector);
}
}
if (file_transfer_state.stream_started) {
vfs_printf("Stream is open, check if we can write ....");
// // Ignore sectors coming before this file
// if (sector < file_transfer_state.start_sector) {
// vfs_printf("Sector ABOVE current file!?");
// return;
// }
// sectors must be in order
if (sector != file_transfer_state.file_next_sector) {
vfs_printf("file_data_handler BAD sector=%i\r\n", sector);
// Try to find what file this belongs to, if any
// OS sometimes first writes the FAT and then the individual files,
// so it looks to us as a discontinuous file (in the better case)
vfs_filename_t fname;
vfs_file_t *file;
if (vfs_find_file(sector, &fname, &file)) {
vfs_printf("FOUND A FILE!! matches to %s", fname);
file_transfer_state.file_to_program = file;
stream = stream_start_identify((uint8_t *) buf, VFS_SECTOR_SIZE * num_of_sectors);
if (stream != STREAM_TYPE_NONE) {
switch_to_new_file(stream, sector, true);
goto proceed;
}
vfs_printf("No stream can handle this, give up. Could be kateswap junk (1)\r\n");
return;
}
if (sector >= file_transfer_state.start_sector && sector < file_transfer_state.file_next_sector) {
vfs_printf(" sector out of order! lowest ooo = %i\r\n",
file_transfer_state.last_ooo_sector);
if (VFS_INVALID_SECTOR == file_transfer_state.last_ooo_sector) {
file_transfer_state.last_ooo_sector = sector;
}
file_transfer_state.last_ooo_sector =
MIN(file_transfer_state.last_ooo_sector, sector);
} else {
vfs_printf(" sector not part of file transfer\r\n");
// BUT!! this can be a whole different file written elsewhere
// Let's try it.
if (sector > 70) {
// this is a guess as to where the actual data can start - usually 34 and 67 are some garbage in the FAT(s)
stream = stream_start_identify((uint8_t *) buf, VFS_SECTOR_SIZE * num_of_sectors);
if (stream != STREAM_TYPE_NONE) {
switch_to_new_file(stream, sector, true);
goto proceed;
}
vfs_printf("No stream can handle this, give up. Could be kateswap junk (2)\r\n");
return;
}
}
vfs_printf(" discarding data - size transferred=0x%x\r\n",
file_transfer_state.size_transferred);
vfs_printf_nonl("\033[31m", 5);
vfs_printf_nonl((const char *) buf, VFS_SECTOR_SIZE * num_of_sectors);
vfs_printf_nonl("\033[0m\r\n", 6);
return;
} else {
vfs_printf("sector is good");
}
proceed:
// This sector could be part of the file so record it
size = VFS_SECTOR_SIZE * num_of_sectors;
file_transfer_state.size_transferred += size;
file_transfer_state.file_next_sector = sector + num_of_sectors;
// If stream processing is done then discard the data
if (file_transfer_state.stream_finished) {
vfs_printf("vfs_manager file_data_handler\r\n sector=%i, size=%i\r\n", sector, size);
vfs_printf(" discarding data - size transferred=0x%x\r\n",
file_transfer_state.size_transferred);
vfs_printf_nonl("\033[31m", 5);
vfs_printf_nonl((const char *) buf, VFS_SECTOR_SIZE * num_of_sectors);
vfs_printf_nonl("\033[0m\r\n", 6);
transfer_update_state(E_SUCCESS);
return;
} else {
vfs_printf("stream is not finished, can handle...");
}
transfer_stream_data(sector, buf, size);
} else {
vfs_printf("Stream not started!!!!!!");
}
}
static bool ready_for_state_change(void)
{
uint32_t timeout_ms = INVALID_TIMEOUT_MS;
assert_param(vfs_state != vfs_state_next);
if (VFS_MNGR_STATE_CONNECTED == vfs_state) {
switch (file_transfer_state.transfer_state) {
case TRANSFER_NOT_STARTED:
case TRASNFER_FINISHED:
timeout_ms = DISCONNECT_DELAY_MS;
break;
case TRANSFER_IN_PROGRESS:
timeout_ms = DISCONNECT_DELAY_TRANSFER_TIMEOUT_MS;
break;
case TRANSFER_CAN_BE_FINISHED:
timeout_ms = DISCONNECT_DELAY_TRANSFER_IDLE_MS;
break;
default:
assert_param(0);
timeout_ms = DISCONNECT_DELAY_MS;
break;
}
} else if ((VFS_MNGR_STATE_DISCONNECTED == vfs_state) &&
(VFS_MNGR_STATE_CONNECTED == vfs_state_next)) {
timeout_ms = CONNECT_DELAY_MS;
} else if ((VFS_MNGR_STATE_RECONNECTING == vfs_state) &&
(VFS_MNGR_STATE_CONNECTED == vfs_state_next)) {
timeout_ms = RECONNECT_DELAY_MS;
} else if ((VFS_MNGR_STATE_RECONNECTING == vfs_state) &&
(VFS_MNGR_STATE_DISCONNECTED == vfs_state_next)) {
timeout_ms = 0;
}
if (INVALID_TIMEOUT_MS == timeout_ms) {
assert_param(0);
timeout_ms = 0;
}
return time_usb_idle > timeout_ms ? true : false;
}
// Abort a remount if one is pending
void abort_remount(void)
{
sync_lock();
// Only abort a remount if in the connected state and reconnecting is the next state
if ((VFS_MNGR_STATE_RECONNECTING == vfs_state_next) && (VFS_MNGR_STATE_CONNECTED == vfs_state)) {
vfs_state_next = VFS_MNGR_STATE_CONNECTED;
}
sync_unlock();
}
// Update the tranfer state with file information
static void transfer_update_file_info(vfs_file_t file, uint32_t start_sector, uint32_t size, stream_type_t stream)
{
vfs_printf("\033[33m@transfer_update_file_info\033[0m (file=%p, start_sector=%i, size=%i)\r\n", file, start_sector, size);
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) {
assert_param(0);
return;
}
// Initialize the directory entry if it has not been set
if (VFS_FILE_INVALID == file_transfer_state.file_to_program) {
file_transfer_state.file_to_program = file;
if (file != VFS_FILE_INVALID) {
vfs_printf(" file_to_program=%p\r\n", file);
}
}
// Initialize the starting sector if it has not been set
if (VFS_INVALID_SECTOR == file_transfer_state.start_sector) {
file_transfer_state.start_sector = start_sector;
if (start_sector != VFS_INVALID_SECTOR) {
vfs_printf(" start_sector=%i\r\n", start_sector);
}
}
// Initialize the stream if it has not been set
if (STREAM_TYPE_NONE == file_transfer_state.stream) {
file_transfer_state.stream = stream;
if (stream != STREAM_TYPE_NONE) {
vfs_printf(" stream=%i\r\n", stream);
}
}
// Check - File size must either grow or be smaller than the size already transferred
if ((size < file_transfer_state.file_size) && (size < file_transfer_state.size_transferred)) {
vfs_printf(" error: file size changed from %i to %i\r\n", file_transfer_state.file_size, size);
// this is probably a new file
trap("File shrinks");//XXX
switch_to_new_file(stream, start_sector, true);
}
// Check - Starting sector must be the same - this is optional for file info since it may not be present initially
if ((VFS_INVALID_SECTOR != start_sector) && (start_sector != file_transfer_state.start_sector)) {
vfs_printf(" error: starting sector changed from %i to %i\r\n", file_transfer_state.start_sector, start_sector);
// this is probably a new file
trap("Changed start offset");//XXX
switch_to_new_file(stream, start_sector, true);
}
// Check - stream must be the same
if (stream != file_transfer_state.stream) {
vfs_printf(" error: changed types during transfer from %i to %i\r\n", stream, file_transfer_state.stream);
transfer_update_state(E_ERROR_DURING_TRANSFER);
return;
}
// Update values - Size is the only value that can change
file_transfer_state.file_size = size;
vfs_printf(" updated size=%i\r\n", size);
transfer_update_state(E_SUCCESS);
}
// Reset the transfer information or error if transfer is already in progress
static void transfer_reset_file_info(void)
{
vfs_printf("vfs_manager transfer_reset_file_info()\r\n");
if (file_transfer_state.stream_open) {
transfer_update_state(E_ERROR_DURING_TRANSFER);
} else {
file_transfer_state = default_transfer_state;
abort_remount();
}
}
// Update the tranfer state with new information
static void transfer_stream_open(stream_type_t stream, uint32_t start_sector)
{
error_t status;
assert_param(!file_transfer_state.stream_open);
assert_param(start_sector != VFS_INVALID_SECTOR);
vfs_printf("\033[33m@transfer_stream_open\033[0m (stream=%i, start_sector=%i)\r\n",
stream, start_sector);
// Check - Starting sector must be the same
if (start_sector != file_transfer_state.start_sector && file_transfer_state.start_sector != VFS_INVALID_SECTOR) {
vfs_printf(" error: starting sector changed from %i to %i\r\n", file_transfer_state.start_sector, start_sector);
// this is probably a new file
switch_to_new_file(stream, start_sector, false);
file_transfer_state.start_sector = VFS_INVALID_SECTOR;
}
// Check - stream must be the same
if (stream != file_transfer_state.stream && file_transfer_state.stream != STREAM_TYPE_NONE) {
vfs_printf(" error: changed types during tranfer from %i to %i\r\n", stream, file_transfer_state.stream);
// this is probably a new file
switch_to_new_file(stream, start_sector, false);
file_transfer_state.start_sector = VFS_INVALID_SECTOR;
}
// Initialize the starting sector if it has not been set
if (VFS_INVALID_SECTOR == file_transfer_state.start_sector) {
file_transfer_state.start_sector = start_sector;
if (start_sector != VFS_INVALID_SECTOR) {
vfs_printf(" start_sector=%i\r\n", start_sector);
}
}
// Initialize the stream if it has not been set
if (STREAM_TYPE_NONE == file_transfer_state.stream) {
file_transfer_state.stream = stream;
if (stream != STREAM_TYPE_NONE) {
vfs_printf(" stream=%i\r\n", stream);
}
}
// Open stream
status = stream_open(stream);
vfs_printf(" stream_open stream=%i ret %i\r\n", stream, status);
if (E_SUCCESS == status) {
file_transfer_state.file_next_sector = start_sector;
file_transfer_state.stream_open = true;
file_transfer_state.stream_started = true;
}
transfer_update_state(status);
}
// Update the tranfer state with new information
static void transfer_stream_data(uint32_t sector, const uint8_t *data, uint32_t size)
{
error_t status;
vfs_printf("\033[33m@transfer_stream_data\033[0m (sector=%i, size=%i)\r\n", sector, size);
vfs_printf(" size processed=0x%x, data=%x,%x,%x,%x,...\r\n",
file_transfer_state.size_processed, data[0], data[1], data[2], data[3]);
if (file_transfer_state.stream_finished) {
assert_param(0);
return;
}
assert_param(size % VFS_SECTOR_SIZE == 0);
assert_param(file_transfer_state.stream_open);
status = stream_write((uint8_t *) data, size);
vfs_printf(" stream_write ret=%i\r\n", status);
if (E_SUCCESS_DONE == status) {
// Override status so E_SUCCESS_DONE
// does not get passed into transfer_update_state
status = stream_close();
vfs_printf(" stream_close ret=%i\r\n", status);
file_transfer_state.stream_open = false;
file_transfer_state.stream_finished = true;
file_transfer_state.stream_optional_finish = true;
} else if (E_SUCCESS_DONE_OR_CONTINUE == status) {
status = E_SUCCESS;
file_transfer_state.stream_optional_finish = true;
} else {
file_transfer_state.stream_optional_finish = false;
}
file_transfer_state.size_processed += size;
transfer_update_state(status);
}
// Check if the current transfer is still in progress, done, or if an error has occurred
static void transfer_update_state(error_t status)
{
bool transfer_timeout;
bool transfer_started;
bool transfer_can_be_finished;
bool transfer_must_be_finished;
bool out_of_order_sector;
error_t local_status = status;
assert_param((status != E_SUCCESS_DONE) &&
(status != E_SUCCESS_DONE_OR_CONTINUE));
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) {
assert_param(0);
return;
}
// Update file info status. The end of a file is never known for sure since
// what looks like a complete file could be part of a file getting flushed to disk.
// The criteria for an successful optional finish is
// 1. A file has been detected
// 2. The size of the file indicated in the root dir has been transferred
// 3. The file size is greater than zero
file_transfer_state.file_info_optional_finish =
(file_transfer_state.file_to_program != VFS_FILE_INVALID) &&
(file_transfer_state.size_transferred >= file_transfer_state.file_size) &&
(file_transfer_state.file_size > 0);
transfer_timeout = file_transfer_state.transfer_timeout;
transfer_started = (VFS_FILE_INVALID != file_transfer_state.file_to_program) ||
(STREAM_TYPE_NONE != file_transfer_state.stream);
// The transfer can be finished if both file and stream processing
// can be considered complete
transfer_can_be_finished = file_transfer_state.file_info_optional_finish &&
file_transfer_state.stream_optional_finish;
// The transfer must be fnished if stream processing is for sure complete
// and file processing can be considered complete
transfer_must_be_finished = file_transfer_state.stream_finished &&
file_transfer_state.file_info_optional_finish;
out_of_order_sector = false;
if (file_transfer_state.last_ooo_sector != VFS_INVALID_SECTOR) {
assert_param(file_transfer_state.start_sector != VFS_INVALID_SECTOR);
uint32_t sector_offset = (file_transfer_state.last_ooo_sector -
file_transfer_state.start_sector) * VFS_SECTOR_SIZE;
if (sector_offset < file_transfer_state.size_processed) {
// The out of order sector was within the range of data already
// processed.
out_of_order_sector = true;
}
}
// Set the transfer state and set the status if necessary
if (local_status != E_SUCCESS) {
file_transfer_state.transfer_state = TRASNFER_FINISHED;
} else if (transfer_timeout) {
if (out_of_order_sector) {
local_status = E_OOO_SECTOR;
} else if (!transfer_started) {
local_status = E_SUCCESS;
} else if (transfer_can_be_finished) {
local_status = E_SUCCESS;
} else {
local_status = E_TRANSFER_TIMEOUT;
}
file_transfer_state.transfer_state = TRASNFER_FINISHED;
} else if (transfer_must_be_finished) {
file_transfer_state.transfer_state = TRASNFER_FINISHED;
} else if (transfer_can_be_finished) {
file_transfer_state.transfer_state = TRANSFER_CAN_BE_FINISHED;
} else if (transfer_started) {
file_transfer_state.transfer_state = TRANSFER_IN_PROGRESS;
}
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) {
vfs_printf("vfs_manager transfer_update_state(status=%i)\r\n", status);
vfs_printf(" file=%p, start_sect= %i, size=%i\r\n",
file_transfer_state.file_to_program, file_transfer_state.start_sector,
file_transfer_state.file_size);
vfs_printf(" stream=%i, size_processed=%i, opt_finish=%i, timeout=%i\r\n",
file_transfer_state.stream, file_transfer_state.size_processed,
file_transfer_state.file_info_optional_finish, transfer_timeout);
// Close the file stream if it is open
if (file_transfer_state.stream_open) {
error_t close_status;
close_status = stream_close();
vfs_printf(" stream closed ret=%i\r\n", close_status);
file_transfer_state.stream_open = false;
if (E_SUCCESS == local_status) {
local_status = close_status;
}
}
// Set the fail reason
fail_reason = local_status;
vfs_printf(" Transfer finished, status: %i=%s\r\n", fail_reason, error_get_string(fail_reason));
}
// If this state change is not from aborting a transfer
// due to a remount then trigger a remount
if (!transfer_timeout) {
vfs_printf("~~ request Remount from transfer_update_state()");
vfs_mngr_fs_remount(false);
}
}
+91
View File
@@ -0,0 +1,91 @@
/**
* @file vfs_manager.h
* @brief Methods that build and manipulate a virtual file system
*
* DAPLink Interface Firmware
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef VFS_MANAGER_USER_H
#define VFS_MANAGER_USER_H
#include "platform.h"
#include "virtual_fs.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Callable from anywhere */
// Enable or disable the virtual filesystem
void vfs_mngr_fs_enable(bool enabled);
// Remount the virtual filesystem
void vfs_mngr_fs_remount(bool force_full);
/* Callable only from the thread running the virtual fs */
// Initialize the VFS manager
// Must be called after USB has been initialized (usbd_init())
// Notes: Must only be called from the thread runnning USB
void vfs_mngr_init(bool enabled);
// Run the vfs manager state machine
// Notes: Must only be called from the thread runnning USB
void vfs_mngr_periodic(uint32_t elapsed_ms);
// Return the status of the last transfer or E_SUCCESS
// if none have been performed yet
error_t vfs_mngr_get_transfer_status(void);
/* Use functions */
// Build the filesystem by calling vfs_init and then adding files with vfs_create_file
void vfs_user_build_filesystem(void);
// Called when a file on the filesystem changes
void vfs_user_file_change_handler(const vfs_filename_t filename, vfs_file_change_t change, vfs_file_t file, vfs_file_t new_file_data);
// Called when VFS is disconnecting
void vfs_user_disconnecting(void);
// --- interface ---
void vfs_if_usbd_msc_init(void);
void vfs_if_usbd_msc_read_sect(uint32_t sector, uint8_t *buf, uint32_t num_of_sectors);
void vfs_if_usbd_msc_write_sect(uint32_t sector, uint8_t *buf, uint32_t num_of_sectors);
typedef struct {
uint32_t MemorySize;
uint16_t BlockSize;
uint32_t BlockGroup; // LUN?
uint32_t BlockCount;
// uint8_t *BlockBuf; // apparently unused :thaenkin:
bool MediaReady;
bool MediaChanged;
} vfs_info_t;
extern volatile vfs_info_t vfs_info;
#ifdef __cplusplus
}
#endif
#endif// VFS_MANAGER_USER_H
+90
View File
@@ -0,0 +1,90 @@
/**
* @file vfs_user.c
* @brief Implementation of vfs_user.h
*
* DAPLink Interface Firmware
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utils/ini_writer.h"
#include "framework/settings.h"
#include "platform.h"
#include "vfs_manager.h"
const vfs_filename_t daplink_drive_name = "VIRTUALFS";
// File callback to be used with vfs_add_file to return file contents
static uint32_t read_file_config_ini(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
{
vfs_printf("Read config.ini");
const uint32_t avail = num_sectors*VFS_SECTOR_SIZE;
const uint32_t skip = sector_offset*VFS_SECTOR_SIZE;
IniWriter iw = iw_init((char *)data, skip, avail);
settings_write_ini(&iw);
return avail - iw.count;
}
//
static void write_file_config_ini(uint32_t sector_offset, const uint8_t *data, uint32_t num_sectors)
{
vfs_printf("Write CONFIG.INI, so %d, ns %d", sector_offset, num_sectors);
for(uint32_t i=0;i<num_sectors*VFS_SECTOR_SIZE;i++) {
PRINTF("%c", data[i]);
}
}
void vfs_user_build_filesystem(void)
{
dbg("Rebuilding VFS...");
// Setup the filesystem based on target parameters
vfs_init(daplink_drive_name, 0/*unused*/);
vfs_create_file("CONFIG INI", read_file_config_ini, write_file_config_ini, settings_get_ini_len());
}
// Callback to handle changes to the root directory. Should be used with vfs_set_file_change_callback
void vfs_user_file_change_handler(const vfs_filename_t filename,
vfs_file_change_t change,
vfs_file_t file, vfs_file_t new_file_data)
{
if (VFS_FILE_CHANGED == change) {
// Unused
vfs_printf(">>> CHANGED %s", filename);
}
if (VFS_FILE_CREATED == change) {
// do something based on the filename here
vfs_printf(">>> CREATED %s", filename);
}
if (VFS_FILE_DELETED == change) {
//
vfs_printf(">>> DELETED %s", filename);
}
}
void vfs_user_disconnecting(void)
{
// maybe reset...
vfs_printf("vfs_user_disconnecting");
}
+804
View File
@@ -0,0 +1,804 @@
/**
* @file virtual_fs.c
* @brief Implementation of virtual_fs.h
*
* DAPLink Interface Firmware
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "platform.h"
#include "virtual_fs.h"
// Virtual file system driver
// Limitations:
// - files must be contiguous
// - data written cannot be read back
// - data should only be read once
// FAT16 limitations +- safety margin
#define FAT_CLUSTERS_MAX (65525 - 100)
#define FAT_CLUSTERS_MIN (4086 + 100)
#define DIRTY_MBR_BOOTCODE 1
typedef struct
{
uint8_t boot_sector[11];
/* DOS 2.0 BPB - Bios Parameter Block, 11 bytes */
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_logical_sectors;
uint8_t num_fats;
uint16_t max_root_dir_entries;
uint16_t total_logical_sectors;
uint8_t media_descriptor;
uint16_t logical_sectors_per_fat;
/* DOS 3.31 BPB - Bios Parameter Block, 12 bytes */
uint16_t physical_sectors_per_track;
uint16_t heads;
uint32_t hidden_sectors;
uint32_t big_sectors_on_drive;
/* Extended BIOS Parameter Block, 26 bytes */
uint8_t physical_drive_number;
uint8_t not_used;
uint8_t boot_record_signature;
uint32_t volume_id;
char volume_label[11];
char file_system_type[8];
#if !DIRTY_MBR_BOOTCODE
/* bootstrap data in bytes 62-509 */
uint8_t bootstrap[448];
/* These entries in place of bootstrap code are the *nix partitions */
//uint8_t partition_one[16];
//uint8_t partition_two[16];
//uint8_t partition_three[16];
//uint8_t partition_four[16];
/* Mandatory value at bytes 510-511, must be 0xaa55 */
uint16_t signature; // but only if bootable...
#endif
} __attribute__((packed)) mbr_t;
typedef struct file_allocation_table
{
uint8_t f[512];
} file_allocation_table_t;
typedef struct FatDirectoryEntry
{
vfs_filename_t filename;
uint8_t attributes;
uint8_t reserved;
uint8_t creation_time_ms;
uint16_t creation_time;
uint16_t creation_date;
uint16_t accessed_date;
uint16_t first_cluster_high_16;
uint16_t modification_time;
uint16_t modification_date;
uint16_t first_cluster_low_16;
uint32_t filesize;
} __attribute__((packed)) FatDirectoryEntry_t;
COMPILER_ASSERT(sizeof(FatDirectoryEntry_t) == 32);
// to save RAM all files must be in the first root dir entry (512 bytes)
// but 2 actually exist on disc (32 entries) to accomodate hidden OS files,
// folders and metadata
typedef struct root_dir
{
FatDirectoryEntry_t f[VFS_MAX_FILES * 2];
} root_dir_t;
typedef struct virtual_media
{
vfs_read_cb_t read_cb;
vfs_write_cb_t write_cb;
uint32_t length;
} virtual_media_t;
static uint32_t read_zero(uint32_t offset, uint8_t *data, uint32_t size);
static void write_none(uint32_t offset, const uint8_t *data, uint32_t size);
static uint32_t read_mbr(uint32_t offset, uint8_t *data, uint32_t size);
static uint32_t read_fat(uint32_t offset, uint8_t *data, uint32_t size);
static uint32_t read_dir(uint32_t offset, uint8_t *data, uint32_t size);
static void write_dir(uint32_t offset, const uint8_t *data, uint32_t size);
static void file_change_cb_stub(const vfs_filename_t filename, vfs_file_change_t change,
vfs_file_t file, vfs_file_t new_file_data);
static uint32_t cluster_to_sector(uint32_t cluster_idx);
static bool filename_valid(const vfs_filename_t filename);
static bool filename_character_valid(char character);
static void set_init_done(void);
#if 0
void unused() {
// Initialize MBR
// memcpy(&mbr, &mbr_tmpl, sizeof(mbr_t));
total_sectors = ((VFS_DISK_SIZE + KB(64)) / VFS_SECTOR_SIZE);
// Make sure this is the right size for a FAT16 volume
if (total_sectors < FAT_CLUSTERS_MIN * VFS_CLUSTER_SIZE) {
assert_param(0);
total_sectors = FAT_CLUSTERS_MIN * VFS_CLUSTER_SIZE;
} else if (total_sectors > FAT_CLUSTERS_MAX * VFS_CLUSTER_SIZE) {
assert_param(0);
total_sectors = FAT_CLUSTERS_MAX * VFS_CLUSTER_SIZE;
}
if (total_sectors >= 0x10000) {
mbr.total_logical_sectors = 0;
mbr.big_sectors_on_drive = total_sectors;
} else {
mbr.total_logical_sectors = total_sectors;
mbr.big_sectors_on_drive = 0;
}
// FAT table will likely be larger than needed, but this is allowed by the
// fat specification
num_clusters = ;
mbr.logical_sectors_per_fat = ;
}
#endif
// If sector size changes update comment below
COMPILER_ASSERT(0x0200 == VFS_SECTOR_SIZE);
// If root directory size changes update max_root_dir_entries
COMPILER_ASSERT(0x0020 == sizeof(root_dir_t) / sizeof(FatDirectoryEntry_t));
#define TOTAL_SECTORS_0 ((VFS_DISK_SIZE + KB(64)) / VFS_SECTOR_SIZE)
#define TOTAL_SECTORS MIN(MAX(TOTAL_SECTORS_0, FAT_CLUSTERS_MIN * VFS_SECTORS_PER_CLUSTER), FAT_CLUSTERS_MAX * VFS_SECTORS_PER_CLUSTER)
#define TOTAL_CLUSTERS (TOTAL_SECTORS / VFS_SECTORS_PER_CLUSTER)
static const mbr_t mbr = {
/*uint8_t[11]*/.boot_sector = {
0xEB, 0x3C, 0x90,
'H', 'A', 'L', '-', '9', '0', '0', '0' // OEM Name in text (8 chars max)
},
/*uint16_t*/.bytes_per_sector = VFS_SECTOR_SIZE, // 512 bytes per sector
/*uint8_t */.sectors_per_cluster = VFS_SECTORS_PER_CLUSTER, // 4k cluster
/*uint16_t*/.reserved_logical_sectors = 0x0001, // mbr is 1 sector
/*uint8_t */.num_fats = 0x02, // 2 FATs
/*uint16_t*/.max_root_dir_entries = 0x0020, // 32 dir entries (max)
/*uint16_t*/.total_logical_sectors = (TOTAL_SECTORS < 0x10000) ? TOTAL_SECTORS
: 0, //0x1f50, // sector size * # of sectors = drive size
/*uint8_t */.media_descriptor = 0xf8, // fixed disc = F8, removable = F0
/*uint16_t*/.logical_sectors_per_fat = (TOTAL_CLUSTERS * 2 + VFS_SECTOR_SIZE - 1) /
VFS_SECTOR_SIZE, //0x0001, // FAT is 1k - ToDO:need to edit this (??)<- comment from DAPLINK
/*uint16_t*/.physical_sectors_per_track = 0x0001, // flat
/*uint16_t*/.heads = 0x0001, // flat
/*uint32_t*/.hidden_sectors = 0x00000000, // before mbt, 0
/*uint32_t*/.big_sectors_on_drive = (TOTAL_SECTORS < 0x10000) ? 0 : TOTAL_SECTORS, // 4k sector. not using large clusters
/*uint8_t */.physical_drive_number = 0x00,
/*uint8_t */.not_used = 0x00, // Current head. Linux tries to set this to 0x1
/*uint8_t */.boot_record_signature = 0x29, // signature is present
/*uint32_t*/.volume_id = 0x27021974, // serial number
// needs to match the root dir label - (update: looks like it does not)
/*char[11]*/.volume_label = {'G', 'E', 'X', '-', 'V', 'F', 'S', '-', 'C', 'F', 'G'},
// unused by msft - just a label (FAT, FAT12, FAT16)
/*char[8] */.file_system_type = {'F', 'A', 'T', '1', '6', ' ', ' ', ' '},
#if !DIRTY_MBR_BOOTCODE
/* Executable boot code that starts the operating system */
/*uint8_t[448]*/.bootstrap = { // TODO get rid of this and read junk instead? Saves 0.5 kB
0x52, 0x6F, 0x6D, 0x2E, 0x20, 0x4F, 0x2C, 0x20, 0x73, 0x70, 0x65, 0x61, 0x6B, 0x20, 0x61, 0x67,
0x61, 0x69, 0x6E, 0x2C, 0x20, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x61, 0x6E, 0x67, 0x65,
0x6C, 0x21, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x74, 0x68, 0x6F, 0x75, 0x20, 0x61, 0x72, 0x74, 0x0A,
0x41, 0x73, 0x20, 0x67, 0x6C, 0x6F, 0x72, 0x69, 0x6F, 0x75, 0x73, 0x20, 0x74, 0x6F, 0x20, 0x74,
0x68, 0x69, 0x73, 0x20, 0x6E, 0x69, 0x67, 0x68, 0x74, 0x2C, 0x20, 0x62, 0x65, 0x69, 0x6E, 0x67,
0x20, 0x6F, 0x27, 0x65, 0x72, 0x20, 0x6D, 0x79, 0x20, 0x68, 0x65, 0x61, 0x64, 0x2C, 0x0A, 0x41,
0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x69, 0x6E, 0x67, 0x65, 0x64, 0x20, 0x6D, 0x65,
0x73, 0x73, 0x65, 0x6E, 0x67, 0x65, 0x72, 0x20, 0x6F, 0x66, 0x20, 0x68, 0x65, 0x61, 0x76, 0x65,
0x6E, 0x0A, 0x55, 0x6E, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65,
0x2D, 0x75, 0x70, 0x74, 0x75, 0x72, 0x6E, 0x65, 0x64, 0x20, 0x77, 0x6F, 0x6E, 0x64, 0x27, 0x72,
0x69, 0x6E, 0x67, 0x20, 0x65, 0x79, 0x65, 0x73, 0x0A, 0x4F, 0x66, 0x20, 0x6D, 0x6F, 0x72, 0x74,
0x61, 0x6C, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x66, 0x61, 0x6C, 0x6C, 0x20, 0x62, 0x61,
0x63, 0x6B, 0x20, 0x74, 0x6F, 0x20, 0x67, 0x61, 0x7A, 0x65, 0x20, 0x6F, 0x6E, 0x20, 0x68, 0x69,
0x6D, 0x0A, 0x57, 0x68, 0x65, 0x6E, 0x20, 0x68, 0x65, 0x20, 0x62, 0x65, 0x73, 0x74, 0x72, 0x69,
0x64, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6C, 0x61, 0x7A, 0x79, 0x2D, 0x70, 0x61, 0x63,
0x69, 0x6E, 0x67, 0x20, 0x63, 0x6C, 0x6F, 0x75, 0x64, 0x73, 0x0A, 0x41, 0x6E, 0x64, 0x20, 0x73,
0x61, 0x69, 0x6C, 0x73, 0x20, 0x75, 0x70, 0x6F, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6F,
0x73, 0x6F, 0x6D, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x69, 0x72, 0x2E, 0x0A,
0x0A, 0x4A, 0x75, 0x6C, 0x2E, 0x20, 0x4F, 0x20, 0x52, 0x6F, 0x6D, 0x65, 0x6F, 0x2C, 0x20, 0x52,
0x6F, 0x6D, 0x65, 0x6F, 0x21, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x66, 0x6F, 0x72, 0x65, 0x20,
0x61, 0x72, 0x74, 0x20, 0x74, 0x68, 0x6F, 0x75, 0x20, 0x52, 0x6F, 0x6D, 0x65, 0x6F, 0x3F, 0x0A,
0x44, 0x65, 0x6E, 0x79, 0x20, 0x74, 0x68, 0x79, 0x20, 0x66, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20,
0x61, 0x6E, 0x64, 0x20, 0x72, 0x65, 0x66, 0x75, 0x73, 0x65, 0x20, 0x74, 0x68, 0x79, 0x20, 0x6E,
0x61, 0x6D, 0x65, 0x21, 0x0A, 0x4F, 0x72, 0x2C, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x6F, 0x75,
0x20, 0x77, 0x69, 0x6C, 0x74, 0x20, 0x6E, 0x6F, 0x74, 0x2C, 0x20, 0x62, 0x65, 0x20, 0x62, 0x75,
0x74, 0x20, 0x73, 0x77, 0x6F, 0x72, 0x6E, 0x20, 0x6D, 0x79, 0x20, 0x6C, 0x6F, 0x76, 0x65, 0x2C,
0x0A, 0x41, 0x6E, 0x64, 0x20, 0x49, 0x27, 0x6C, 0x6C, 0x20, 0x6E, 0x6F, 0x20, 0x6C, 0x6F, 0x6E,
0x67, 0x65, 0x72, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x43, 0x61, 0x70, 0x75, 0x6C, 0x65, 0x74
},
// Set signature to 0xAA55 to make drive bootable
/*uint16_t*/.signature = 0x0000,
#endif
};
enum virtual_media_idx_t
{
MEDIA_IDX_MBR = 0,
MEDIA_IDX_FAT1,
MEDIA_IDX_FAT2,
MEDIA_IDX_ROOT_DIR,
MEDIA_IDX_COUNT
};
// Note - everything in virtual media must be a multiple of VFS_SECTOR_SIZE
const virtual_media_t virtual_media_tmpl[] = {
/* Read CB Write CB Region Size Region Name */
{read_mbr, write_none, VFS_SECTOR_SIZE}, /* MBR */
{read_fat, write_none, 0 /* Set at runtime */ }, /* FAT1 */
{read_fat, write_none, 0 /* Set at runtime */ }, /* FAT2 */
{read_dir, write_dir, VFS_SECTOR_SIZE * 2}, /* Root Dir */
/* Raw filesystem contents follow */
};
// Keep virtual_media_idx_t in sync with virtual_media_tmpl
COMPILER_ASSERT(MEDIA_IDX_COUNT == ELEMENTS_IN_ARRAY(virtual_media_tmpl));
#define DOS_DATE(y, m, d) ((((y)-1980)<<9)|((m)<<5)|(d))
#define DOS_TIME(h, m, s) (((h)<<11)|((m)<<5)|((s)>>1))
static const FatDirectoryEntry_t root_dir_entry = {
/*uint8_t[11] */ .filename = {""},
/*uint8_t */ .attributes = VFS_FILE_ATTR_VOLUME_LABEL | VFS_FILE_ATTR_ARCHIVE,
/*uint8_t */ .reserved = 0x00,
/*uint8_t */ .creation_time_ms = 0x00,
/*uint16_t*/ .creation_time = 0x0000,
/*uint16_t*/ .creation_date = 0x0000,
/*uint16_t*/ .accessed_date = 0x0000,
/*uint16_t*/ .first_cluster_high_16 = 0x0000,
/*uint16_t*/ .modification_time = 0x8E41, // nobody will ever see this
/*uint16_t*/ .modification_date = 0x32bb,
/*uint16_t*/ .first_cluster_low_16 = 0x0000,
/*uint32_t*/ .filesize = 0x00000000
};
static const FatDirectoryEntry_t dir_entry_tmpl = {
/*uint8_t[11] */ .filename = {""},
/*uint8_t */ .attributes = 0, //VFS_FILE_ATTR_READ_ONLY
/*uint8_t */ .reserved = 0x00,
/*uint8_t */ .creation_time_ms = 0x00,
/*uint16_t*/ .creation_time = 0x0000,
/*uint16_t*/ .creation_date = 0x4876,
/*uint16_t*/ .accessed_date = 0x4876,
/*uint16_t*/ .first_cluster_high_16 = 0x0000,
/*uint16_t*/ .modification_time = DOS_TIME(0, 0, 0),
/*uint16_t*/ .modification_date = DOS_DATE(1984, 4, 4),
/*uint16_t*/ .first_cluster_low_16 = 0x0000,
/*uint32_t*/ .filesize = 0x00000000
};
//mbr_t mbr;
file_allocation_table_t fat;
virtual_media_t virtual_media[VFS_MAX_FILES];
union {
FatDirectoryEntry_t initial[VFS_MAX_FILES];
root_dir_t current;
} rootdir;
#define dir_current rootdir.current
#define dir_initial rootdir.initial
uint8_t file_count;
vfs_file_change_cb_t file_change_cb;
uint32_t virtual_media_idx;
uint32_t fat_idx;
uint32_t dir_idx;
uint32_t data_start;
bool init_complete;
// Virtual media must be larger than the template
COMPILER_ASSERT(sizeof(virtual_media) > sizeof(virtual_media_tmpl));
static void write_fat(file_allocation_table_t *aFat, uint32_t idx, uint16_t val)
{
uint32_t low_idx;
uint32_t high_idx;
low_idx = idx * 2 + 0;
high_idx = idx * 2 + 1;
// Assert that this is still within the fat table
if (high_idx >= ELEMENTS_IN_ARRAY(aFat->f)) {
assert_param(0);
return;
}
aFat->f[low_idx] = (val >> 0) & 0xFF;
aFat->f[high_idx] = (val >> 8) & 0xFF;
}
void vfs_init(const vfs_filename_t drive_name, uint32_t disk_size)
{
uint32_t i;
// uint32_t num_clusters;
// uint32_t total_sectors;
// Clear everything
// memset(&mbr, 0, sizeof(mbr));
memset(&fat, 0, sizeof(fat));
fat_idx = 0;
memset(&virtual_media, 0, sizeof(virtual_media));
memset(&dir_current, 0, sizeof(dir_current));
// memset(&dir_initial, 0, sizeof(dir_initial));
dir_idx = 0;
file_count = 0;
file_change_cb = file_change_cb_stub;
virtual_media_idx = 0;
data_start = 0;
init_complete = false;
//
// vfs_printf("MBR totalsec %d, min %d, max %d", mbr.total_logical_sectors,
// FAT_CLUSTERS_MIN * mbr.sectors_per_cluster,
// FAT_CLUSTERS_MAX * mbr.sectors_per_cluster);
//
// MIN(MAX(, FAT_CLUSTERS_MIN * mbr.sectors_per_cluster), FAT_CLUSTERS_MAX * mbr.sectors_per_cluster)
// // Initialize MBR
//// memcpy(&mbr, &mbr_tmpl, sizeof(mbr_t));
// total_sectors = ((disk_size + KB(64)) / mbr.bytes_per_sector);
// // Make sure this is the right size for a FAT16 volume
// if (total_sectors < FAT_CLUSTERS_MIN * mbr.sectors_per_cluster) {
// assert_param(0);
// total_sectors = FAT_CLUSTERS_MIN * mbr.sectors_per_cluster;
// } else if (total_sectors > FAT_CLUSTERS_MAX * mbr.sectors_per_cluster) {
// assert_param(0);
// total_sectors = FAT_CLUSTERS_MAX * mbr.sectors_per_cluster;
// }
// if (total_sectors >= 0x10000) {
// mbr.total_logical_sectors = 0;
// mbr.big_sectors_on_drive = total_sectors;
// } else {
// mbr.total_logical_sectors = total_sectors;
// mbr.big_sectors_on_drive = 0;
// }
// // FAT table will likely be larger than needed, but this is allowed by the
// // fat specification
// num_clusters = total_sectors / mbr.sectors_per_cluster;
// mbr.logical_sectors_per_fat = (num_clusters * 2 + VFS_SECTOR_SIZE - 1) / VFS_SECTOR_SIZE;
// Initailize virtual media
memcpy(&virtual_media, &virtual_media_tmpl, sizeof(virtual_media_tmpl));
virtual_media[MEDIA_IDX_FAT1].length = VFS_SECTOR_SIZE * mbr.logical_sectors_per_fat;
virtual_media[MEDIA_IDX_FAT2].length = VFS_SECTOR_SIZE * mbr.logical_sectors_per_fat;
// Initialize indexes
virtual_media_idx = MEDIA_IDX_COUNT;
data_start = 0;
for (i = 0; i < ELEMENTS_IN_ARRAY(virtual_media_tmpl); i++) {
data_start += virtual_media[i].length;
}
// Initialize FAT
fat_idx = 0;
write_fat(&fat, fat_idx, 0xFFF8); // Media type "media_descriptor"
fat_idx++;
write_fat(&fat, fat_idx, 0xFFFF); // FAT12 - always 0xFFF (no meaning), FAT16 - dirty/clean (clean = 0xFFFF)
fat_idx++;
// Initialize root dir
dir_idx = 0;
dir_current.f[dir_idx] = root_dir_entry;
memcpy(dir_current.f[dir_idx].filename, drive_name, sizeof(dir_current.f[0].filename));
dir_idx++;
}
uint32_t vfs_get_total_size(void)
{
uint32_t size;
if (mbr.total_logical_sectors > 0) {
size = mbr.total_logical_sectors * mbr.bytes_per_sector;
} else if (mbr.big_sectors_on_drive > 0) {
size = mbr.big_sectors_on_drive * mbr.bytes_per_sector;
} else {
size = 0;
assert_param(0);
}
return size;
}
vfs_file_t vfs_create_file(const vfs_filename_t filename, vfs_read_cb_t read_cb, vfs_write_cb_t write_cb, uint32_t len)
{
uint32_t first_cluster;
FatDirectoryEntry_t *de;
uint32_t clusters;
uint32_t cluster_size;
uint32_t i;
assert_param(filename_valid(filename));
// Compute the number of clusters in the file
cluster_size = mbr.bytes_per_sector * mbr.sectors_per_cluster;
clusters = (len + cluster_size - 1) / cluster_size;
// Write the cluster chain to the fat table
first_cluster = 0;
if (len > 0) {
first_cluster = fat_idx;
for (i = 0; i < clusters - 1; i++) {
write_fat(&fat, fat_idx, fat_idx + 1);
fat_idx++;
}
write_fat(&fat, fat_idx, 0xFFFF);
fat_idx++;
}
// Update directory entry
if (dir_idx >= ELEMENTS_IN_ARRAY(dir_current.f)) {
assert_param(0);
return VFS_FILE_INVALID;
}
de = &dir_current.f[dir_idx];
dir_idx++;
memcpy(de, &dir_entry_tmpl, sizeof(dir_entry_tmpl));
memcpy(de->filename, filename, 11);
de->filesize = len;
de->first_cluster_high_16 = (first_cluster >> 16) & 0xFFFF;
de->first_cluster_low_16 = (first_cluster >> 0) & 0xFFFF;
// Update virtual media
if (virtual_media_idx >= ELEMENTS_IN_ARRAY(virtual_media)) {
assert_param(0);
return VFS_FILE_INVALID;
}
virtual_media[virtual_media_idx].read_cb = read_zero;
virtual_media[virtual_media_idx].write_cb = write_none;
if (0 != read_cb) {
virtual_media[virtual_media_idx].read_cb = read_cb;
}
if (0 != write_cb) {
virtual_media[virtual_media_idx].write_cb = write_cb;
}
virtual_media[virtual_media_idx].length = clusters * mbr.bytes_per_sector * mbr.sectors_per_cluster;
virtual_media_idx++;
file_count += 1;
return de;
}
void vfs_file_set_attr(vfs_file_t file, vfs_file_attr_bit_t attr)
{
FatDirectoryEntry_t *de = file;
de->attributes = attr;
}
vfs_sector_t vfs_file_get_start_sector(vfs_file_t file)
{
FatDirectoryEntry_t *de = file;
if (vfs_file_get_size(file) == 0) {
return VFS_INVALID_SECTOR;
}
return cluster_to_sector(de->first_cluster_low_16);
}
uint32_t vfs_file_get_size(vfs_file_t file)
{
FatDirectoryEntry_t *de = file;
return de->filesize;
}
vfs_file_attr_bit_t vfs_file_get_attr(vfs_file_t file)
{
FatDirectoryEntry_t *de = file;
return (vfs_file_attr_bit_t) de->attributes;
}
void vfs_set_file_change_callback(vfs_file_change_cb_t cb)
{
file_change_cb = cb;
}
void vfs_read(uint32_t requested_sector, uint8_t *buf, uint32_t num_sectors)
{
uint8_t i = 0;
uint32_t current_sector;
// Zero out the buffer
memset(buf, 0, num_sectors * VFS_SECTOR_SIZE);
current_sector = 0;
set_init_done();
// vfs_printf("vfs_read sec %d, len %d secs", requested_sector, num_sectors);
for (i = 0; i < ELEMENTS_IN_ARRAY(virtual_media); i++) {
uint32_t vm_sectors = virtual_media[i].length / VFS_SECTOR_SIZE;
uint32_t vm_start = current_sector;
uint32_t vm_end = current_sector + vm_sectors;
// Data can be used in this sector
if ((requested_sector >= vm_start) && (requested_sector < vm_end)) {
uint32_t sector_offset;
uint32_t sectors_to_write = vm_end - requested_sector;
sectors_to_write = MIN(sectors_to_write, num_sectors);
sector_offset = requested_sector - current_sector;
virtual_media[i].read_cb(sector_offset, buf, sectors_to_write);
// Update requested sector
requested_sector += sectors_to_write;
num_sectors -= sectors_to_write;
}
// If there is no more data to be read then break
if (num_sectors == 0) {
break;
}
// Move to the next virtual media entry
current_sector += vm_sectors;
}
}
void vfs_write(uint32_t requested_sector, const uint8_t *buf, uint32_t num_sectors)
{
uint8_t i = 0;
uint32_t current_sector;
current_sector = 0;
set_init_done();
vfs_printf("vfs_write - at sector %d, count %d", requested_sector, num_sectors);
for (i = 0; i < virtual_media_idx; i++) {
uint32_t vm_sectors = virtual_media[i].length / VFS_SECTOR_SIZE;
uint32_t vm_start = current_sector;
uint32_t vm_end = current_sector + vm_sectors;
//vfs_printf("Testing file %d: (%d -> %d)", i, vm_start, vm_end);
// Data can be used in this sector
if ((requested_sector >= vm_start) && (requested_sector < vm_end)) {
uint32_t sector_offset;
uint32_t sectors_to_read = vm_end - requested_sector;
sectors_to_read = MIN(sectors_to_read, num_sectors);
sector_offset = requested_sector - current_sector;
virtual_media[i].write_cb(sector_offset, buf, sectors_to_read);
// Update requested sector
requested_sector += sectors_to_read;
num_sectors -= sectors_to_read;
}
// If there is no more data to be read then break
if (num_sectors == 0) {
break;
}
// Move to the next virtual media entry
current_sector += vm_sectors;
}
if (num_sectors > 0) vfs_printf("Failed to find place for writing, remain %d secs to write.", num_sectors);
}
static uint32_t read_zero(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
{
uint32_t read_size = VFS_SECTOR_SIZE * num_sectors;
memset(data, 0, read_size);
return read_size;
}
static void write_none(uint32_t sector_offset, const uint8_t *data, uint32_t num_sectors)
{
// Do nothing
}
static uint32_t read_mbr(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
{
uint32_t read_size = VFS_SECTOR_SIZE;
if (sector_offset != 0) {
// Don't worry about reading other sectors
return 0;
}
// clear the buffer - MBR is not complete
memset(data, 0, read_size);
// copy MBR
memcpy(data, &mbr, sizeof(mbr_t));
return VFS_SECTOR_SIZE;
}
/* No need to handle writes to the mbr */
static uint32_t read_fat(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
{
uint32_t read_size = sizeof(file_allocation_table_t);
COMPILER_ASSERT(sizeof(file_allocation_table_t) <= VFS_SECTOR_SIZE);
if (sector_offset != 0) {
// Don't worry about reading other sectors
return 0;
}
memcpy(data, &fat, read_size);
return read_size;
}
/* No need to handle writes to the fat */
static uint32_t read_dir(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
{
uint32_t start_index;
uint32_t copy_size;
if ((sector_offset + num_sectors) * VFS_SECTOR_SIZE > sizeof(dir_current)) {
// Trying to read too much of the root directory
assert_param(0);
return 0;
}
// Zero buffer
memset(data, 0, num_sectors * VFS_SECTOR_SIZE);
start_index = sector_offset * VFS_SECTOR_SIZE / sizeof(FatDirectoryEntry_t);
// Copy data if anything can be copied
if (start_index < ELEMENTS_IN_ARRAY(dir_initial)) {
assert_param(sizeof(dir_initial) > sector_offset * VFS_SECTOR_SIZE);
copy_size = sizeof(dir_initial) - sector_offset * VFS_SECTOR_SIZE;
memcpy(data, &dir_initial[start_index], copy_size);
}
return num_sectors * VFS_SECTOR_SIZE;
}
static void write_dir(uint32_t sector_offset, const uint8_t *data, uint32_t num_sectors)
{
FatDirectoryEntry_t *old_entry;
FatDirectoryEntry_t *new_entry;
uint32_t start_index;
uint32_t num_entries;
uint32_t i;
if ((sector_offset + num_sectors) * VFS_SECTOR_SIZE > sizeof(dir_current)) {
// Trying to write too much of the root directory
assert_param(0);
return;
}
start_index = sector_offset * VFS_SECTOR_SIZE / sizeof(FatDirectoryEntry_t);
num_entries = num_sectors * VFS_SECTOR_SIZE / sizeof(FatDirectoryEntry_t);
old_entry = &dir_current.f[start_index];
new_entry = (FatDirectoryEntry_t *) data;
// If this is the first sector start at index 1 to get past drive name
i = 0 == sector_offset ? 1 : 0;
for (; i < num_entries; i++) {
bool same_name;
if (0 == memcmp(&old_entry[i], &new_entry[i], sizeof(FatDirectoryEntry_t))) {
continue;
}
// If were at this point then something has changed in the file
same_name = (0 == memcmp(old_entry[i].filename, new_entry[i].filename, sizeof(new_entry[i].filename))) ? 1 : 0;
// Changed
if (new_entry[i].attributes != VFS_FILE_ATTR_LFN) {
file_change_cb(new_entry[i].filename, VFS_FILE_CHANGED, (vfs_file_t) &old_entry[i],
(vfs_file_t) &new_entry[i]);
}
// Deleted
if (old_entry[i].attributes != VFS_FILE_ATTR_LFN && 0xe5 == (uint8_t) new_entry[i].filename[0]) {
file_change_cb(old_entry[i].filename, VFS_FILE_DELETED, (vfs_file_t) &old_entry[i], (vfs_file_t) &new_entry[i]);
continue;
}
// Created
if (new_entry[i].attributes != VFS_FILE_ATTR_LFN && !same_name && filename_valid(new_entry[i].filename)) {
file_change_cb(new_entry[i].filename, VFS_FILE_CREATED, (vfs_file_t) &old_entry[i], (vfs_file_t) &new_entry[i]);
continue;
}
}
memcpy(&dir_current.f[start_index], data, num_sectors * VFS_SECTOR_SIZE);
}
static void file_change_cb_stub(const vfs_filename_t filename, vfs_file_change_t change, vfs_file_t file, vfs_file_t new_file_data)
{
// Do nothing
}
static uint32_t cluster_to_sector(uint32_t cluster_idx)
{
uint32_t sectors_before_data = data_start / mbr.bytes_per_sector;
return sectors_before_data + (cluster_idx - 2) * mbr.sectors_per_cluster;
}
static bool filename_valid(const vfs_filename_t filename)
{
// Information on valid 8.3 filenames can be found in
// the microsoft hardware whitepaper:
//
// Microsoft Extensible Firmware Initiative
// FAT32 File System Specification
// FAT: General Overview of On-Disk Format
const char invalid_starting_chars[] = {
0xE5, // Deleted
0x00, // Deleted (and all following entries are free)
0x20, // Space not allowed as first character
};
uint32_t i;
// Check for invalid starting characters
for (i = 0; i < sizeof(invalid_starting_chars); i++) {
if (invalid_starting_chars[i] == filename[0]) {
return false;
}
}
// Make sure all the characters are valid
for (i = 0; i < sizeof(vfs_filename_t); i++) {
if (!filename_character_valid(filename[i])) {
return false;
}
}
// All checks have passed so filename is valid
return true;
}
static bool filename_character_valid(char character)
{
const char invalid_chars[] = {0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
uint32_t i;
// Lower case characters are not allowed
if ((character >= 'a') && (character <= 'z')) {
return false;
}
// Values less than 0x20 are not allowed except 0x5
if ((character < 0x20) && (character != 0x5)) {
return false;
}
// Check for special characters that are not allowed
for (i = 0; i < sizeof(invalid_chars); i++) {
if (invalid_chars[i] == character) {
return false;
}
}
// All of the checks have passed so this is a valid file name character
return true;
}
static void set_init_done(void)
{
if (!init_complete) {
// memcpy(&dir_initial, &dir_current, MIN(sizeof(dir_initial), sizeof(dir_current)));
init_complete = true;
}
}
bool vfs_find_file(uint32_t start_sector, vfs_filename_t *destFilename, vfs_file_t **destFile)
{
vfs_printf("Looking for file at %d", start_sector);
for (int i = 0; i < 32; i++) {
FatDirectoryEntry_t *f = &dir_current.f[i];
if (f->attributes == VFS_FILE_ATTR_LFN) continue;
if (cluster_to_sector((f->first_cluster_high_16 << 16) + f->first_cluster_low_16) == start_sector) {
memcpy(destFilename, f->filename, sizeof(vfs_filename_t));
vfs_printf("Found one at: %s - SUCCESS!!", f->filename);
*destFile = (vfs_file_t *) f;
return true;
}
}
vfs_printf("NOT FOUND.");
return false;
}
+121
View File
@@ -0,0 +1,121 @@
/**
* @file virtual_fs.h
* @brief FAT 12/16 filesystem handling
*
* DAPLink Interface Firmware
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef VIRTUAL_FS_H
#define VIRTUAL_FS_H
#include "platform.h"
#ifdef __cplusplus
extern "C" {
#endif
#if DEBUG_VFS
#define vfs_printf(...) do { dbg(__VA_ARGS__); } while(0)
#define vfs_printf_nonl(...) do { PRINTF(__VA_ARGS__); } while(0)
#else
#define vfs_printf(...) do { } while(0)
#define vfs_printf_nonl(...) do { } while(0)
#endif
#define VFS_CLUSTER_SIZE 0x1000
#define VFS_SECTOR_SIZE 512
#define VFS_SECTORS_PER_CLUSTER 8
#define VFS_INVALID_SECTOR 0xFFFFFFFF
#define VFS_FILE_INVALID 0
#define VFS_MAX_FILES 16
#define VFS_DISK_SIZE MB(32)
typedef char vfs_filename_t[11];
typedef enum {
VFS_FILE_ATTR_READ_ONLY = (1 << 0),
VFS_FILE_ATTR_HIDDEN = (1 << 1),
VFS_FILE_ATTR_SYSTEM = (1 << 2),
VFS_FILE_ATTR_VOLUME_LABEL = (1 << 3),
VFS_FILE_ATTR_SUB_DIR = (1 << 4),
VFS_FILE_ATTR_ARCHIVE = (1 << 5),
VFS_FILE_ATTR_LFN = 0x0F, // composite special
} vfs_file_attr_bit_t;
typedef enum {
VFS_FILE_CREATED = 0, /*!< A new file was created */
VFS_FILE_DELETED, /*!< An existing file was deleted */
VFS_FILE_CHANGED, /*!< Some attribute of the file changed.
Note: when a file is deleted or
created a file changed
notification will also occur*/
} vfs_file_change_t;
typedef void *vfs_file_t;
typedef uint32_t vfs_sector_t;
// Callback for when data is written to a file on the virtual filesystem
typedef void (*vfs_write_cb_t)(uint32_t sector_offset, const uint8_t *data, uint32_t num_sectors);
// Callback for when data is ready from the virtual filesystem
typedef uint32_t (*vfs_read_cb_t)(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors);
// Callback for when a file's attributes are changed on the virtual filesystem. Note that the 'file' parameter
// can be saved and compared to other files to see if they are referencing the same object. The
// same cannot be done with new_file_data since it points to a temporary buffer.
typedef void (*vfs_file_change_cb_t)(const vfs_filename_t filename, vfs_file_change_t change,
vfs_file_t file, vfs_file_t new_file_data);
// Initialize the filesystem with the given size and name
void vfs_init(const vfs_filename_t drive_name, uint32_t disk_size);
// Get the total size of the virtual filesystem
uint32_t vfs_get_total_size(void);
// Add a file to the virtual FS and return a handle to this file.
// This must be called before vfs_read or vfs_write are called.
// Adding a new file after vfs_read or vfs_write have been called results in undefined behavior.
vfs_file_t vfs_create_file(const vfs_filename_t filename, vfs_read_cb_t read_cb, vfs_write_cb_t write_cb, uint32_t len);
// Set the attributes of a file
void vfs_file_set_attr(vfs_file_t file, vfs_file_attr_bit_t attr);
// Get the starting sector of this file.
// NOTE - If the file size is 0 there is no starting
// sector so VFS_INVALID_SECTOR will be returned.
vfs_sector_t vfs_file_get_start_sector(vfs_file_t file);
// Get the size of the file.
uint32_t vfs_file_get_size(vfs_file_t file);
// Get the attributes of a file
vfs_file_attr_bit_t vfs_file_get_attr(vfs_file_t file);
// Set the callback when a file is created, deleted or has atributes changed.
void vfs_set_file_change_callback(vfs_file_change_cb_t cb);
// Read one or more sectors from the virtual filesystem
void vfs_read(uint32_t sector, uint8_t *buf, uint32_t num_of_sectors);
// Write one or more sectors to the virtual filesystem
void vfs_write(uint32_t sector, const uint8_t *buf, uint32_t num_of_sectors);
bool vfs_find_file(uint32_t start_sector, vfs_filename_t *destFilename, vfs_file_t **destFile);
#ifdef __cplusplus
}
#endif
#endif// VIRTUAL_FS_H