1
0
Fork 0

littlefs-do binary to work with spi raw file (#52)

Add helper to modify spi raw file, to make experimenting with it easier.

```sh
$ ./littlefs-do --help
Usage: ./littlefs-do <command> [options]
Commands:
  -h, --help           show this help message for the selected command and exit
  -v, --verbose        print status messages to the console
  stat                 show information of specified file or directory
  ls                   list available files in 'spiNorFlash.raw' file
  mkdir                create directory
  rmdir                remove directory
  rm                   remove directory or file
  cp                   copy files into or out of flash file
  settings             list settings from 'settings.h'
```

In the process restructure the CMake file for less duplicate
includes/defines for both executables (`infinisim` and `littlefs-do`).

Upload the `littlefs-do` binary built by the CI additionally to the `infinisim` binary.
Use the updated upload-artifact@v3 template to do that.
This commit is contained in:
NeroBurner 2022-08-29 12:05:21 +02:00 committed by GitHub
parent 741db4ea32
commit 9e15182af2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 774 additions and 56 deletions

View file

@ -64,7 +64,13 @@ jobs:
cmake --build build_lv_sim cmake --build build_lv_sim
- name: Upload simulator executable - name: Upload simulator executable
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: infinisim name: infinisim
path: build_lv_sim/infinisim path: build_lv_sim/infinisim
- name: Upload littlefs-do executable
uses: actions/upload-artifact@v3
with:
name: littlefs-do
path: build_lv_sim/littlefs-do

View file

@ -50,22 +50,64 @@ configure_file(
"${CMAKE_CURRENT_BINARY_DIR}/lv_conf.h" "${CMAKE_CURRENT_BINARY_DIR}/lv_conf.h"
COPYONLY) COPYONLY)
file(GLOB_RECURSE INCLUDES "lv_drivers/*.h" "${InfiniTime_DIR}/src/libs/lvgl/src/*.h" "./*.h" ) file(GLOB_RECURSE LVGL_INCLUDES "lv_drivers/*.h" "${InfiniTime_DIR}/src/libs/lvgl/src/*.h" "./*.h" )
file(GLOB_RECURSE SOURCES "lv_drivers/*.c" "${InfiniTime_DIR}/src/libs/lvgl/src/*.c" ) file(GLOB_RECURSE LVGL_SOURCES "lv_drivers/*.c" "${InfiniTime_DIR}/src/libs/lvgl/src/*.c" )
add_library(sim-base STATIC
# LVGL sources
${LVGL_SOURCES} ${LVGL_INCLUDES}
# FreeRTOS
sim/FreeRTOS.h
sim/FreeRTOS.cpp
sim/task.h
sim/task.cpp
sim/timers.h
sim/timers.cpp
sim/queue.h
sim/queue.cpp
# src/FreeRTOS
sim/portmacro_cmsis.h
sim/portmacro_cmsis.cpp
# nrf
sim/libraries/log/nrf_log.h
sim/libraries/delay/nrf_delay.h
sim/libraries/delay/nrf_delay.cpp
sim/nrfx/nrfx_log.h
sim/nrfx/drivers/include/nrfx_twi.h
sim/nrfx/hal/nrf_gpio.h
sim/nrfx/hal/nrf_gpio.cpp
sim/nrfx/hal/nrfx_gpiote.h # includes hal/nrf_gpio.h
sim/nrfx/hal/nrf_rtc.h
sim/nrfx/hal/nrf_rtc.cpp
# nrf/components/libraries/timer
sim/libraries/timer/app_timer.h
sim/libraries/timer/app_timer.cpp
sim/libraries/gpiote/app_gpiote.h # includes hal/nrf_gpio.h
)
# include the generated lv_conf.h file before anything else
target_include_directories(sim-base PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") # lv_conf.h
target_include_directories(sim-base PUBLIC "sim")
target_include_directories(sim-base PUBLIC "sim/libraries/log") # for nrf_log.h
target_include_directories(sim-base PUBLIC "sim/libraries/timer") # for app_timer.h
target_include_directories(sim-base PUBLIC "sim/nrfx") # for nrfx_log.h and others
target_include_directories(sim-base PUBLIC "sim/nrfx/hal") # for nrfx_log.h
target_include_directories(sim-base PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") # lv_drv_conf.h
target_include_directories(sim-base PUBLIC "${InfiniTime_DIR}/src/libs")
target_include_directories(sim-base PUBLIC "lv_drivers")
target_include_directories(sim-base PUBLIC "${InfiniTime_DIR}/src") # InfiniTime drivers, components and all
add_executable(infinisim main.cpp ${SOURCES} ${INCLUDES})
set(MONITOR_ZOOM 1 CACHE STRING "Scale simulator window by this factor") set(MONITOR_ZOOM 1 CACHE STRING "Scale simulator window by this factor")
if(MONITOR_ZOOM MATCHES "^[0-9]\.?[0-9]*") if(MONITOR_ZOOM MATCHES "^[0-9]\.?[0-9]*")
message(STATUS "Using MONITOR_ZOOM=${MONITOR_ZOOM}") message(STATUS "Using MONITOR_ZOOM=${MONITOR_ZOOM}")
target_compile_definitions(infinisim PRIVATE MONITOR_ZOOM=${MONITOR_ZOOM}) target_compile_definitions(sim-base PUBLIC MONITOR_ZOOM=${MONITOR_ZOOM})
else() else()
message(FATAL_ERROR "variable MONITOR_ZOOM=${MONITOR_ZOOM} must be a positive number") message(FATAL_ERROR "variable MONITOR_ZOOM=${MONITOR_ZOOM} must be a positive number")
endif() endif()
# include the generated lv_conf.h file before anything else add_executable(infinisim main.cpp)
target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
# add simulator files # add simulator files
target_sources(infinisim PUBLIC target_sources(infinisim PUBLIC
sim/displayapp/LittleVgl.h sim/displayapp/LittleVgl.h
@ -108,45 +150,8 @@ target_sources(infinisim PUBLIC
sim/drivers/SpiNorFlash.cpp sim/drivers/SpiNorFlash.cpp
sim/heartratetask/HeartRateTask.h sim/heartratetask/HeartRateTask.h
sim/heartratetask/HeartRateTask.cpp sim/heartratetask/HeartRateTask.cpp
# FreeRTOS )
sim/FreeRTOS.h target_link_libraries(infinisim PUBLIC sim-base)
sim/FreeRTOS.cpp
sim/task.h
sim/task.cpp
sim/timers.h
sim/timers.cpp
sim/queue.h
sim/queue.cpp
# src/FreeRTOS
sim/portmacro_cmsis.h
sim/portmacro_cmsis.cpp
# nrf
sim/libraries/log/nrf_log.h
sim/libraries/delay/nrf_delay.h
sim/libraries/delay/nrf_delay.cpp
sim/nrfx/nrfx_log.h
sim/nrfx/drivers/include/nrfx_twi.h
sim/nrfx/hal/nrf_gpio.h
sim/nrfx/hal/nrf_gpio.cpp
sim/nrfx/hal/nrfx_gpiote.h # includes hal/nrf_gpio.h
sim/nrfx/hal/nrf_rtc.h
sim/nrfx/hal/nrf_rtc.cpp
# nrf/components/libraries/timer
sim/libraries/timer/app_timer.h
sim/libraries/timer/app_timer.cpp
sim/libraries/gpiote/app_gpiote.h # includes hal/nrf_gpio.h
)
target_include_directories(infinisim PRIVATE "sim")
target_include_directories(infinisim PRIVATE "sim/libraries/log") # for nrf_log.h
target_include_directories(infinisim PRIVATE "sim/libraries/timer") # for app_timer.h
target_include_directories(infinisim PRIVATE "sim/nrfx") # for nrfx_log.h and others
target_include_directories(infinisim PRIVATE "sim/nrfx/hal") # for nrfx_log.h
target_compile_definitions(infinisim PRIVATE LV_CONF_INCLUDE_SIMPLE)
target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src/libs")
target_include_directories(infinisim PRIVATE "lv_drivers")
# add dates library # add dates library
if(EXISTS "${InfiniTime_DIR}/src/libs/date/includes") if(EXISTS "${InfiniTime_DIR}/src/libs/date/includes")
@ -175,21 +180,12 @@ file(GLOB InfiniTime_WIDGETS
"${InfiniTime_DIR}/src/displayapp/widgets/*.cpp" "${InfiniTime_DIR}/src/displayapp/widgets/*.cpp"
"${InfiniTime_DIR}/src/displayapp/widgets/*.h" "${InfiniTime_DIR}/src/displayapp/widgets/*.h"
) )
set(LITTLEFS_SRC
${InfiniTime_DIR}/src/libs/littlefs/lfs_util.h
${InfiniTime_DIR}/src/libs/littlefs/lfs.h
${InfiniTime_DIR}/src/libs/littlefs/lfs_util.c
${InfiniTime_DIR}/src/libs/littlefs/lfs.c
)
target_sources(infinisim PUBLIC ${InfiniTime_SCREENS}) target_sources(infinisim PUBLIC ${InfiniTime_SCREENS})
target_sources(infinisim PUBLIC ${InfiniTime_FONTS}) target_sources(infinisim PUBLIC ${InfiniTime_FONTS})
target_sources(infinisim PUBLIC ${InfiniTime_ICONS}) target_sources(infinisim PUBLIC ${InfiniTime_ICONS})
target_sources(infinisim PUBLIC ${InfiniTime_WIDGETS}) target_sources(infinisim PUBLIC ${InfiniTime_WIDGETS})
target_sources(infinisim PUBLIC ${LITTLEFS_SRC})
# add files directly from InfiniTime sources # add files directly from InfiniTime sources
target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src")
target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src/libs/littlefs")
target_sources(infinisim PUBLIC target_sources(infinisim PUBLIC
${InfiniTime_DIR}/src/BootloaderVersion.h ${InfiniTime_DIR}/src/BootloaderVersion.h
${InfiniTime_DIR}/src/BootloaderVersion.cpp ${InfiniTime_DIR}/src/BootloaderVersion.cpp
@ -242,6 +238,16 @@ else()
) )
endif() endif()
# littlefs
add_library(littlefs STATIC
${InfiniTime_DIR}/src/libs/littlefs/lfs_util.h
${InfiniTime_DIR}/src/libs/littlefs/lfs.h
${InfiniTime_DIR}/src/libs/littlefs/lfs_util.c
${InfiniTime_DIR}/src/libs/littlefs/lfs.c
)
target_include_directories(littlefs PUBLIC "${InfiniTime_DIR}/src/libs/littlefs")
target_link_libraries(infinisim PRIVATE littlefs)
# QCBOR # QCBOR
add_library(QCBOR STATIC add_library(QCBOR STATIC
${InfiniTime_DIR}/src/libs/QCBOR/src/ieee754.c ${InfiniTime_DIR}/src/libs/QCBOR/src/ieee754.c
@ -305,3 +311,28 @@ endif()
target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/gif-h") target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/gif-h")
install(TARGETS infinisim DESTINATION bin) install(TARGETS infinisim DESTINATION bin)
# helper library to manipulate littlefs raw image
add_executable(littlefs-do
littlefs-do-main.cpp
# want to use FS.h
${InfiniTime_DIR}/src/components/fs/FS.h
${InfiniTime_DIR}/src/components/fs/FS.cpp
# dependencies for FS.h
sim/drivers/SpiNorFlash.h
sim/drivers/SpiNorFlash.cpp
# dependencies for SpiNorFlash.h
${InfiniTime_DIR}/src/drivers/Spi.h
${InfiniTime_DIR}/src/drivers/Spi.cpp
sim/drivers/SpiMaster.h
sim/drivers/SpiMaster.cpp
${InfiniTime_DIR}/src/components/settings/Settings.h
${InfiniTime_DIR}/src/components/settings/Settings.cpp
)
target_link_libraries(littlefs-do PUBLIC sim-base)
target_link_libraries(littlefs-do PUBLIC littlefs)
target_link_libraries(littlefs-do PRIVATE SDL2::SDL2)
target_link_libraries(littlefs-do PRIVATE infinitime_fonts)

View file

@ -115,6 +115,26 @@ Using the keyboard the following events can be triggered:
- `i` ... take screenshot - `i` ... take screenshot
- `I` ... start/stop Gif scren capture - `I` ... start/stop Gif scren capture
## Littlefs-do helper
To help working with the SPI-raw file the tool `littlefs-do` is provided.
The SPI-raw file emulates the persistent 4MB storage available over the SPI bus on the PineTime.
```sh
$ ./littlefs-do --help
Usage: ./littlefs-do <command> [options]
Commands:
-h, --help show this help message for the selected command and exit
-v, --verbose print status messages to the console
stat show information of specified file or directory
ls list available files in 'spiNorFlash.raw' file
mkdir create directory
rmdir remove directory
rm remove directory or file
cp copy files into or out of flash file
settings list settings from 'settings.h'
```
## Licenses ## Licenses
This project is released under the GNU General Public License version 3 or, at your option, any later version. This project is released under the GNU General Public License version 3 or, at your option, any later version.

661
littlefs-do-main.cpp Normal file
View file

@ -0,0 +1,661 @@
/**
* @file littlefs-do-main.cpp
*
*/
/*********************
* INCLUDES
*********************/
#define _DEFAULT_SOURCE /* needed for usleep() */
#include <stdlib.h>
#include <unistd.h>
#include <array>
#include <iostream>
#include <typeinfo>
#include <algorithm>
#include <filesystem>
#include <vector>
#include <cmath> // std::pow
#include "components/fs/FS.h"
#include "components/settings/Settings.h"
#include "drivers/SpiNorFlash.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* VARIABLES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
Pinetime::Drivers::SpiNorFlash spiNorFlash {"spiNorFlash.raw"};
Pinetime::Controllers::FS fs {spiNorFlash};
Pinetime::Controllers::Settings settingsController {fs};
const char* lfs_error_to_string(int err) {
if (err == LFS_ERR_OK) return "LFS_ERR_OK"; // No error
if (err == LFS_ERR_IO) return "LFS_ERR_IO"; // Error during device operation
if (err == LFS_ERR_CORRUPT) return "LFS_ERR_CORRUPT"; // Corrupted
if (err == LFS_ERR_NOENT) return "LFS_ERR_NOENT"; // No directory entry
if (err == LFS_ERR_EXIST) return "LFS_ERR_EXIST"; // Entry already exists
if (err == LFS_ERR_NOTDIR) return "LFS_ERR_NOTDIR"; // Entry is not a dir
if (err == LFS_ERR_ISDIR) return "LFS_ERR_ISDIR"; // Entry is a dir
if (err == LFS_ERR_NOTEMPTY) return "LFS_ERR_NOTEMPTY"; // Dir is not empty
if (err == LFS_ERR_BADF) return "LFS_ERR_BADF"; // Bad file number
if (err == LFS_ERR_FBIG) return "LFS_ERR_FBIG"; // File too large
if (err == LFS_ERR_INVAL) return "LFS_ERR_INVAL"; // Invalid parameter
if (err == LFS_ERR_NOSPC) return "LFS_ERR_NOSPC"; // No space left on device
if (err == LFS_ERR_NOMEM) return "LFS_ERR_NOMEM"; // No more memory available
if (err == LFS_ERR_NOATTR) return "LFS_ERR_NOATTR"; // No data/attr available
if (err == LFS_ERR_NAMETOOLONG) return "LFS_ERR_NAMETOOLONG"; // File name too long
return "unknown";
}
void print_help_generic(const std::string &program_name)
{
std::cout << "Usage: " << program_name << " <command> [options]" << std::endl;
std::cout << "Commands:" << std::endl;
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
std::cout << " -v, --verbose print status messages to the console" << std::endl;
std::cout << " stat show information of specified file or directory" << std::endl;
std::cout << " ls list available files in 'spiNorFlash.raw' file" << std::endl;
std::cout << " mkdir create directory" << std::endl;
std::cout << " rmdir remove directory" << std::endl;
std::cout << " rm remove directory or file" << std::endl;
std::cout << " cp copy files into or out of flash file" << std::endl;
std::cout << " settings list settings from 'settings.h'" << std::endl;
}
void print_help_stat(const std::string &program_name)
{
std::cout << "Usage: " << program_name << " stat [options] [path]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
std::cout << " path path to directory or file to work on, defaults to '/'" << std::endl;
}
void print_help_ls(const std::string &program_name)
{
std::cout << "Usage: " << program_name << " ls [options] [path]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
std::cout << " path path to directory or file to work on, defaults to '/'" << std::endl;
}
void print_help_mkdir(const std::string &program_name)
{
std::cout << "Usage: " << program_name << " mkdir [options] path" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
std::cout << " path path to directory to create" << std::endl;
}
void print_help_rmdir(const std::string &program_name)
{
std::cout << "Usage: " << program_name << " rmdir [options] path" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
std::cout << " path path to directory to remove" << std::endl;
}
void print_help_rm(const std::string &program_name)
{
std::cout << "Usage: " << program_name << " rm [options] path" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
std::cout << " path path to file or directory directory to remove" << std::endl;
}
void print_help_cp(const std::string &program_name)
{
std::cout << "Usage: " << program_name << " cp [options] source [source2 ...] destination" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
std::cout << std::endl;
std::cout << "if the destination starts with '/' it is assumed to copy sources from" << std::endl;
std::cout << "the host system into the raw image, otherwise the files are copied" << std::endl;
std::cout << "from the raw image to the host system provided directory." << std::endl;
}
void print_help_settings(const std::string &program_name)
{
std::cout << "Usage: " << program_name << " settings [options]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
}
int command_stat(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
{
if (verbose)
std::cout << "running command 'stat'" << std::endl;
// argv: littlefs-do stat [args]
std::string path = "/";
// check for help flag first
for (const std::string &arg : args)
{
if (arg == "-h" || arg == "--help")
{
print_help_stat(program_name);
return 0;
}
}
if (args.empty()) {
if (verbose) {
std::cout << "no path given, showing '/'" << std::endl;
}
} if (args.size() == 1) {
path = args.at(0);
}
lfs_info info;
int ret = fs.Stat(path.c_str(), &info);
if (ret) {
std::cout << "fs.Stat returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
if (info.type == LFS_TYPE_REG) {
std::cout << "type: REG" << std::endl;
std::cout << "size: " << info.size << std::endl;
std::cout << "name: " << std::string(info.name) << std::endl;
} else if (info.type == LFS_TYPE_DIR) {
std::cout << "type: DIR" << std::endl;
std::cout << "name: " << std::string(info.name) << std::endl;
}
else {
std::cout << "unknown type: " << info.type << std::endl;
return 1;
}
return 0;
}
int command_ls(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
{
if (verbose)
std::cout << "running command 'ls'" << std::endl;
// argv: littlefs-do ls [args]
std::string path = "/";
// check for help flag first
for (const std::string &arg : args)
{
if (arg == "-h" || arg == "--help")
{
print_help_ls(program_name);
return 0;
}
}
if (args.empty()) {
if (verbose) {
std::cout << "no path given, showing '/'" << std::endl;
}
} if (args.size() == 1) {
path = args.at(0);
}
lfs_info info;
int ret = fs.Stat(path.c_str(), &info);
if (ret) {
std::cout << "fs.Stat returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
if (info.type == LFS_TYPE_REG) {
std::cout << "type: REG" << std::endl;
std::cout << "size: " << info.size << std::endl;
std::cout << "name: " << std::string(info.name) << std::endl;
} else if (info.type == LFS_TYPE_DIR) {
std::cout << "type: DIR" << std::endl;
std::cout << "name: " << std::string(info.name) << std::endl;
lfs_dir_t lfs_dir;
ret = fs.DirOpen(path.c_str(), &lfs_dir);
if (ret) {
std::cout << "fs.DirOpen returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
ret = fs.DirRead(&lfs_dir, &info);
while (ret > 0) {
if (ret < 0) {
std::cout << "fs.DirRead returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
if (info.type == LFS_TYPE_REG) {
std::cout << "type: REG";
std::cout << " size: " << info.size;
std::cout << " name: " << std::string(info.name) << std::endl;
} else if (info.type == LFS_TYPE_DIR) {
std::cout << "type: DIR";
std::cout << " name: " << std::string(info.name) << std::endl;
} else {
std::cout << "unknown type: " << info.type << std::endl;
return 1;
}
// fill for next iteration
ret = fs.DirRead(&lfs_dir, &info);
} // end of while loop
if (ret < 0) {
std::cout << "fs.DirRead returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
} else { // endif provided path
std::cout << "unknown type: " << info.type << std::endl;
return 1;
}
return 0;
}
int command_mkdir(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
{
if (verbose) {
std::cout << "running command 'mkdir'" << std::endl;
}
// argv: littlefs-do mkdir path
// check for help flag first
for (const std::string &arg : args)
{
if (arg == "-h" || arg == "--help")
{
print_help_mkdir(program_name);
return 0;
}
}
if (args.empty()) {
std::cout << "error: no path given" << std::endl;
print_help_mkdir(program_name);
return 1;
}
for (const std::string &path : args)
{
if (verbose) {
std::cout << "mkdir: " << path << std::endl;
}
int ret = fs.DirCreate(path.c_str());
if (ret < 0) {
std::cout << "fs.DirCreate returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
}
return 0;
}
int command_rmdir(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
{
if (verbose) {
std::cout << "running command 'rmdir'" << std::endl;
}
// argv: littlefs-do rmdir path
// check for help flag first
for (const std::string &arg : args)
{
if (arg == "-h" || arg == "--help")
{
print_help_rmdir(program_name);
return 0;
}
}
if (args.empty()) {
std::cout << "error: no path given" << std::endl;
print_help_rmdir(program_name);
return 1;
}
for (const std::string &path : args)
{
if (verbose) {
std::cout << "rmdir: " << path << std::endl;
}
lfs_info info;
int ret = fs.Stat(path.c_str(), &info);
if (ret) {
std::cout << "fs.Stat returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
if (info.type == LFS_TYPE_REG) {
std::cout << "error: provided path '" << path << "is a file" << std::endl;
return 1;
}
// assume non-files are directories
ret = fs.FileDelete(path.c_str());
if (ret < 0) {
std::cout << "fs.FileDelete returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
}
return 0;
}
int command_rm(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
{
if (verbose) {
std::cout << "running command 'rm'" << std::endl;
}
// argv: littlefs-do rm path
// check for help flag first
for (const std::string &arg : args)
{
if (arg == "-h" || arg == "--help")
{
print_help_rm(program_name);
return 0;
}
}
if (args.empty()) {
std::cout << "error: no path given" << std::endl;
print_help_rm(program_name);
return 1;
}
for (const std::string &path : args)
{
if (verbose) {
std::cout << "rm: " << path << std::endl;
}
// assume non-files are directories
int ret = fs.FileDelete(path.c_str());
if (ret < 0) {
std::cout << "fs.FileDelete returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
}
return 0;
}
int command_cp(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
{
if (verbose) {
std::cout << "running 'cp'" << std::endl;
}
for (const std::string &arg : args)
{
if (arg == "-h" || arg == "--help")
{
print_help_cp(program_name);
return 0;
}
}
if (args.size() < 2) {
std::cout << "error: no destination given, need source and destination" << std::endl;
print_help_cp(program_name);
return 1;
}
const std::string &destination = args.back();
static constexpr size_t memorySize {0x400000};
std::array<uint8_t, memorySize> buffer;
if (destination[0] == '/') {
if (verbose) {
std::cout << "destination starts with '/', copying files into image" << std::endl;
}
{
lfs_info info;
int ret = fs.Stat(destination.c_str(), &info);
if (ret) {
std::cout << "fs.Stat for destination path returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
if (info.type == LFS_TYPE_REG) {
std::cout << "destination is file, assumed directory" << std::endl;
return 1;
}
}
for (size_t i=0; i<args.size()-1; i++)
{
const std::string &source = args.at(i);
if (!std::filesystem::exists(source))
{
std::cout << "error: source file not found: '" << source << "'" << std::endl;
return 1;
}
const std::string dest_path = (std::filesystem::path{destination} / std::filesystem::path{source}.filename()).generic_string();
if (verbose) {
std::cout << "copy file: " << source << " to " << dest_path << std::endl;
}
lfs_file_t file_p;
int ret = fs.FileOpen(&file_p, dest_path.c_str(), LFS_O_WRONLY | LFS_O_CREAT);
if (ret) {
std::cout << "fs.FileOpen returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
const size_t f_size = std::filesystem::file_size(source);
std::ifstream ifs(source, std::ios::binary);
ifs.read((char*)(buffer.data()), f_size);
ret = fs.FileWrite(&file_p, buffer.data(), f_size);
if (ret < 0) {
std::cout << "fs.FileWrite returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
fs.FileClose(&file_p);
return ret;
}
fs.FileClose(&file_p);
}
} // end cp from host to raw image
else
{
if (verbose) {
std::cout << "destination not starting with '/', copying files from image to host" << std::endl;
}
if (!std::filesystem::is_directory(destination))
{
std::cout << "error: destination is expected to be a directory: '" << destination << "'" << std::endl;
return 1;
}
for (size_t i=0; i<args.size()-1; i++)
{
const std::string &source = args.at(i);
const std::string dest_path = (std::filesystem::path{destination} / std::filesystem::path{source}.filename()).generic_string();
if (verbose) {
std::cout << "copy file: " << source << " to " << dest_path << std::endl;
}
lfs_info info;
int ret = fs.Stat(source.c_str(), &info);
if (ret) {
std::cout << "fs.Stat for source path returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
if (info.type == LFS_TYPE_DIR) {
std::cout << "source is directory, assumed a file" << std::endl;
return 1;
}
lfs_file_t file_p;
ret = fs.FileOpen(&file_p, source.c_str(), LFS_O_RDONLY);
if (ret) {
std::cout << "fs.FileOpen returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
return ret;
}
ret = fs.FileRead(&file_p, buffer.data(), info.size);
if (ret < 0) {
std::cout << "fs.FileRead returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
fs.FileClose(&file_p);
return ret;
}
fs.FileClose(&file_p);
std::ofstream ofs(dest_path, std::ios::binary);
ofs.write((char*)(buffer.data()), info.size);
}
} // end cp from raw image to host
return 0;
}
int command_settings(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
{
if (verbose) {
std::cout << "running 'settings'" << std::endl;
}
for (const std::string &arg : args)
{
if (arg == "-h" || arg == "--help")
{
print_help_settings(program_name);
return 0;
}
}
if (verbose) {
std::cout << "calling Settings::Init()" << std::endl;
}
settingsController.Init();
using namespace Pinetime::Controllers;
{
auto clockface = settingsController.GetClockFace();
auto clockface_str = [](auto val) {
if (val == 0) return "Digital";
if (val == 1) return "Analog";
if (val == 2) return "PineTimeStyle";
if (val == 3) return "Terminal";
return "unknown";
}(clockface);
std::cout << "ClockFace: " << static_cast<int>(clockface) << " " << clockface_str << std::endl;
}
{
auto chimes = settingsController.GetChimeOption();
auto chimes_str = [](auto val) {
if (val == Settings::ChimesOption::None) return "None";
if (val == Settings::ChimesOption::Hours) return "Hours";
if (val == Settings::ChimesOption::HalfHours) return "HalfHours";
return "unknown";
}(chimes);
std::cout << "Chimes: " << static_cast<int>(chimes) << " " << chimes_str << std::endl;
}
auto color_str = [](auto c) {
if (c == Settings::Colors::White) return "White";
if (c == Settings::Colors::Silver) return "Silver";
if (c == Settings::Colors::Gray) return "Gray";
if (c == Settings::Colors::Black) return "Black";
if (c == Settings::Colors::Red) return "Red";
if (c == Settings::Colors::Maroon) return "Maroon";
if (c == Settings::Colors::Yellow) return "Yellow";
if (c == Settings::Colors::Olive) return "Olive";
if (c == Settings::Colors::Lime) return "Lime";
if (c == Settings::Colors::Green) return "Cyan";
if (c == Settings::Colors::Teal) return "Teal";
if (c == Settings::Colors::Blue) return "Blue";
if (c == Settings::Colors::Navy) return "Navy";
if (c == Settings::Colors::Magenta) return "Magenta";
if (c == Settings::Colors::Purple) return "Purple";
if (c == Settings::Colors::Orange) return "Orange";
return "unknown";
};
std::cout << "PTSColorTime: " << color_str(settingsController.GetPTSColorTime()) << std::endl;
std::cout << "PTSColorBar: " << color_str(settingsController.GetPTSColorBar()) << std::endl;
std::cout << "PTSColorBG: " << color_str(settingsController.GetPTSColorBG()) << std::endl;
std::cout << "AppMenu: " << static_cast<int>(settingsController.GetAppMenu()) << std::endl;
std::cout << "SettingsMenu: " << static_cast<int>(settingsController.GetSettingsMenu()) << std::endl;
std::cout << "ClockType: " << (settingsController.GetClockType() == Settings::ClockType::H24 ? "H24" : "H12") << std::endl;
{
auto notif = settingsController.GetNotificationStatus();
auto notif_str = [](auto val) {
if (val == Settings::Notification::ON) return "ON";
if (val == Settings::Notification::OFF) return "OFF";
return "unknown";
}(notif);
std::cout << "NotificationStatus: " << static_cast<int>(notif) << " " << notif_str << std::endl;
}
std::cout << "ScreenTimeOut: " << settingsController.GetScreenTimeOut() << " ms" << std::endl;
std::cout << "ShakeThreshold: " << settingsController.GetShakeThreshold() << std::endl;
{
std::cout << "WakeUpModes: " << std::endl;
std::cout << "- SingleTap: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::SingleTap) ? "ON" : "OFF") << std::endl;
std::cout << "- DoubleTap: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::DoubleTap) ? "ON" : "OFF") << std::endl;
std::cout << "- RaiseWrist: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::RaiseWrist) ? "ON" : "OFF") << std::endl;
std::cout << "- Shake: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::Shake) ? "ON" : "OFF") << std::endl;
}
{
auto brightness = settingsController.GetBrightness();
auto brightness_str = [](auto val) {
if (val == BrightnessController::Levels::Off) return "Off";
if (val == BrightnessController::Levels::Low) return "Low";
if (val == BrightnessController::Levels::Medium) return "Medium";
if (val == BrightnessController::Levels::High) return "High";
return "unknown";
}(brightness);
std::cout << "Brightness: " << static_cast<int>(brightness) << " " << brightness_str << std::endl;
}
std::cout << "StepsGoal: " << settingsController.GetStepsGoal() << std::endl;
std::cout << "BleRadioEnabled: " << (settingsController.GetBleRadioEnabled() ? "true" : "false") << std::endl;
return 0;
}
int main(int argc, char **argv)
{
// parse arguments
if (argc <= 1) {
print_help_generic(argv[0]);
return 1;
}
bool verbose = false;
std::vector<std::string> args;
for (int i=1; i<argc; i++)
{
const std::string arg(argv[i]);
if (arg == "-v" || arg == "--verbose") {
verbose = true;
} else {
args.push_back(arg);
}
}
if (args.empty()) {
print_help_generic(argv[0]);
return 1;
}
if (verbose) {
std::cout << "Calling FS::Init()" << std::endl;
}
fs.Init();
const std::string command = args.front();
args.erase(args.begin()); // pop_front
if (command == "-h" || command == "--help") {
print_help_generic(argv[0]);
return 0;
} else if (command == "stat") {
return command_stat(argv[0], args, verbose);
} else if (command == "ls") {
return command_ls(argv[0], args, verbose);
} else if (command == "mkdir") {
return command_mkdir(argv[0], args, verbose);
} else if (command == "rmdir") {
return command_rmdir(argv[0], args, verbose);
} else if (command == "rm") {
return command_rm(argv[0], args, verbose);
} else if (command == "cp") {
return command_cp(argv[0], args, verbose);
} else if (command == "settings") {
return command_settings(argv[0], args, verbose);
} else
{
std::cout << "unknown argument '" << command << "'" << std::endl;
return 1;
}
return 0;
}
/**********************
* STATIC FUNCTIONS
**********************/