307 lines
9.8 KiB
C
307 lines
9.8 KiB
C
/**
|
|
* @file tftp.c
|
|
* @brief tftp server
|
|
* @author Sander S.
|
|
*/
|
|
|
|
#include "tftp.h"
|
|
|
|
#define VIRT_INDEX_TXT 0
|
|
#define VIRT_IMAGE_BMP 1
|
|
#define VIRT_IMAGE_GIF 2
|
|
#define VIRT_TEXT_TXT 3
|
|
|
|
#define IMAGE_BUFFER_SIZE 81920
|
|
|
|
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.bmp", .data = NULL, .len = 0, .ofset = 0},
|
|
{.name = "virtImage.gif", .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);
|
|
|
|
UNUSED(mode);
|
|
|
|
if (strcmp(fname, virt_file[VIRT_INDEX_TXT].name) == 0 && write == TFTP_READ) {
|
|
tftp_custom_fseek(&virt_file[VIRT_INDEX_TXT], 0, SEEK_SET);
|
|
return &virt_file[0];
|
|
} else if (strcmp(fname, virt_file[VIRT_IMAGE_BMP].name) == 0 && write != TFTP_READ) {
|
|
return &virt_file[VIRT_IMAGE_BMP];
|
|
} else if (strcmp(fname, virt_file[VIRT_IMAGE_GIF].name) == 0 && write != TFTP_READ) {
|
|
return &virt_file[VIRT_IMAGE_GIF];
|
|
} else if (strcmp(fname, virt_file[VIRT_TEXT_TXT].name) == 0 && write != TFTP_READ) {
|
|
return &virt_file[VIRT_TEXT_TXT];
|
|
}
|
|
|
|
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[VIRT_IMAGE_BMP]) {
|
|
lcd_clear(LCD_COLOR_BLACK);
|
|
lcd_draw_bmp_img((uint8_t*)virt_file[VIRT_IMAGE_BMP].data, 0, 0);
|
|
}
|
|
|
|
if (handle == &virt_file[VIRT_IMAGE_GIF]) {
|
|
lcd_clear(LCD_COLOR_BLACK);
|
|
lcd_draw_gif((uint8_t*)virt_file[VIRT_IMAGE_GIF].data, virt_file[VIRT_IMAGE_GIF].ofset,0, 0);
|
|
}
|
|
|
|
if (handle == &virt_file[VIRT_TEXT_TXT]) {
|
|
lcd_clear(LCD_COLOR_BLACK);
|
|
lcd_display_text((uint8_t*)virt_file[VIRT_TEXT_TXT].data, 0, 0, LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_FONT16);
|
|
}
|
|
|
|
if (handle == &virt_file[VIRT_INDEX_TXT] || handle == &virt_file[VIRT_IMAGE_BMP] || handle == &virt_file[VIRT_IMAGE_GIF] || handle == &virt_file[VIRT_TEXT_TXT]) {
|
|
((tftp_custom_file_t*)handle)->ofset = 0;
|
|
return;
|
|
}
|
|
|
|
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[VIRT_INDEX_TXT]) {
|
|
ret = (int)tftp_custom_fread(buf, (size_t)bytes, (tftp_custom_file_t*)file);
|
|
return ret;
|
|
} else if ((tftp_custom_file_t*)file == &virt_file[VIRT_IMAGE_BMP]) {
|
|
LOG_CRIT(TAG, "Exception: Trying to read a write only file");
|
|
return -1;
|
|
} else if ((tftp_custom_file_t*)file == &virt_file[VIRT_IMAGE_GIF]) {
|
|
LOG_CRIT(TAG, "Exception: Trying to read a write only file");
|
|
return -1;
|
|
} else if ((tftp_custom_file_t*)file == &virt_file[VIRT_TEXT_TXT]) {
|
|
LOG_CRIT(TAG, "Exception: Trying to read a write only file");
|
|
return -1;
|
|
}
|
|
|
|
ret = (int)fread(buf, sizeof(uint8_t), (size_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");
|
|
tftp_custom_file_t* file = (tftp_custom_file_t*)handle;
|
|
if (file == &virt_file[VIRT_IMAGE_BMP] || file == &virt_file[VIRT_IMAGE_GIF] || file == &virt_file[VIRT_TEXT_TXT]) {
|
|
return (int)tftp_custom_fwrite(p->payload, (size_t)(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[VIRT_INDEX_TXT].data = malloc(len);
|
|
virt_file[VIRT_INDEX_TXT].len = len;
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
str_cat_str(virt_file[VIRT_INDEX_TXT].data, len, virt_file[i].name);
|
|
str_cat(virt_file[VIRT_INDEX_TXT].data, len, '\n');
|
|
}
|
|
root = llfs_root;
|
|
while (root != NULL) {
|
|
str_cat_str(virt_file[VIRT_INDEX_TXT].data, len, root->name);
|
|
str_cat(virt_file[VIRT_INDEX_TXT].data, len, '\n');
|
|
root = root->next;
|
|
}
|
|
}
|
|
|
|
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 virtImage.raw virt_file with 80kb of ram
|
|
virt_file[VIRT_IMAGE_BMP].data = calloc(IMAGE_BUFFER_SIZE, sizeof(char));
|
|
if (virt_file[VIRT_IMAGE_BMP].data == NULL) {
|
|
LOG_FATAL(TAG, "Could not allocate memory for virtImage.raw");
|
|
return;
|
|
}
|
|
virt_file[VIRT_IMAGE_BMP].len = IMAGE_BUFFER_SIZE;
|
|
|
|
virt_file[VIRT_IMAGE_GIF].data = virt_file[VIRT_IMAGE_BMP].data;
|
|
virt_file[VIRT_IMAGE_GIF].len = virt_file[VIRT_IMAGE_BMP].len;
|
|
|
|
// 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[VIRT_INDEX_TXT].data);
|
|
virt_file[VIRT_INDEX_TXT].data = NULL;
|
|
virt_file[VIRT_INDEX_TXT].len = 0;
|
|
virt_file[VIRT_INDEX_TXT].ofset = 0;
|
|
free(virt_file[VIRT_IMAGE_BMP].data);
|
|
virt_file[VIRT_IMAGE_BMP].data = NULL;
|
|
virt_file[VIRT_IMAGE_BMP].len = 0;
|
|
virt_file[VIRT_IMAGE_GIF].data = NULL;
|
|
virt_file[VIRT_IMAGE_GIF].len = 0;
|
|
} |