Code import
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user