Merge branch 'main' into LCD_API
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -37,4 +37,12 @@ project/.idea/
|
||||
|
||||
project/cmake-build-debug/
|
||||
|
||||
project/Scripts/
|
||||
project/.settings/
|
||||
|
||||
project/.mxproject
|
||||
|
||||
project/project.launch
|
||||
|
||||
project/.cproject
|
||||
|
||||
project/Scripts
|
||||
150
README.md
150
README.md
@@ -1,21 +1,29 @@
|
||||
# 2023-Webservices_And_Applications
|
||||
## Used Libs, compiler and apps
|
||||
|
||||
- lwip version 2.1.2
|
||||
- CubeIDE version 1.12.1
|
||||
- STM32CubeMX version 6.8.1
|
||||
- Firmware Lib (stm32f7) 1.17.1
|
||||
# 2023 — Webservices and Applications
|
||||
## Table of Contents
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Used Libs, compiler and apps](#used-libs-compiler-and-apps)
|
||||
- [Tasks](#tasks)
|
||||
- [Style Guide](#style-guide)
|
||||
- [Editor Configuration](#editor-configuration)
|
||||
- [Commit Messages Conventions](#commit-messages-conventions)
|
||||
- [Documentation](#documentation)
|
||||
|
||||
## Tasks
|
||||
Make for each task a sperate c and h file. The name of the file is the name of the task.
|
||||
This way we can keep the code clean.
|
||||
|
||||
[tasks_and_taskowners.md](tasks_and_taskowners.md)
|
||||
## Used Libs, Compiler and Apps
|
||||
- lwip version 2.1.2
|
||||
- CubeIDE version 1.12.1
|
||||
- STM32CubeMX version 6.8.1
|
||||
- Firmware Lib (stm32f7) 1.17.1
|
||||
|
||||
## Tasks
|
||||
Make for each task a separate c and h file. The name of the file is the name of the task.
|
||||
This way we can keep the code clean.
|
||||
|
||||
[tasks_and_taskowners.md](tasks_and_taskowners.md)
|
||||
|
||||
## Style Guide
|
||||
To maintain a consistent and clean codebase, follow the [style guide](style_guide.md). This document provides detailed instructions on naming conventions, code structure, and commenting practices.
|
||||
To maintain a consistent and clean codebase, follow the [style guide](docs/style_guide.md). This document provides detailed instructions on naming conventions, code structure, and commenting practices.
|
||||
|
||||
Please read the [style_guide.md](style_guide.md) carefully before making contributions.
|
||||
Please read the [style_guide.md](docs/style_guide.md) carefully before making contributions.
|
||||
|
||||
### Editor Configuration
|
||||
|
||||
@@ -30,106 +38,26 @@ You can choose from the following options:
|
||||
- For Eclipse-based editors, including STM32CubeIDE.
|
||||
- You can import it within eclipse settings, `Preferences -> LANGUAGE -> Code Style -> Formatter` tab.
|
||||
|
||||
## Logging and Debugging Messages
|
||||
The logging system is designed to make log messages clearer and improve their uniformity across the project.
|
||||
It makes it easier to see where a log message comes from and what its verbosity is.
|
||||
## Commit Messages Conventions
|
||||
The subject line of a commit message should follow the following rules:
|
||||
- Short and descriptive (max 50 chars)
|
||||
- In imperative present tense
|
||||
- Capitalized
|
||||
- Not end with a period
|
||||
|
||||
To use the logging system, include `log.h` in your source file, and use the following macros to print log messages:
|
||||
```c
|
||||
LOG_DEBUG(TAG, fmt, ...)
|
||||
LOG_INFO(TAG, fmt, ...)
|
||||
LOG_WARN(TAG, fmt, ...)
|
||||
LOG_CRIT(TAG, fmt, ...)
|
||||
LOG_FATAL(TAG, fmt, ...)
|
||||
Example:
|
||||
```
|
||||
The same format specifiers as in `printf` can be used.
|
||||
|
||||
The `TAG` parameter is a string that identifies the source of the log message.
|
||||
It is recommended to use one tag for each source file / module, and to name the tag after the source file / module.
|
||||
See the example below.
|
||||
|
||||
### Global Log Level
|
||||
You can control the verbosity of the logging output by setting a global log level in `log.h`.
|
||||
This log level filters out messages with a lower priority.
|
||||
|
||||
### Custom Log Levels
|
||||
before you include `log.h`, you can define custom log levels by defining the following macros:
|
||||
```c
|
||||
// All log messages will be printed
|
||||
#define LOGGER_LEVEL_ALL
|
||||
#include "log.h"
|
||||
Implement access right management
|
||||
```
|
||||
|
||||
```c
|
||||
// Info and higher priority messages will be printed
|
||||
#define LOGGER_LEVEL_INFO
|
||||
#include "log.h"
|
||||
```
|
||||
The body of a commit message may be used to explain the what and why of a commit.
|
||||
|
||||
```c
|
||||
// Only warnings and errors will be printed
|
||||
#define LOGGER_LEVEL_WARN
|
||||
#include "log.h"
|
||||
```
|
||||
## Documentation
|
||||
Documentation is placed in the [docs](docs) folder.
|
||||
If your part needs documentation (e.g. how to use tcp cmd interface), add a markdown file in the above-mentioned folder.
|
||||
|
||||
```c
|
||||
// Only log messages with level ERROR will be printed
|
||||
#define LOGGER_LEVEL_CRITICAL
|
||||
#include "log.h"
|
||||
```
|
||||
|
||||
```c
|
||||
// Only log messages with level ERROR will be printed
|
||||
#define LOGGER_LEVEL_FATAL
|
||||
#include "log.h"
|
||||
```
|
||||
|
||||
### Colorful Log Messages
|
||||
For improved readability, log messages can be printed in color by defining `LOGGER_USE_COLOR` in `log.h` or before you include `log.h`. \
|
||||
Default is `0`
|
||||
|
||||
### Log Output Format
|
||||
Each log entry is formatted to include the following information:
|
||||
|
||||
- Log level ([Debug], [Info], [Warn], [Critical], [Fatal])
|
||||
- Timestamp (in milliseconds since boot)
|
||||
- Tag
|
||||
- The log message
|
||||
|
||||
For instance, a log entry may look like this:
|
||||
|
||||
`[Info] (2009) [LTDC]: This is a log message`
|
||||
|
||||
### Example
|
||||
```c
|
||||
#define LOGGER_LEVEL_INFO
|
||||
#include "log.h"
|
||||
|
||||
// Don't use a define for the tag, as the pointer to the tag is used
|
||||
static const char *TAG = "main";
|
||||
|
||||
int main(void) {
|
||||
|
||||
LOG_DEBUG(TAG, "This message will not be printed");
|
||||
LOG_INFO(TAG, "This message will be printed");
|
||||
LOG_WARN(TAG, "This message will be printed");
|
||||
LOG_CRIT(TAG, "This message will be printed");
|
||||
LOG_FATAL(TAG, "This message will be printed");
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
LOG_INFO(TAG, "Iteration %d of %d", i, 3);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Result:
|
||||
```
|
||||
[Info] (2009) main: This message will be printed
|
||||
[Warning] (2026) main: This message will be printed
|
||||
[CRITICAL] (2033) main: This message will be printed
|
||||
[FATAL] (2040) main: This message will be printed
|
||||
[Info] (2040) main: Iteration 0 of 3
|
||||
[Info] (2047) main: Iteration 1 of 3
|
||||
[Info] (2054) main: Iteration 2 of 3
|
||||
```
|
||||
This folder contains the following documents:
|
||||
- [llfs.md](docs/llfs.md): Linked List File System
|
||||
- [logger.md](docs/logger.md): Logging and Debugging Messages
|
||||
- [mkllfs.md](docs/mkllfs.md): Make Linked List File System
|
||||
- [style_guide.md](docs/style_guide.md): Style Guide
|
||||
57
docs/llfs.md
Normal file
57
docs/llfs.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# LLFS (Linked List File System)
|
||||
|
||||
## Introduction
|
||||
The llfs filesystem can be generated using the mkllfss utility.
|
||||
It is a simple filesystem that uses a linked list to store files.
|
||||
The filesystem is stored in a single c file (`llfs_data.c`), which can be compiled and read by the llfs library.
|
||||
The llfs filesystem is a flat filesystem, meaning that it does not support directories.
|
||||
|
||||
## Usage of the llfs API
|
||||
### Getting a list of files
|
||||
```c
|
||||
#include "llfs.h"
|
||||
|
||||
void main(void) {
|
||||
// Allocate space for 10 files
|
||||
llfs_file_t file_list[10];
|
||||
|
||||
// Get the file list
|
||||
size_t file_count = llfs_file_list(file_list, 10, NULL);
|
||||
|
||||
// Loop through the files and print their names and sizes
|
||||
for (int i = 0; i < file_count; i++) {
|
||||
LOG_INFO(TAG, "File: %s, size: %d", file_list[i].name, file_list[i].len);
|
||||
}
|
||||
}
|
||||
```
|
||||
Result:
|
||||
```
|
||||
[Info] (2009) [main]: File: image.bmp, size: 9270
|
||||
[Info] (2013) [main]: File: python_file.py, size: 645
|
||||
[Info] (2019) [main]: File: image2.bmp, size: 7738
|
||||
[Info] (2024) [main]: File: filename with a space.txt, size: 61
|
||||
[Info] (2031) [main]: File: file1.txt, size: 77
|
||||
```
|
||||
|
||||
### Reading a file
|
||||
```c
|
||||
#include "llfs.h"
|
||||
|
||||
void main(void) {
|
||||
// Get a file by name
|
||||
llfs_file_t *file = llfs_file_open("filename with a space.txt");
|
||||
|
||||
if (file != NULL) {
|
||||
// Print the file name, size and data
|
||||
LOG_INFO(TAG, "File found: %s, size: %d", file->name, file->len);
|
||||
LOG_INFO(TAG, "File data: %s", file->data);
|
||||
} else {
|
||||
LOG_WARN(TAG, "File not found");
|
||||
}
|
||||
}
|
||||
```
|
||||
Result:
|
||||
```
|
||||
[Info] (2040) [main]: File found: filename with a space.txt, size: 61
|
||||
[Info] (2047) [main]: File data: This is a file with a space in it's filename.
|
||||
```
|
||||
103
docs/logger.md
Normal file
103
docs/logger.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# Logging and Debugging Messages
|
||||
The logging system is designed to make log messages clearer and improve their uniformity across the project.
|
||||
It makes it easier to see where a log message comes from and what its verbosity is.
|
||||
|
||||
To use the logging system, include `log.h` in your source file, and use the following macros to print log messages:
|
||||
```c
|
||||
LOG_DEBUG(TAG, fmt, ...)
|
||||
LOG_INFO(TAG, fmt, ...)
|
||||
LOG_WARN(TAG, fmt, ...)
|
||||
LOG_CRIT(TAG, fmt, ...)
|
||||
LOG_FATAL(TAG, fmt, ...)
|
||||
```
|
||||
The same format specifiers as in `printf` can be used.
|
||||
|
||||
The `TAG` parameter is a string that identifies the source of the log message.
|
||||
It is recommended to use one tag for each source file / module, and to name the tag after the source file / module.
|
||||
See the example below.
|
||||
|
||||
## Global Log Level
|
||||
You can control the verbosity of the logging output by setting a global log level in `log.h`.
|
||||
This log level filters out messages with a lower priority.
|
||||
|
||||
## Custom Log Levels
|
||||
before you include `log.h`, you can define custom log levels by defining the following macros:
|
||||
```c
|
||||
// All log messages will be printed
|
||||
#define LOGGER_LEVEL_ALL
|
||||
#include "log.h"
|
||||
```
|
||||
|
||||
```c
|
||||
// Info and higher priority messages will be printed
|
||||
#define LOGGER_LEVEL_INFO
|
||||
#include "log.h"
|
||||
```
|
||||
|
||||
```c
|
||||
// Only warnings and errors will be printed
|
||||
#define LOGGER_LEVEL_WARN
|
||||
#include "log.h"
|
||||
```
|
||||
|
||||
```c
|
||||
// Only log messages with level ERROR will be printed
|
||||
#define LOGGER_LEVEL_CRITICAL
|
||||
#include "log.h"
|
||||
```
|
||||
|
||||
```c
|
||||
// Only log messages with level ERROR will be printed
|
||||
#define LOGGER_LEVEL_FATAL
|
||||
#include "log.h"
|
||||
```
|
||||
|
||||
## Colorful Log Messages
|
||||
For improved readability, log messages can be printed in color by defining `LOGGER_USE_COLOR` in `log.h` or before you include `log.h`. \
|
||||
Default is `0`
|
||||
|
||||
## Log Output Format
|
||||
Each log entry is formatted to include the following information:
|
||||
|
||||
- Log level ([Debug], [Info], [Warn], [Critical], [Fatal])
|
||||
- Timestamp (in milliseconds since boot)
|
||||
- Tag
|
||||
- The log message
|
||||
|
||||
For instance, a log entry may look like this:
|
||||
|
||||
`[Info] (2009) [LTDC]: This is a log message`
|
||||
|
||||
## Example
|
||||
```c
|
||||
#define LOGGER_LEVEL_INFO
|
||||
#include "log.h"
|
||||
|
||||
// Don't use a define for the tag, as the pointer to the tag is used
|
||||
static const char *TAG = "main";
|
||||
|
||||
int main(void) {
|
||||
|
||||
LOG_DEBUG(TAG, "This message will not be printed");
|
||||
LOG_INFO(TAG, "This message will be printed");
|
||||
LOG_WARN(TAG, "This message will be printed");
|
||||
LOG_CRIT(TAG, "This message will be printed");
|
||||
LOG_FATAL(TAG, "This message will be printed");
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
LOG_INFO(TAG, "Iteration %d of %d", i, 3);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Result:
|
||||
```
|
||||
[Info] (2009) [main]: This message will be printed
|
||||
[Warning] (2026) [main]: This message will be printed
|
||||
[CRITICAL] (2033) [main]: This message will be printed
|
||||
[FATAL] (2040) [main]: This message will be printed
|
||||
[Info] (2040) [main]: Iteration 0 of 3
|
||||
[Info] (2047) [main]: Iteration 1 of 3
|
||||
[Info] (2054) [main]: Iteration 2 of 3
|
||||
```
|
||||
33
docs/mkllfs.md
Normal file
33
docs/mkllfs.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# MKLLFS (Make Linked List File System)
|
||||
|
||||
## Introduction
|
||||
The llfs filesystem can be generated using the mkllfss utility. It is a simple filesystem that uses a linked list to store files.
|
||||
The filesystem is stored in a single c file (llfs_data.c), which can be compiled and read by the llfs library.
|
||||
The llfs filesystem is a flat filesystem, meaning that it does not support directories.
|
||||
|
||||
The mkllfs utilit can be used to generate the `llfs_data.c` file. The `llfs_data.c` file from a directory with files.
|
||||
|
||||
## Usage
|
||||
The mkllfs utility can be used as follows:
|
||||
```bash
|
||||
mkllfs [options] <directory> <output_file>
|
||||
```
|
||||
For example:
|
||||
```bash
|
||||
mkllfs data llfs_data.c
|
||||
```
|
||||
Available options:
|
||||
```
|
||||
-h, --help show the help message and exit
|
||||
-v, --version print the version of mkllfs and the llfs library that it generates for
|
||||
```
|
||||
|
||||
## Building
|
||||
The mkllfs utility can be built using the following command:
|
||||
```bash
|
||||
gcc -o mkllfs mkllfs.c
|
||||
```
|
||||
For windows, you can use the following command:
|
||||
```bash
|
||||
gcc -o mkllfs.exe mkllfs.c
|
||||
```
|
||||
2
mkllfs/.gitignore
vendored
Normal file
2
mkllfs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
cmake-build-debug
|
||||
.idea
|
||||
6
mkllfs/CMakeLists.txt
Normal file
6
mkllfs/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
project(mkllfs C)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
add_executable(mkllfs main.c)
|
||||
187
mkllfs/main.c
Normal file
187
mkllfs/main.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* @file main.c
|
||||
* @brief Converts files to a C file that can be used by llfs (linked list file system).
|
||||
* @version 0.1.0
|
||||
* @author Lorenz C.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "tinydir.h"
|
||||
|
||||
#define VERSION "0.1.0"
|
||||
#define LLFS_VERSION "0.1.0"
|
||||
#define MAX_PATH_LEN 256
|
||||
|
||||
static void file_name_to_llfs_name(char* llfs_name, const char* file_name);
|
||||
static void print_help(void);
|
||||
static void print_version(void);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char src_path[MAX_PATH_LEN];
|
||||
char llfs_name[MAX_PATH_LEN];
|
||||
char prev_llfs_name[MAX_PATH_LEN] = "NULL";
|
||||
FILE* out_file;
|
||||
FILE* src_file;
|
||||
tinydir_dir dir;
|
||||
int file_count = 0;
|
||||
|
||||
// Check if the help or version flag is set
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||
print_help();
|
||||
return 0;
|
||||
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
||||
print_version();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if at least 2 arguments are given (directory and output file)
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "Error: Not enough arguments.\r\n");
|
||||
print_help();
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
// Open the output file
|
||||
out_file = fopen(argv[argc - 1], "w");
|
||||
if (out_file == NULL) {
|
||||
fprintf(stderr, "Error: Could not open output file\r\n");
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
// Open the source directory
|
||||
strcpy(src_path, argv[argc - 2]);
|
||||
if (tinydir_open(&dir, src_path) == -1) {
|
||||
fprintf(stderr, "Error: Could not open directory\r\n");
|
||||
goto fail_open_dir;
|
||||
}
|
||||
|
||||
// Write the header of the output file
|
||||
fprintf(out_file, "/**\n"
|
||||
" * @file %s\n"
|
||||
" * @brief Linked list file system (llfs) data file.\n"
|
||||
" * This file was generated by mkllfs %s.\n"
|
||||
" * @date %s\n */\n\n",
|
||||
argv[argc - 1], VERSION, __DATE__);
|
||||
|
||||
// Write the headers of the output file
|
||||
fprintf(out_file, "#include <stddef.h>\n");
|
||||
fprintf(out_file, "#include \"llfs.h\"\n");
|
||||
fprintf(out_file, "\n");
|
||||
|
||||
// Iterate over all files in the directory
|
||||
while (dir.has_next) {
|
||||
tinydir_file file;
|
||||
size_t file_size = 0;
|
||||
|
||||
// Get file info
|
||||
if (tinydir_readfile(&dir, &file) == -1) {
|
||||
fprintf(stderr, "Error: Could not read file\r\n");
|
||||
continue;
|
||||
}
|
||||
if (file.is_dir) {
|
||||
tinydir_next(&dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Open the source file
|
||||
src_file = fopen(file.path, "rb");
|
||||
if (src_file == NULL) {
|
||||
printf("Error: Could not open source file: %s\r\n", file.path);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert the file name to a valid llfs name
|
||||
file_name_to_llfs_name(llfs_name, file.name);
|
||||
|
||||
// Write the file data to the output file
|
||||
fprintf(out_file, "// File: %s\n", file.name);
|
||||
fprintf(out_file, "const uint8_t %s_data[] = {\n", llfs_name);
|
||||
for (int i = 0, c; (c = fgetc(src_file)) != EOF; i++) {
|
||||
file_size++;
|
||||
fprintf(out_file, "0x%02X, ", c);
|
||||
if (i % 16 == 15) {
|
||||
fprintf(out_file, "\n");
|
||||
}
|
||||
}
|
||||
fprintf(out_file, "\n};\n");
|
||||
|
||||
// Write the file info to the output file
|
||||
fprintf(out_file,
|
||||
"const struct llfs_data_file %s = {\n .data = %s_data,\n .name = \"%s\",\n .len = %zu,\n "
|
||||
".next =%s%s\n};\n\n",
|
||||
llfs_name, llfs_name, file.name, file_size, strcmp(prev_llfs_name, "NULL") == 0 ? " " : " &",
|
||||
prev_llfs_name);
|
||||
|
||||
file_count++;
|
||||
tinydir_next(&dir);
|
||||
strcpy(prev_llfs_name, llfs_name);
|
||||
fclose(src_file);
|
||||
}
|
||||
|
||||
// Make the last file the root file of the llfs
|
||||
fprintf(out_file, "\n");
|
||||
fprintf(out_file, "const struct llfs_data_file *llfs_root = &%s;\n", prev_llfs_name);
|
||||
|
||||
// Print the number of files
|
||||
printf("Successfully converted %d files.\r\n", file_count);
|
||||
|
||||
fail:
|
||||
tinydir_close(&dir);
|
||||
fail_open_dir:
|
||||
fclose(out_file);
|
||||
fail_init:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a file name to a valid llfs name, so that it can be used as a variable name in the generated C file.
|
||||
* The length of the generated llfs name is the same as the length of the original file name.
|
||||
*
|
||||
* @param[out] llfs_name The output llfs name. Must be at least as long as the file name.
|
||||
* @param[in] file_name The input file name as a null terminated string.
|
||||
*/
|
||||
static void file_name_to_llfs_name(char* llfs_name, const char* file_name) {
|
||||
size_t len = strlen(file_name);
|
||||
|
||||
// Replace all invalid characters with '_'
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (file_name[i] == '.' || file_name[i] == '-' || file_name[i] == ' ') {
|
||||
llfs_name[i] = '_';
|
||||
} else {
|
||||
llfs_name[i] = file_name[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the first character if it is a number
|
||||
if (llfs_name[0] >= '0' && llfs_name[0] <= '9') {
|
||||
llfs_name[0] = '_';
|
||||
}
|
||||
|
||||
llfs_name[len] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints the help message.
|
||||
*/
|
||||
static void print_help(void) {
|
||||
printf("Make Linked list file system (mkllfs)\r\n");
|
||||
printf("Converts files into a linked list file system.\r\n");
|
||||
printf("By Lorenz C.\r\n");
|
||||
printf("\r\n");
|
||||
printf("Usage: mkllfs [options] <directory> <output file>\r\n");
|
||||
printf("Options:\r\n");
|
||||
printf(" -h, --help: Print this help message\r\n");
|
||||
printf(" -v, --version: Print the version\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints the version.
|
||||
*/
|
||||
static void print_version(void) {
|
||||
printf("mkllfs version %s\r\n", VERSION);
|
||||
printf("llfs version %s\r\n", LLFS_VERSION);
|
||||
}
|
||||
838
mkllfs/tinydir.h
Normal file
838
mkllfs/tinydir.h
Normal file
@@ -0,0 +1,838 @@
|
||||
/*
|
||||
Copyright (c) 2013-2021, tinydir authors:
|
||||
- Cong Xu
|
||||
- Lautis Sun
|
||||
- Baudouin Feildel
|
||||
- Andargor <andargor@yahoo.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef TINYDIR_H
|
||||
#define TINYDIR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if ((defined _UNICODE) && !(defined UNICODE))
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
#if ((defined UNICODE) && !(defined _UNICODE))
|
||||
#define _UNICODE
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _MSC_VER
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <tchar.h>
|
||||
# pragma warning(push)
|
||||
# pragma warning (disable : 4996)
|
||||
#else
|
||||
# include <dirent.h>
|
||||
# include <libgen.h>
|
||||
# include <sys/stat.h>
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
#ifdef __MINGW32__
|
||||
# include <tchar.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* types */
|
||||
|
||||
/* Windows UNICODE wide character support */
|
||||
#if defined _MSC_VER || defined __MINGW32__
|
||||
# define _tinydir_char_t TCHAR
|
||||
# define TINYDIR_STRING(s) _TEXT(s)
|
||||
# define _tinydir_strlen _tcslen
|
||||
# define _tinydir_strcpy _tcscpy
|
||||
# define _tinydir_strcat _tcscat
|
||||
# define _tinydir_strcmp _tcscmp
|
||||
# define _tinydir_strrchr _tcsrchr
|
||||
# define _tinydir_strncmp _tcsncmp
|
||||
#else
|
||||
# define _tinydir_char_t char
|
||||
# define TINYDIR_STRING(s) s
|
||||
# define _tinydir_strlen strlen
|
||||
# define _tinydir_strcpy strcpy
|
||||
# define _tinydir_strcat strcat
|
||||
# define _tinydir_strcmp strcmp
|
||||
# define _tinydir_strrchr strrchr
|
||||
# define _tinydir_strncmp strncmp
|
||||
#endif
|
||||
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
# include <windows.h>
|
||||
# define _TINYDIR_PATH_MAX MAX_PATH
|
||||
#elif defined __linux__
|
||||
# include <limits.h>
|
||||
# ifdef PATH_MAX
|
||||
# define _TINYDIR_PATH_MAX PATH_MAX
|
||||
# endif
|
||||
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
# include <sys/param.h>
|
||||
# if defined(BSD)
|
||||
# include <limits.h>
|
||||
# ifdef PATH_MAX
|
||||
# define _TINYDIR_PATH_MAX PATH_MAX
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef _TINYDIR_PATH_MAX
|
||||
#define _TINYDIR_PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* extra chars for the "\\*" mask */
|
||||
# define _TINYDIR_PATH_EXTRA 2
|
||||
#else
|
||||
# define _TINYDIR_PATH_EXTRA 0
|
||||
#endif
|
||||
|
||||
#define _TINYDIR_FILENAME_MAX 256
|
||||
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
#define _TINYDIR_DRIVE_MAX 3
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define _TINYDIR_FUNC static __inline
|
||||
#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
|
||||
# define _TINYDIR_FUNC static __inline__
|
||||
#elif defined(__cplusplus)
|
||||
# define _TINYDIR_FUNC static inline
|
||||
#elif defined(__GNUC__)
|
||||
/* Suppress unused function warning */
|
||||
# define _TINYDIR_FUNC __attribute__((unused)) static
|
||||
#else
|
||||
# define _TINYDIR_FUNC static
|
||||
#endif
|
||||
|
||||
/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
|
||||
#ifdef TINYDIR_USE_READDIR_R
|
||||
|
||||
/* readdir_r is a POSIX-only function, and may not be available under various
|
||||
* environments/settings, e.g. MinGW. Use readdir fallback */
|
||||
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
|
||||
_POSIX_SOURCE
|
||||
# define _TINYDIR_HAS_READDIR_R
|
||||
#endif
|
||||
#if _POSIX_C_SOURCE >= 200112L
|
||||
# define _TINYDIR_HAS_FPATHCONF
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#if _BSD_SOURCE || _SVID_SOURCE || \
|
||||
(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
|
||||
# define _TINYDIR_HAS_DIRFD
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
|
||||
defined _PC_NAME_MAX
|
||||
# define _TINYDIR_USE_FPATHCONF
|
||||
#endif
|
||||
#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
|
||||
!(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
|
||||
# define _TINYDIR_USE_READDIR
|
||||
#endif
|
||||
|
||||
/* Use readdir by default */
|
||||
#else
|
||||
# define _TINYDIR_USE_READDIR
|
||||
#endif
|
||||
|
||||
/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
|
||||
#ifndef _MSC_VER
|
||||
#if (defined __MINGW32__) && (defined _UNICODE)
|
||||
#define _TINYDIR_DIR _WDIR
|
||||
#define _tinydir_dirent _wdirent
|
||||
#define _tinydir_opendir _wopendir
|
||||
#define _tinydir_readdir _wreaddir
|
||||
#define _tinydir_closedir _wclosedir
|
||||
#else
|
||||
#define _TINYDIR_DIR DIR
|
||||
#define _tinydir_dirent dirent
|
||||
#define _tinydir_opendir opendir
|
||||
#define _tinydir_readdir readdir
|
||||
#define _tinydir_closedir closedir
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
|
||||
#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
|
||||
#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
|
||||
#else
|
||||
#error "Either define both alloc and free or none of them!"
|
||||
#endif
|
||||
|
||||
#if !defined(_TINYDIR_MALLOC)
|
||||
#define _TINYDIR_MALLOC(_size) malloc(_size)
|
||||
#define _TINYDIR_FREE(_ptr) free(_ptr)
|
||||
#endif /* !defined(_TINYDIR_MALLOC) */
|
||||
|
||||
typedef struct tinydir_file
|
||||
{
|
||||
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||
_tinydir_char_t name[_TINYDIR_FILENAME_MAX];
|
||||
_tinydir_char_t *extension;
|
||||
int is_dir;
|
||||
int is_reg;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifdef __MINGW32__
|
||||
struct _stat _s;
|
||||
#else
|
||||
struct stat _s;
|
||||
#endif
|
||||
#endif
|
||||
} tinydir_file;
|
||||
|
||||
typedef struct tinydir_dir
|
||||
{
|
||||
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||
int has_next;
|
||||
size_t n_files;
|
||||
|
||||
tinydir_file *_files;
|
||||
#ifdef _MSC_VER
|
||||
HANDLE _h;
|
||||
WIN32_FIND_DATA _f;
|
||||
#else
|
||||
_TINYDIR_DIR *_d;
|
||||
struct _tinydir_dirent *_e;
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
struct _tinydir_dirent *_ep;
|
||||
#endif
|
||||
#endif
|
||||
} tinydir_dir;
|
||||
|
||||
|
||||
/* declarations */
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
|
||||
_TINYDIR_FUNC
|
||||
void tinydir_close(tinydir_dir *dir);
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_next(tinydir_dir *dir);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
|
||||
_TINYDIR_FUNC
|
||||
void _tinydir_get_ext(tinydir_file *file);
|
||||
_TINYDIR_FUNC
|
||||
int _tinydir_file_cmp(const void *a, const void *b);
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
_TINYDIR_FUNC
|
||||
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* definitions*/
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
|
||||
{
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
int error;
|
||||
int size; /* using int size */
|
||||
#endif
|
||||
#else
|
||||
_tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
|
||||
#endif
|
||||
_tinydir_char_t *pathp;
|
||||
|
||||
if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* initialise dir */
|
||||
dir->_files = NULL;
|
||||
#ifdef _MSC_VER
|
||||
dir->_h = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
dir->_d = NULL;
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
dir->_ep = NULL;
|
||||
#endif
|
||||
#endif
|
||||
tinydir_close(dir);
|
||||
|
||||
_tinydir_strcpy(dir->path, path);
|
||||
/* Remove trailing slashes */
|
||||
pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
|
||||
while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
|
||||
{
|
||||
*pathp = TINYDIR_STRING('\0');
|
||||
pathp++;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
_tinydir_strcpy(path_buf, dir->path);
|
||||
_tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
|
||||
#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
|
||||
dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
|
||||
#else
|
||||
dir->_h = FindFirstFile(path_buf, &dir->_f);
|
||||
#endif
|
||||
if (dir->_h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
errno = ENOENT;
|
||||
#else
|
||||
dir->_d = _tinydir_opendir(path);
|
||||
if (dir->_d == NULL)
|
||||
{
|
||||
#endif
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* read first file */
|
||||
dir->has_next = 1;
|
||||
#ifndef _MSC_VER
|
||||
#ifdef _TINYDIR_USE_READDIR
|
||||
dir->_e = _tinydir_readdir(dir->_d);
|
||||
#else
|
||||
/* allocate dirent buffer for readdir_r */
|
||||
size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
|
||||
if (size == -1) return -1;
|
||||
dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
|
||||
if (dir->_ep == NULL) return -1;
|
||||
|
||||
error = readdir_r(dir->_d, dir->_ep, &dir->_e);
|
||||
if (error != 0) return -1;
|
||||
#endif
|
||||
if (dir->_e == NULL)
|
||||
{
|
||||
dir->has_next = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
tinydir_close(dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
|
||||
{
|
||||
/* Count the number of files first, to pre-allocate the files array */
|
||||
size_t n_files = 0;
|
||||
if (tinydir_open(dir, path) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while (dir->has_next)
|
||||
{
|
||||
n_files++;
|
||||
if (tinydir_next(dir) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
tinydir_close(dir);
|
||||
|
||||
if (n_files == 0 || tinydir_open(dir, path) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
dir->n_files = 0;
|
||||
dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
|
||||
if (dir->_files == NULL)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
while (dir->has_next)
|
||||
{
|
||||
tinydir_file *p_file;
|
||||
dir->n_files++;
|
||||
|
||||
p_file = &dir->_files[dir->n_files - 1];
|
||||
if (tinydir_readfile(dir, p_file) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (tinydir_next(dir) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Just in case the number of files has changed between the first and
|
||||
second reads, terminate without writing into unallocated memory */
|
||||
if (dir->n_files == n_files)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
tinydir_close(dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
void tinydir_close(tinydir_dir *dir)
|
||||
{
|
||||
if (dir == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memset(dir->path, 0, sizeof(dir->path));
|
||||
dir->has_next = 0;
|
||||
dir->n_files = 0;
|
||||
_TINYDIR_FREE(dir->_files);
|
||||
dir->_files = NULL;
|
||||
#ifdef _MSC_VER
|
||||
if (dir->_h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FindClose(dir->_h);
|
||||
}
|
||||
dir->_h = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
if (dir->_d)
|
||||
{
|
||||
_tinydir_closedir(dir->_d);
|
||||
}
|
||||
dir->_d = NULL;
|
||||
dir->_e = NULL;
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
_TINYDIR_FREE(dir->_ep);
|
||||
dir->_ep = NULL;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_next(tinydir_dir *dir)
|
||||
{
|
||||
if (dir == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!dir->has_next)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
if (FindNextFile(dir->_h, &dir->_f) == 0)
|
||||
#else
|
||||
#ifdef _TINYDIR_USE_READDIR
|
||||
dir->_e = _tinydir_readdir(dir->_d);
|
||||
#else
|
||||
if (dir->_ep == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
if (dir->_e == NULL)
|
||||
#endif
|
||||
{
|
||||
dir->has_next = 0;
|
||||
#ifdef _MSC_VER
|
||||
if (GetLastError() != ERROR_SUCCESS &&
|
||||
GetLastError() != ERROR_NO_MORE_FILES)
|
||||
{
|
||||
tinydir_close(dir);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
|
||||
{
|
||||
const _tinydir_char_t *filename;
|
||||
if (dir == NULL || file == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
if (dir->_h == INVALID_HANDLE_VALUE)
|
||||
#else
|
||||
if (dir->_e == NULL)
|
||||
#endif
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
filename =
|
||||
#ifdef _MSC_VER
|
||||
dir->_f.cFileName;
|
||||
#else
|
||||
dir->_e->d_name;
|
||||
#endif
|
||||
if (_tinydir_strlen(dir->path) +
|
||||
_tinydir_strlen(filename) + 1 + _TINYDIR_PATH_EXTRA >=
|
||||
_TINYDIR_PATH_MAX)
|
||||
{
|
||||
/* the path for the file will be too long */
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_tinydir_strcpy(file->path, dir->path);
|
||||
if (_tinydir_strcmp(dir->path, TINYDIR_STRING("/")) != 0)
|
||||
_tinydir_strcat(file->path, TINYDIR_STRING("/"));
|
||||
_tinydir_strcpy(file->name, filename);
|
||||
_tinydir_strcat(file->path, filename);
|
||||
#ifndef _MSC_VER
|
||||
#ifdef __MINGW32__
|
||||
if (_tstat(
|
||||
#elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) \
|
||||
|| ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \
|
||||
|| ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) \
|
||||
|| ((defined __APPLE__) && (defined __MACH__)) \
|
||||
|| (defined BSD)
|
||||
if (lstat(
|
||||
#else
|
||||
if (stat(
|
||||
#endif
|
||||
file->path, &file->_s) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
_tinydir_get_ext(file);
|
||||
|
||||
file->is_dir =
|
||||
#ifdef _MSC_VER
|
||||
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
#else
|
||||
S_ISDIR(file->_s.st_mode);
|
||||
#endif
|
||||
file->is_reg =
|
||||
#ifdef _MSC_VER
|
||||
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
|
||||
(
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
|
||||
#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
|
||||
#endif
|
||||
#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
|
||||
#endif
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
|
||||
#else
|
||||
S_ISREG(file->_s.st_mode);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
|
||||
{
|
||||
if (dir == NULL || file == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (i >= dir->n_files)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(file, &dir->_files[i], sizeof(tinydir_file));
|
||||
_tinydir_get_ext(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
|
||||
{
|
||||
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||
if (dir == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (i >= dir->n_files || !dir->_files[i].is_dir)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_tinydir_strcpy(path, dir->_files[i].path);
|
||||
tinydir_close(dir);
|
||||
if (tinydir_open_sorted(dir, path) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open a single file given its path */
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
|
||||
{
|
||||
tinydir_dir dir;
|
||||
int result = 0;
|
||||
int found = 0;
|
||||
_tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
|
||||
_tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
|
||||
_tinydir_char_t *dir_name;
|
||||
_tinydir_char_t *base_name;
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
_tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
|
||||
_tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
|
||||
#endif
|
||||
|
||||
if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the parent path */
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
|
||||
errno = _tsplitpath_s(
|
||||
path,
|
||||
drive_buf, _TINYDIR_DRIVE_MAX,
|
||||
dir_name_buf, _TINYDIR_FILENAME_MAX,
|
||||
file_name_buf, _TINYDIR_FILENAME_MAX,
|
||||
ext_buf, _TINYDIR_FILENAME_MAX);
|
||||
#else
|
||||
_tsplitpath(
|
||||
path,
|
||||
drive_buf,
|
||||
dir_name_buf,
|
||||
file_name_buf,
|
||||
ext_buf);
|
||||
#endif
|
||||
|
||||
if (errno)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* _splitpath_s not work fine with only filename and widechar support */
|
||||
#ifdef _UNICODE
|
||||
if (drive_buf[0] == L'\xFEFE')
|
||||
drive_buf[0] = '\0';
|
||||
if (dir_name_buf[0] == L'\xFEFE')
|
||||
dir_name_buf[0] = '\0';
|
||||
#endif
|
||||
|
||||
/* Emulate the behavior of dirname by returning "." for dir name if it's
|
||||
empty */
|
||||
if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
|
||||
{
|
||||
_tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
|
||||
}
|
||||
/* Concatenate the drive letter and dir name to form full dir name */
|
||||
_tinydir_strcat(drive_buf, dir_name_buf);
|
||||
dir_name = drive_buf;
|
||||
/* Concatenate the file name and extension to form base name */
|
||||
_tinydir_strcat(file_name_buf, ext_buf);
|
||||
base_name = file_name_buf;
|
||||
#else
|
||||
_tinydir_strcpy(dir_name_buf, path);
|
||||
dir_name = dirname(dir_name_buf);
|
||||
_tinydir_strcpy(file_name_buf, path);
|
||||
base_name = basename(file_name_buf);
|
||||
#endif
|
||||
|
||||
/* Special case: if the path is a root dir, open the parent dir as the file */
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
if (_tinydir_strlen(base_name) == 0)
|
||||
#else
|
||||
if ((_tinydir_strcmp(base_name, TINYDIR_STRING("/"))) == 0)
|
||||
#endif
|
||||
{
|
||||
memset(file, 0, sizeof * file);
|
||||
file->is_dir = 1;
|
||||
file->is_reg = 0;
|
||||
_tinydir_strcpy(file->path, dir_name);
|
||||
file->extension = file->path + _tinydir_strlen(file->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open the parent directory */
|
||||
if (tinydir_open(&dir, dir_name) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read through the parent directory and look for the file */
|
||||
while (dir.has_next)
|
||||
{
|
||||
if (tinydir_readfile(&dir, file) == -1)
|
||||
{
|
||||
result = -1;
|
||||
goto bail;
|
||||
}
|
||||
if (_tinydir_strcmp(file->name, base_name) == 0)
|
||||
{
|
||||
/* File found */
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
tinydir_next(&dir);
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
result = -1;
|
||||
errno = ENOENT;
|
||||
}
|
||||
|
||||
bail:
|
||||
tinydir_close(&dir);
|
||||
return result;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
void _tinydir_get_ext(tinydir_file *file)
|
||||
{
|
||||
_tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
|
||||
if (period == NULL)
|
||||
{
|
||||
file->extension = &(file->name[_tinydir_strlen(file->name)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
file->extension = period + 1;
|
||||
}
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int _tinydir_file_cmp(const void *a, const void *b)
|
||||
{
|
||||
const tinydir_file *fa = (const tinydir_file *)a;
|
||||
const tinydir_file *fb = (const tinydir_file *)b;
|
||||
if (fa->is_dir != fb->is_dir)
|
||||
{
|
||||
return -(fa->is_dir - fb->is_dir);
|
||||
}
|
||||
return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
/*
|
||||
The following authored by Ben Hutchings <ben@decadent.org.uk>
|
||||
from https://womble.decadent.org.uk/readdir_r-advisory.html
|
||||
*/
|
||||
/* Calculate the required buffer size (in bytes) for directory *
|
||||
* entries read from the given directory handle. Return -1 if this *
|
||||
* this cannot be done. *
|
||||
* *
|
||||
* This code does not trust values of NAME_MAX that are less than *
|
||||
* 255, since some systems (including at least HP-UX) incorrectly *
|
||||
* define it to be a smaller value. */
|
||||
_TINYDIR_FUNC
|
||||
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
|
||||
{
|
||||
long name_max;
|
||||
size_t name_end;
|
||||
/* parameter may be unused */
|
||||
(void)dirp;
|
||||
|
||||
#if defined _TINYDIR_USE_FPATHCONF
|
||||
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
|
||||
if (name_max == -1)
|
||||
#if defined(NAME_MAX)
|
||||
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
||||
#else
|
||||
return (size_t)(-1);
|
||||
#endif
|
||||
#elif defined(NAME_MAX)
|
||||
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
||||
#else
|
||||
#error "buffer size for readdir_r cannot be determined"
|
||||
#endif
|
||||
name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
|
||||
return (name_end > sizeof(struct _tinydir_dirent) ?
|
||||
name_end : sizeof(struct _tinydir_dirent));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
# if defined (_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
# endif
|
||||
|
||||
#endif
|
||||
69
project/.clang-format
Normal file
69
project/.clang-format
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: true
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: false
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 120
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: "^<(.*)>"
|
||||
Priority: 0
|
||||
- Regex: ^"(.*)"
|
||||
Priority: 1
|
||||
- Regex: "(.*)"
|
||||
Priority: 2
|
||||
IncludeIsMainRegex: "(_test)?$"
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
Language: Cpp
|
||||
MaxEmptyLinesToKeep: 1
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 1000
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
UseCRLF: false
|
||||
...
|
||||
File diff suppressed because one or more lines are too long
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project>
|
||||
<configuration id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.1923792012" name="Debug">
|
||||
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider class="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" console="false" env-hash="1439583462234250292" id="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="MCU ARM GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||
</provider>
|
||||
</extension>
|
||||
</configuration>
|
||||
<configuration id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.release.2077354707" name="Release">
|
||||
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider class="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" console="false" env-hash="1439583462234250292" id="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="MCU ARM GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||
</provider>
|
||||
</extension>
|
||||
</configuration>
|
||||
</project>
|
||||
@@ -1,5 +0,0 @@
|
||||
635E684B79701B039C64EA45C3F84D30=80FB35DB9B6CC80F943DFC38749575DE
|
||||
66BE74F758C12D739921AEA421D593D3=0
|
||||
8DF89ED150041C4CBC7CB9A9CAA90856=61120D4BCEA54A37FDB6D8A0FEEC8DD5
|
||||
DC22A860405A8BF2F2C095E5B6529F12=61120D4BCEA54A37FDB6D8A0FEEC8DD5
|
||||
eclipse.preferences.version=1
|
||||
@@ -53,7 +53,9 @@ include_directories(LWIP/App LWIP/Target Core/Inc Middlewares/Third_Party/LwIP/s
|
||||
|
||||
add_definitions(-DDEBUG -DUSE_HAL_DRIVER -DSTM32F746xx)
|
||||
|
||||
file(GLOB_RECURSE SOURCES "Core/*.*" "LWIP/*.*" "Middlewares/*.*" "Drivers/*.*")
|
||||
#file(GLOB_RECURSE SOURCES "Core/*.*" "LWIP/*.*" "Middlewares/*.*" "Drivers/*.*")
|
||||
file(GLOB_RECURSE SOURCES "Core/Src/*.*" "Core/Startup/*.*" "LWIP/*.*" "Middlewares/*.*" "Drivers/*.*")
|
||||
|
||||
|
||||
set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/STM32F746NGHX_FLASH.ld)
|
||||
|
||||
|
||||
@@ -52,7 +52,9 @@ include_directories(${includes})
|
||||
|
||||
add_definitions(${defines})
|
||||
|
||||
file(GLOB_RECURSE SOURCES ${sources})
|
||||
#file(GLOB_RECURSE SOURCES ${sources})
|
||||
file(GLOB_RECURSE SOURCES "Core/Src/*.*" "Core/Startup/*.*" "LWIP/*.*" "Middlewares/*.*" "Drivers/*.*")
|
||||
|
||||
|
||||
set(LINKER_SCRIPT $${CMAKE_SOURCE_DIR}/${linkerScript})
|
||||
|
||||
|
||||
61
project/Core/Inc/llfs.h
Normal file
61
project/Core/Inc/llfs.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @file llfs.h
|
||||
* @brief Linked List Filesystem header (llfs)
|
||||
* @author Lorenz C.
|
||||
*/
|
||||
|
||||
#ifndef LLFS_H
|
||||
#define LLFS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Representation of a file in the llfs filesystem
|
||||
*/
|
||||
typedef struct llfs_file {
|
||||
const uint8_t* data; // Pointer to the file data (len bytes)
|
||||
const char* name; // Null-terminated string with the filename
|
||||
size_t len; // Length of the file data
|
||||
} llfs_file_t;
|
||||
|
||||
/**
|
||||
* @brief Internal representation of a file in the filesystem
|
||||
* @warning This struct should only be used in the llfs_data.c file.
|
||||
*/
|
||||
struct llfs_data_file {
|
||||
const uint8_t* data;
|
||||
const char* name;
|
||||
const size_t len;
|
||||
const struct llfs_data_file* next;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get a list of files in the filesystem
|
||||
* Get a list of all the files in the filesystem.
|
||||
*
|
||||
* Use the filter to filter out files with a filename that do not match the filter. (e.g. "*.txt" or "*.png|*.jpg") (not
|
||||
* implemented yet) Multiple filters can be used by separating them with a pipe (|).
|
||||
*
|
||||
* The following members of the llfs_file_t struct are set: name, len and data. @ref llfs_file_t
|
||||
*
|
||||
* @todo Implement file filter
|
||||
*
|
||||
* @param[out] file_list A pointer to an array of llfs_file_t to store the files in @ref llfs_file_t
|
||||
* @param[in] max_files The maximum number of files to return (size of file_list)
|
||||
* @param[in] filter A string with file extensions to filter out. (e.g. "*.txt" or "*.png|*.jpg")
|
||||
* @return The number of files returned
|
||||
*/
|
||||
size_t llfs_file_list(llfs_file_t* file_list, size_t max_files, char* filter);
|
||||
|
||||
/**
|
||||
* @brief Open a file
|
||||
* Get a file from the filesystem by name.
|
||||
*
|
||||
* @param[in] name The name of the file to open
|
||||
* @return A pointer to a llfs_file_t with the file data @ref llfs_file_t
|
||||
* NULL if the file does not exist
|
||||
*/
|
||||
llfs_file_t* llfs_file_open(const char* name);
|
||||
|
||||
#endif // LLFS_H
|
||||
@@ -88,17 +88,17 @@
|
||||
#endif
|
||||
|
||||
#if LOGGER_LEVEL <= 1
|
||||
#define LOG_DEBUG(tag, fmt, ...) printf(LOG_COLOR_D"[Debug] (%lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
|
||||
#define LOG_DEBUG(tag, fmt, ...) printf(LOG_COLOR_D"[Debug] (%lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_DEBUG(tag, fmt, ...)
|
||||
#endif
|
||||
#if LOGGER_LEVEL <= 2
|
||||
#define LOG_INFO(tag, fmt, ...) printf(LOG_COLOR_I"[Info] (%lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
|
||||
#define LOG_INFO(tag, fmt, ...) printf(LOG_COLOR_I"[Info] (%lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_INFO(tag, fmt, ...)
|
||||
#endif
|
||||
#if LOGGER_LEVEL <= 3
|
||||
#define LOG_WARN(tag, fmt, ...) printf(LOG_COLOR_W"[Warning] (%lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
|
||||
#define LOG_WARN(tag, fmt, ...) printf(LOG_COLOR_W"[Warning] (%lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_WARN(tag, fmt, ...)
|
||||
#endif
|
||||
@@ -108,7 +108,7 @@
|
||||
#define LOG_CRIT(tag, fmt, ...)
|
||||
#endif
|
||||
#if LOGGER_LEVEL <= 4
|
||||
#define LOG_FATAL(tag, fmt, ...) printf(LOG_COLOR_F"[Fatal] (%lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
|
||||
#define LOG_FATAL(tag, fmt, ...) printf(LOG_COLOR_F"[Fatal] (%lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_FATAL(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
50
project/Core/Src/llfs.c
Normal file
50
project/Core/Src/llfs.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @file llfs.c
|
||||
* @brief Linked List Filesystem implementation (llfs)
|
||||
* @author Lorenz C.
|
||||
* @todo Implement file extension filter
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#define LOGGER_LEVEL_WARN
|
||||
#include "llfs.h"
|
||||
#include "log.h"
|
||||
|
||||
extern struct llfs_data_file* llfs_root;
|
||||
const char* TAG = "llfs";
|
||||
|
||||
// TODO: Implement file extension filter
|
||||
size_t llfs_file_list(llfs_file_t* file_list, size_t max_files, char* filter) {
|
||||
size_t file_count = 0;
|
||||
const struct llfs_data_file* file = llfs_root;
|
||||
|
||||
LOG_DEBUG(TAG, "Getting file list with filter: %s", filter);
|
||||
|
||||
while (file != NULL && file_count < max_files) {
|
||||
file_list[file_count].data = file->data;
|
||||
file_list[file_count].name = file->name;
|
||||
file_list[file_count].len = file->len;
|
||||
file_count++;
|
||||
file = file->next;
|
||||
}
|
||||
|
||||
LOG_DEBUG(TAG, "Files found: %d", file_count);
|
||||
return file_count;
|
||||
}
|
||||
|
||||
llfs_file_t* llfs_file_open(const char* name) {
|
||||
const struct llfs_data_file* file = llfs_root;
|
||||
|
||||
LOG_DEBUG(TAG, "Opening file: %s", name);
|
||||
|
||||
while (file != NULL) {
|
||||
if (strcmp(file->name, name) == 0) {
|
||||
LOG_DEBUG(TAG, "File found: %s, size: %d", file->name, file->len);
|
||||
return (llfs_file_t*)file;
|
||||
}
|
||||
file = file->next;
|
||||
}
|
||||
|
||||
LOG_DEBUG(TAG, "File not found: %s", name);
|
||||
return NULL;
|
||||
}
|
||||
3152
project/Core/Src/llfs_data.c
Normal file
3152
project/Core/Src/llfs_data.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@
|
||||
|
||||
/* Private includes ----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Includes */
|
||||
#define LOGGER_LEVEL_ALL
|
||||
#include "log.h"
|
||||
|
||||
//#include "test_img.h"
|
||||
@@ -36,7 +37,7 @@
|
||||
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PD */
|
||||
|
||||
static const char *TAG = "main";
|
||||
/* USER CODE END PD */
|
||||
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
@@ -125,6 +126,7 @@ int main(void)
|
||||
/* USER CODE END WHILE */
|
||||
|
||||
/* USER CODE BEGIN 3 */
|
||||
MX_LWIP_Process();
|
||||
}
|
||||
/* USER CODE END 3 */
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
#define NO_SYS 1
|
||||
/*----- Value in opt.h for SYS_LIGHTWEIGHT_PROT: 1 -----*/
|
||||
#define SYS_LIGHTWEIGHT_PROT 0
|
||||
/*----- Default Value for MEM_LIBC_MALLOC: 0 ---*/
|
||||
#define MEM_LIBC_MALLOC 1
|
||||
/*----- Default Value for MEMP_MEM_MALLOC: 0 ---*/
|
||||
#define MEMP_MEM_MALLOC 1
|
||||
/*----- Value in opt.h for MEM_ALIGNMENT: 1 -----*/
|
||||
@@ -97,6 +99,8 @@
|
||||
#define LWIP_HTTPD_CUSTOM_FILES 1
|
||||
/*----- Value in opt.h for HTTPD_USE_CUSTOM_FSDATA: 0 -----*/
|
||||
#define HTTPD_USE_CUSTOM_FSDATA 1
|
||||
/*----- Default Value for LWIP_TFTP: 0 ---*/
|
||||
#define LWIP_TFTP 1
|
||||
/*----- Value in opt.h for LWIP_STATS: 1 -----*/
|
||||
#define LWIP_STATS 0
|
||||
/*----- Value in opt.h for CHECKSUM_GEN_IP: 1 -----*/
|
||||
|
||||
435
project/Middlewares/Third_Party/LwIP/src/apps/tftp/tftp_server.c
vendored
Normal file
435
project/Middlewares/Third_Party/LwIP/src/apps/tftp/tftp_server.c
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
/**
|
||||
*
|
||||
* @file tftp_server.c
|
||||
*
|
||||
* @author Logan Gunthorpe <logang@deltatee.com>
|
||||
* Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
*
|
||||
* @brief Trivial File Transfer Protocol (RFC 1350)
|
||||
*
|
||||
* Copyright (c) Deltatee Enterprises Ltd. 2013
|
||||
* All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification,are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Logan Gunthorpe <logang@deltatee.com>
|
||||
* Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tftp TFTP server
|
||||
* @ingroup apps
|
||||
*
|
||||
* This is simple TFTP server for the lwIP raw API.
|
||||
*/
|
||||
|
||||
#include "lwip/apps/tftp_server.h"
|
||||
|
||||
#if LWIP_UDP
|
||||
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/timeouts.h"
|
||||
#include "lwip/debug.h"
|
||||
|
||||
#define TFTP_MAX_PAYLOAD_SIZE 512
|
||||
#define TFTP_HEADER_LENGTH 4
|
||||
|
||||
#define TFTP_RRQ 1
|
||||
#define TFTP_WRQ 2
|
||||
#define TFTP_DATA 3
|
||||
#define TFTP_ACK 4
|
||||
#define TFTP_ERROR 5
|
||||
|
||||
enum tftp_error {
|
||||
TFTP_ERROR_FILE_NOT_FOUND = 1,
|
||||
TFTP_ERROR_ACCESS_VIOLATION = 2,
|
||||
TFTP_ERROR_DISK_FULL = 3,
|
||||
TFTP_ERROR_ILLEGAL_OPERATION = 4,
|
||||
TFTP_ERROR_UNKNOWN_TRFR_ID = 5,
|
||||
TFTP_ERROR_FILE_EXISTS = 6,
|
||||
TFTP_ERROR_NO_SUCH_USER = 7
|
||||
};
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct tftp_state {
|
||||
const struct tftp_context *ctx;
|
||||
void *handle;
|
||||
struct pbuf *last_data;
|
||||
struct udp_pcb *upcb;
|
||||
ip_addr_t addr;
|
||||
u16_t port;
|
||||
int timer;
|
||||
int last_pkt;
|
||||
u16_t blknum;
|
||||
u8_t retries;
|
||||
u8_t mode_write;
|
||||
};
|
||||
|
||||
static struct tftp_state tftp_state;
|
||||
|
||||
static void tftp_tmr(void *arg);
|
||||
|
||||
static void
|
||||
close_handle(void)
|
||||
{
|
||||
tftp_state.port = 0;
|
||||
ip_addr_set_any(0, &tftp_state.addr);
|
||||
|
||||
if (tftp_state.last_data != NULL) {
|
||||
pbuf_free(tftp_state.last_data);
|
||||
tftp_state.last_data = NULL;
|
||||
}
|
||||
|
||||
sys_untimeout(tftp_tmr, NULL);
|
||||
|
||||
if (tftp_state.handle) {
|
||||
tftp_state.ctx->close(tftp_state.handle);
|
||||
tftp_state.handle = NULL;
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
|
||||
{
|
||||
int str_length = strlen(str);
|
||||
struct pbuf *p;
|
||||
u16_t *payload;
|
||||
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM);
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
payload = (u16_t *) p->payload;
|
||||
payload[0] = PP_HTONS(TFTP_ERROR);
|
||||
payload[1] = lwip_htons(code);
|
||||
MEMCPY(&payload[2], str, str_length + 1);
|
||||
|
||||
udp_sendto(tftp_state.upcb, p, addr, port);
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
static void
|
||||
send_ack(u16_t blknum)
|
||||
{
|
||||
struct pbuf *p;
|
||||
u16_t *payload;
|
||||
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM);
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
payload = (u16_t *) p->payload;
|
||||
|
||||
payload[0] = PP_HTONS(TFTP_ACK);
|
||||
payload[1] = lwip_htons(blknum);
|
||||
udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
static void
|
||||
resend_data(void)
|
||||
{
|
||||
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
|
||||
udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
static void
|
||||
send_data(void)
|
||||
{
|
||||
u16_t *payload;
|
||||
int ret;
|
||||
|
||||
if (tftp_state.last_data != NULL) {
|
||||
pbuf_free(tftp_state.last_data);
|
||||
}
|
||||
|
||||
tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
|
||||
if (tftp_state.last_data == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
payload = (u16_t *) tftp_state.last_data->payload;
|
||||
payload[0] = PP_HTONS(TFTP_DATA);
|
||||
payload[1] = lwip_htons(tftp_state.blknum);
|
||||
|
||||
ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
|
||||
if (ret < 0) {
|
||||
send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
|
||||
close_handle();
|
||||
return;
|
||||
}
|
||||
|
||||
pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
|
||||
resend_data();
|
||||
}
|
||||
|
||||
static void
|
||||
recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
|
||||
{
|
||||
u16_t *sbuf = (u16_t *) p->payload;
|
||||
int opcode;
|
||||
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
LWIP_UNUSED_ARG(upcb);
|
||||
|
||||
if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
|
||||
(!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
|
||||
opcode = sbuf[0];
|
||||
|
||||
tftp_state.last_pkt = tftp_state.timer;
|
||||
tftp_state.retries = 0;
|
||||
|
||||
switch (opcode) {
|
||||
case PP_HTONS(TFTP_RRQ): /* fall through */
|
||||
case PP_HTONS(TFTP_WRQ): {
|
||||
const char tftp_null = 0;
|
||||
char filename[TFTP_MAX_FILENAME_LEN + 1];
|
||||
char mode[TFTP_MAX_MODE_LEN + 1];
|
||||
u16_t filename_end_offset;
|
||||
u16_t mode_end_offset;
|
||||
|
||||
if (tftp_state.handle != NULL) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
|
||||
break;
|
||||
}
|
||||
|
||||
sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
|
||||
|
||||
/* find \0 in pbuf -> end of filename string */
|
||||
filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
|
||||
if ((u16_t)(filename_end_offset - 1) > sizeof(filename)) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
|
||||
break;
|
||||
}
|
||||
pbuf_copy_partial(p, filename, filename_end_offset - 1, 2);
|
||||
|
||||
/* find \0 in pbuf -> end of mode string */
|
||||
mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset + 1);
|
||||
if ((u16_t)(mode_end_offset - filename_end_offset) > sizeof(mode)) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
|
||||
break;
|
||||
}
|
||||
pbuf_copy_partial(p, mode, mode_end_offset - filename_end_offset, filename_end_offset + 1);
|
||||
|
||||
tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
|
||||
tftp_state.blknum = 1;
|
||||
|
||||
if (!tftp_state.handle) {
|
||||
send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
|
||||
break;
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
|
||||
ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
|
||||
|
||||
ip_addr_copy(tftp_state.addr, *addr);
|
||||
tftp_state.port = port;
|
||||
|
||||
if (opcode == PP_HTONS(TFTP_WRQ)) {
|
||||
tftp_state.mode_write = 1;
|
||||
send_ack(0);
|
||||
} else {
|
||||
tftp_state.mode_write = 0;
|
||||
send_data();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PP_HTONS(TFTP_DATA): {
|
||||
int ret;
|
||||
u16_t blknum;
|
||||
|
||||
if (tftp_state.handle == NULL) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tftp_state.mode_write != 1) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
|
||||
break;
|
||||
}
|
||||
|
||||
blknum = lwip_ntohs(sbuf[1]);
|
||||
if (blknum == tftp_state.blknum) {
|
||||
pbuf_remove_header(p, TFTP_HEADER_LENGTH);
|
||||
|
||||
ret = tftp_state.ctx->write(tftp_state.handle, p);
|
||||
if (ret < 0) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
|
||||
close_handle();
|
||||
} else {
|
||||
send_ack(blknum);
|
||||
}
|
||||
|
||||
if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
|
||||
close_handle();
|
||||
} else {
|
||||
tftp_state.blknum++;
|
||||
}
|
||||
} else if ((u16_t)(blknum + 1) == tftp_state.blknum) {
|
||||
/* retransmit of previous block, ack again (casting to u16_t to care for overflow) */
|
||||
send_ack(blknum);
|
||||
} else {
|
||||
send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PP_HTONS(TFTP_ACK): {
|
||||
u16_t blknum;
|
||||
int lastpkt;
|
||||
|
||||
if (tftp_state.handle == NULL) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tftp_state.mode_write != 0) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
|
||||
break;
|
||||
}
|
||||
|
||||
blknum = lwip_ntohs(sbuf[1]);
|
||||
if (blknum != tftp_state.blknum) {
|
||||
send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
|
||||
break;
|
||||
}
|
||||
|
||||
lastpkt = 0;
|
||||
|
||||
if (tftp_state.last_data != NULL) {
|
||||
lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
|
||||
}
|
||||
|
||||
if (!lastpkt) {
|
||||
tftp_state.blknum++;
|
||||
send_data();
|
||||
} else {
|
||||
close_handle();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
|
||||
break;
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
static void
|
||||
tftp_tmr(void *arg)
|
||||
{
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
tftp_state.timer++;
|
||||
|
||||
if (tftp_state.handle == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
|
||||
|
||||
if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
|
||||
if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
|
||||
resend_data();
|
||||
tftp_state.retries++;
|
||||
} else {
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
|
||||
close_handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @ingroup tftp
|
||||
* Initialize TFTP server.
|
||||
* @param ctx TFTP callback struct
|
||||
*/
|
||||
err_t
|
||||
tftp_init(const struct tftp_context *ctx)
|
||||
{
|
||||
err_t ret;
|
||||
|
||||
/* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
|
||||
struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
|
||||
if (pcb == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
|
||||
if (ret != ERR_OK) {
|
||||
udp_remove(pcb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tftp_state.handle = NULL;
|
||||
tftp_state.port = 0;
|
||||
tftp_state.ctx = ctx;
|
||||
tftp_state.timer = 0;
|
||||
tftp_state.last_data = NULL;
|
||||
tftp_state.upcb = pcb;
|
||||
|
||||
udp_recv(pcb, recv, NULL);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** @ingroup tftp
|
||||
* Deinitialize ("turn off") TFTP server.
|
||||
*/
|
||||
void tftp_cleanup(void)
|
||||
{
|
||||
LWIP_ASSERT("Cleanup called on non-initialized TFTP", tftp_state.upcb != NULL);
|
||||
udp_remove(tftp_state.upcb);
|
||||
close_handle();
|
||||
memset(&tftp_state, 0, sizeof(tftp_state));
|
||||
}
|
||||
|
||||
#endif /* LWIP_UDP */
|
||||
@@ -33,7 +33,7 @@ LTDC.WindowX1_L1=480
|
||||
LTDC.WindowY1_L0=272
|
||||
LTDC.WindowY1_L1=272
|
||||
LWIP.BSP.number=1
|
||||
LWIP.IPParameters=LWIP_DHCP,IP_ADDRESS,NETMASK_ADDRESS,LWIP_HTTPD,LWIP_HTTPD_CUSTOM_FILES,LWIP_HTTPD_CGI_SSI,LWIP_HTTPD_SSI,LWIP_HTTPD_SSI_RAW,LWIP_DNS,MEMP_MEM_MALLOC,LWIP_IGMP,LWIP_HTTPD_CGI,LWIP_HTTPD_SUPPORT_POST
|
||||
LWIP.IPParameters=LWIP_DHCP,IP_ADDRESS,NETMASK_ADDRESS,LWIP_HTTPD,LWIP_HTTPD_CUSTOM_FILES,LWIP_HTTPD_CGI_SSI,LWIP_HTTPD_SSI,LWIP_HTTPD_SSI_RAW,LWIP_DNS,MEMP_MEM_MALLOC,LWIP_IGMP,LWIP_HTTPD_CGI,LWIP_HTTPD_SUPPORT_POST,MEM_LIBC_MALLOC,LWIP_TFTP
|
||||
LWIP.IP_ADDRESS=192.168.069.010
|
||||
LWIP.LWIP_DHCP=0
|
||||
LWIP.LWIP_DNS=1
|
||||
@@ -45,7 +45,9 @@ LWIP.LWIP_HTTPD_SSI=1
|
||||
LWIP.LWIP_HTTPD_SSI_RAW=1
|
||||
LWIP.LWIP_HTTPD_SUPPORT_POST=1
|
||||
LWIP.LWIP_IGMP=1
|
||||
LWIP.LWIP_TFTP=1
|
||||
LWIP.MEMP_MEM_MALLOC=1
|
||||
LWIP.MEM_LIBC_MALLOC=1
|
||||
LWIP.NETMASK_ADDRESS=255.255.255.000
|
||||
LWIP.Version=v2.1.2_Cube
|
||||
LWIP0.BSP.STBoard=false
|
||||
|
||||
Reference in New Issue
Block a user