Files
2023-Webservices_And_Applic…/project/Core/Src/tftp.c
Sander Speetjens 623cf3749c TFTP
Add tftp_custom_write and test functions
2023-11-13 16:03:14 +01:00

287 lines
8.2 KiB
C

/**
* @file tftp.c
* @brief tftp server
* @author Sander S.
*/
#include "tftp.h"
static const char* TAG = "tftp_server";
extern struct llfs_data_file* llfs_root;
static tftp_custom_file_t virt_file[] =
{
{.name = "index.txt",.data = NULL, .len = 0, .ofset = 0},
{.name = "virtImage.raw",.data = NULL, .len = 0, .ofset = 0},
{.name = "virtText.txt",.data = NULL, .len = 0, .ofset = 0}
};
int str_cat_str(char* dest, size_t dest_size, const char* src) {
size_t dest_len = strlen(dest);
size_t src_len = strlen(src);
if (dest_len + src_len > dest_size) {
return -1;
}
memcpy(dest + dest_len, src, src_len);
return 0;
}
int str_cat(char* dest, size_t dest_size, char c)
{
size_t dest_len = strlen(dest);
if (dest_len + 1 > dest_size) {
return -1;
}
dest[dest_len] = c;
dest[dest_len + 1] = '\0';
return 0;
}
/**
* @brief tftp custom file functions to set the offset and read the data
* @param[in,out] handle Custom file handles
* @param[in] offset The offset to set
* @param[in] whence The origin of the offset
*/
void tftp_custom_fseek(tftp_custom_file_t* handle, size_t offset, int whence) {
switch (whence) {
case SEEK_SET:
handle->ofset = offset;
break;
case SEEK_CUR:
handle->ofset += offset;
break;
case SEEK_END:
break;
}
if (handle->ofset > handle->len) {
handle->ofset = handle->len;
}
}
/**
* @brief tftp custom file functions to read the data
* auto rolling over the offset
* if the bytes to read is bigger than the remaining bytes
* it will read the remaining bytes and set the bytes to 0
* @param[out] buf The buffer to write the data to
* @param[in] bytes The number of bytes to read
* @param[in,out] handle Custom file handles
*/
size_t tftp_custom_fread(void* buf, size_t bytes, tftp_custom_file_t* handle) {
if (handle->ofset + bytes > handle->len) {
bytes = handle->len - handle->ofset;
}
memcpy(buf, handle->data + handle->ofset, bytes);
handle->ofset += bytes;
((char*)buf)[bytes] = '\0';
if (handle->ofset > handle->len) {
bytes = 0;
}
return bytes;
}
/**
* @brief tftp custom file functions to write the data
* auto rolling over the offset
*
* @param buf The buffer to write the data to
* @param bytes The number of bytes to write
* @param handle The handle to the file to write to
* @return The number of bytes written
*/
size_t tftp_custom_fwrite(const void* buf, size_t bytes, tftp_custom_file_t* handle) {
if (handle->ofset + bytes > handle->len) {
bytes = handle->len - handle->ofset;
}
memcpy(handle->data + handle->ofset, buf, bytes);
handle->ofset += bytes;
if (handle->ofset > handle->len) {
bytes = 0;
}
return bytes;
}
/**
* @brief tftp helper functions
*/
/**
* @brief This function is called when a file is opened
* It should return a handle to the file or NULL if the file does not exist
* The handle contains a ptr to or the actual file data or a virtual file
*
* @param fname The name of the file to open
* @param mode Mode string from TFTP RFC
* @param write Flag indicating read (0) or write (!= 0) access
* @return void* File handle supplied to other functions
*/
void* tftp_open(const char* fname, const char* mode, uint8_t write) {
LOG_INFO(TAG, "Opening %s", fname);
if (strcmp(fname, virt_file[0].name) == 0 && write == TFTP_READ) {
tftp_custom_fseek(&virt_file[0], 0, SEEK_SET);
return &virt_file[0];
} else if (strcmp(fname, virt_file[1].name) == 0 && write != TFTP_READ) {
return &virt_file[1];
} else if (strcmp(fname, virt_file[2].name) == 0 && write != TFTP_READ) {
return &virt_file[2];
}
// TODO: waiting on Lorentz to finish creating f* functions for LLFS
return fopen(fname, write ? "wb" : "rb");
}
/**
* @brief This function is called when a file is closed
*
* @param handle The handle to the file to close
*/
void tftp_close(void* handle) {
LOG_INFO(TAG, "closing file");
if (handle == NULL) {
LOG_CRIT(TAG, "handle is null");
return;
}
if (handle == &virt_file[2]) {
// TODO: Clear display
lcd_display_text((uint8_t*)virt_file[2].data, 0, 0, LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_FONT16);
}
if (handle == &virt_file[0] || handle == &virt_file[1] || handle == &virt_file[2]) {
((tftp_custom_file_t*)handle)->ofset = 0;
return;
}
// TODO: waiting on Lorentz to finish creating f* functions for LLFS
fclose((FILE*)handle);
}
/**
* @brief This function is called when a file is read
* The virtual files are filtered out first
* then the file is trying to get read from the llfs
*
* @param handle File handle returned by open()
* @param buf Target buffer to copy read data to
* @param bytes Number of bytes to copy to buf
* @return int >= 0: Success; < 0: Error
*/
int tftp_read(void* handle, void* buf, int bytes) {
int ret = 0;
LOG_INFO(TAG, "reading file");
if (handle == NULL) {
LOG_CRIT(TAG, "handle is null");
return -1;
}
FILE* file = (FILE*)handle;
if ((tftp_custom_file_t*)file == &virt_file[0]) {
ret = tftp_custom_fread(buf, bytes, (tftp_custom_file_t*)file);
return ret;
} else if ((tftp_custom_file_t*)file == &virt_file[1]) {
LOG_CRIT(TAG, "Exception: Trying to read a write only file");
return -1;
} else if ((tftp_custom_file_t*)file == &virt_file[2]) {
LOG_CRIT(TAG, "Exception: Trying to read a write only file");
return -1;
}
// TODO: waiting on Lorentz to finish creating f* functions for LLFS
ret = fread(buf, sizeof(uint8_t), bytes, file);
if (ret <= 0) {
return -1;
}
return ret;
}
/**
* @brief This function is called when a file is written
*
* @param handle File handle returned by open()
* @param p PBUF adjusted such that payload pointer points to the beginning of write data.
* In other words, TFTP headers are stripped off.
* @return int >= 0: Success; < 0: Error
*/
int tftp_write(void* handle, struct pbuf* p) {
LOG_INFO(TAG, "Writing file");
LOG_DEBUG(TAG, "Not implemented yet");
tftp_custom_file_t *file = (tftp_custom_file_t*)handle;
if (file == &virt_file[1] || file == &virt_file[2]) {
return tftp_custom_fwrite(p->payload, p->len, file);
}
return -1;
}
/**
* @brief This function creates the file list for index.txt
*/
void init_index(void)
{
size_t len = 0;
// Add len of the virt files to the size
for (int i = 0; i < 2; i++)
{
len += strlen(virt_file[i].name) + 1;
}
const struct llfs_data_file* root = llfs_root;
while(root != NULL) {
len += strlen(root->name) + 1;
root = root->next;
}
len++; // +1 for the \0
virt_file[0].data = malloc(len);
virt_file[0].len = len;
for (int i = 0; i < 2; i++)
{
str_cat_str(virt_file[0].data, len, virt_file[i].name);
str_cat(virt_file[0].data, len, '\n');
}
root = llfs_root;
while(root != NULL) {
str_cat_str(virt_file[0].data, len, root->name);
str_cat(virt_file[0].data, len, '\n');
root = root->next;
}
virt_file[2].data = malloc(100);
virt_file[2].len = 100;
}
struct tftp_context tftpContext_s = {
.open = tftp_open,
.close = tftp_close,
.read = tftp_read,
.write = tftp_write
};
/**
* @brief Initialize tftp server
*/
void tftp_server_init(void) {
LOG_INFO(TAG, "Initializing tftp server");
// init the index.txt virt_file
init_index();
// Init the tftp server
if (tftp_init(&tftpContext_s) != ERR_OK) {
LOG_FATAL(TAG, "Could not initialize tftp server");
return;
}
LOG_INFO(TAG, "tftp server initialized successfully");
}
void tftp_server_deinit(void) {
LOG_INFO(TAG, "Deinitializing tftp server");
tftp_cleanup();
LOG_INFO(TAG, "tftp server deinitialized successfully");
free(virt_file[0].data);
virt_file[0].data = NULL;
virt_file[0].len = 0;
virt_file[0].ofset = 0;
free(virt_file[2].data);
virt_file[2].data = NULL;
virt_file[2].len = 0;
}