From 6545ef3546bb8e898ba31e5c11893c462560dad2 Mon Sep 17 00:00:00 2001 From: "Thomas \"Cakeisalie5\" Touhey" Date: Thu, 22 Dec 2016 17:38:55 +0100 Subject: [PATCH] Bodged p7os in. --- .gitmodules | 3 + Makefile | 19 ++++- Makefile.vars | 9 ++- doc/p7os.1.txt | 53 ++++++++++++++ src/p7os/args.c | 175 ++++++++++++++++++++++++++++++++++++++++++++ src/p7os/main.c | 112 ++++++++++++++++++++++++++++ src/p7os/main.h | 54 ++++++++++++++ src/p7os/stuff.c | 30 ++++++++ src/p7os/update.exe | 1 + 9 files changed, 452 insertions(+), 4 deletions(-) create mode 100644 .gitmodules create mode 100644 doc/p7os.1.txt create mode 100644 src/p7os/args.c create mode 100644 src/p7os/main.c create mode 100644 src/p7os/main.h create mode 100644 src/p7os/stuff.c create mode 160000 src/p7os/update.exe diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..419b9f0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/p7os/update.exe"] + path = src/p7os/update.exe + url = https://forge.touhey.fr/casio/update.exe diff --git a/Makefile b/Makefile index ccceaff..bb39b27 100755 --- a/Makefile +++ b/Makefile @@ -84,13 +84,26 @@ reinstall: uninstall install $(BINARIES:%=$(OBJDIR)/%): $(call bcmd,mkdir,$@,$(MD) $@) -# Make an object out of a C source file. +# Make an object out of a source file/directory. define make-binaryobj-rule - $(OBJDIR)/$1/%.o: $(SRCDIR)/$1/%.c | $(OBJDIR)/$1 +ifeq ($(shell test -f $(SRCDIR)/$1/$2.c && echo y),y) +# - Out of a C source file + $(OBJDIR)/$1/$2.o: $(SRCDIR)/$1/$2.c | $(OBJDIR)/$1 $(call bcmd,cc,$$@,$(CC) -c -o $$@ $$< $(CFLAGS)) +else +# - Out of an update.exe project + $(SRCDIR)/$1/$2.exe/$2.exe.bin:| $(SRCDIR)/$1/$2.exe + $(call qcmd,cd $$| && ./configure) + $(call bcmd,make,$2.exe,$(MAKE) $$| $2.exe.bin | sed -e 's/^/ /') + + $(OBJDIR)/$1/$2.o: $(SRCDIR)/$1/$2.exe/$2.exe.bin | $(OBJDIR)/$1 + $(call bcmd,ld -r,$$@,cd $(SRCDIR)/$1/$2.exe && \ + $(LDR) -o ../../../$$@ -b binary $2.exe.bin) +endif endef $(foreach bin,$(BINARIES),\ -$(eval $(call make-binaryobj-rule,$(bin)))) +$(foreach obj,$(SRC_$(bin)),\ +$(eval $(call make-binaryobj-rule,$(bin),$(obj))))) # Make a binary define make-binary-rule diff --git a/Makefile.vars b/Makefile.vars index f036253..562219f 100755 --- a/Makefile.vars +++ b/Makefile.vars @@ -76,6 +76,11 @@ LDFLAGS := $(shell $(PKGCONFIG) --libs $(LIBS) 2>/dev/null) \ $(if $(FOR_WINDOWS),,$(LDFLAGS_Linux)) +# Raw ELF object maker + LDR := $(TARGET)ld -r + +# Maker + MAKE := make -C # Directory maker MD := mkdir -p # File remover @@ -97,7 +102,9 @@ # Look for their sources define get-binary-sources SRC_$1 := $(basename $(shell find $(SRCDIR)/$1 \ - -maxdepth 1 -mindepth 1 -type f -name "*.c" -printf "%P\n" | sort)) + -maxdepth 1 -mindepth 1 \ + \( -name "*.c" -or -name "*.exe" \) \ + -printf "%P\n" | sort)) endef $(foreach bin,$(BINARIES), \ $(eval $(call get-binary-sources,$(bin)))) diff --git a/doc/p7os.1.txt b/doc/p7os.1.txt new file mode 100644 index 0000000..2fdd35d --- /dev/null +++ b/doc/p7os.1.txt @@ -0,0 +1,53 @@ +P7OS(1) +======= +Thomas "Cakeisalie5" Touhey +:Email: thomas@touhey.fr +:man source: p7os +:man manual: p7os manual + +NAME +---- +p7os - get and change CASIO calculator's OSes using protocol 7 + +SYNOPSIS +-------- +[source,bash] +---- +p7os [-h|--help] [-v|--version] [ []] +---- + +DESCRIPTION +----------- +p7os is a command-line utility to get and upload an OS on CASIO fx calculators, +using its communication protocol 7.00 and OS update mode. + +Available submenus are : + +*prepare*:: + This subcommand must be run before *any* other subcommand. It will upload + a P7 server that supports the commands required by them. +*get [-o os.bin]*:: + Get OS image copy. + +You have to *prepare* before doing any other action. Preparing means sending +a P7 server on the calculator that will be able to execute commands required +by other subcommands. + +OPTIONS +------- +Options start with one or two dashes. Some of the options require an additional +value next to them. + +*-h, --help*:: + Display command/subcommand help page and quit. +*-v, --version*:: + Display version and quit. +*--device /dev/mydevice0*:: + The device with which to interact. + Mostly */dev/cfx0* for USB and */dev/ttyS0* for serial connections. +*-o OUT, --output=OUT*:: + When getting something, where to store. + +SEE ALSO +-------- +*libp7*(3) diff --git a/src/p7os/args.c b/src/p7os/args.c new file mode 100644 index 0000000..cd9fe62 --- /dev/null +++ b/src/p7os/args.c @@ -0,0 +1,175 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* args.c |_ _|__ _ _| |__ ___ _ _ */ +/* | Project: p7os | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/09/28 06:00:10 |___/ */ +/* */ +/* ************************************************************************** */ +#include "main.h" +#include +#include +#include +#include + +/* ************************************************************************** */ +/* Help and version messages */ +/* ************************************************************************** */ +/* The version message - that's when the President comes in */ +const char version_message[] = +"P7OS v" QUOTE(VERSION) " (licensed under " QUOTE(LICENSE) ")\n" +"Made by " QUOTE(AUTHOR) " <" QUOTE(AUTHOR_MAIL) ">.\n" +"\n" +"This is free software; see the source for copying conditions.\n" +"There is NO warranty; not even for MERCHANTABILITY or\n" +"FITNESS FOR A PARTICULAR PURPOSE."; + +/* Main help message */ +const char help_main[] = +"Usage: p7os [--version|-v] [--help|-h] [--device /dev/omg0]\n" +" [options...]\n" +"\n" +"Subcommands you can use are :\n" +" prepare Prepare server on distant machine\n" +" get Get the OS image\n" +"\n" +"All subcommands that aren't \"prepare\" require \"prepare\" to be run\n" +"before them.\n" +"\n" +"General options:\n" +" -h, --help Display the help page of the (sub)command and quit.\n" +" -v, --version Display the version message and quit.\n" +" --device dev The calculator device (usually /dev/ttyUSBx).\n" +" By default, will use the first appropriate device found.\n" +"\n" +"Type \"p7os --help\" for some help about a subcommand.\n" +"Report bugs to " QUOTE(AUTHOR) " <" QUOTE(AUTHOR_MAIL) ">."; + +/* Help message for prepare subcommand */ +const char help_prepare[] = +"Usage: p7os prepare\n" +"Send the P7 server on the calculator for further operations.\n" +"This must be used before any other p7os operation.\n" +"\n" +"Type \"p7os --help\" for other subcommands and general options."; + +/* Help message for get subcommand */ +const char help_get[] = +"Usage: p7os get [-o ]\n" +"Get the calculator OS image.\n" +"You must have \"p7os prepare\"-ed before.\n" +"\n" +"Options are :\n" +" -o Where to store the image (default is \"os.bin\")\n" +"\n" +"Type \"p7os --help\" for other subcommands and general options."; + +/* ************************************************************************** */ +/* Main function */ +/* ************************************************************************** */ +/* Help macro */ +#define ragequit(ARGS, N) { \ + if (help || aac != (ARGS)) { \ + puts(help_##N); \ + return (1); \ + } \ +} + +/** + * parse_args: + * Args parsing main function. + * + * Based on my very first experiment with getopt. + * + * @arg ac the arguments count + * @arg av the arguments values + * @arg args the parsed args pointer + * @return if has been parsed successfully + */ + +int parse_args(int ac, char **av, args_t *args) +{ + /* initialize args */ + *args = (args_t){ + .device = NULL, + .local = NULL, .localpath = NULL + }; + + /* define options */ + const char short_options[] = "hvo:"; + const struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {"device", required_argument, NULL, 'p'}, + {"output", required_argument, NULL, 'o'}, + {NULL, 0, NULL, 0} + }; + + /* get all options */ + int c; opterr = 0; + int help = 0, version = 0; + const char *s_out = "os.bin"; + while ((c = getopt_long(ac, av, short_options, long_options, NULL)) != -1) { + switch (c) { + /* help */ + case 'h': help = 1; break; + /* version */ + case 'v': version = 1; break; + /* device */ + case 'p': args->device = optarg; break; + /* output */ + case 'o': s_out = optarg; break; + + /* error */ + case '?': + if (optopt == 'o') + log("-o, --output: expected an argument\n"); + else if (optopt == 'p') + log("--device: expected an argument\n"); + else + break; + return (1); + } + } + + /* check for version */ + if (version) { + puts(version_message); + return (1); + } + + /* get non-option arguments (subcommand and parameters) */ + int aac = ac - optind; + char **aav = &av[optind]; + char *sub = aac ? aav[0] : NULL; + aac--; aav++; + + /* subcommand. */ + char fpmode[2] = "r"; + if (!sub || !strcmp(sub, "help")) { + puts(help_main); + return (1); + } else if (!strcmp(sub, "prepare")) { + ragequit(0, prepare) + } else if (!strcmp(sub, "get")) { + ragequit(0, get) + args->localpath = s_out; + fpmode[0] = 'w'; + } else { + log("Unknown subcommand '%s'.\n", aav[0]); + return (1); + } + + /* open destination file */ + if (args->localpath) { + args->local = fopen(args->localpath, fpmode); + if (!args->local) { + log("Could not open local file: '%s'\n", strerror(errno)); + return (1); + } + } + + /* everything went well :) */ + return (0); +} diff --git a/src/p7os/main.c b/src/p7os/main.c new file mode 100644 index 0000000..bf1361c --- /dev/null +++ b/src/p7os/main.c @@ -0,0 +1,112 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* main.c |_ _|__ _ _| |__ ___ _ _ */ +/* | Project: p7os | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/09/28 06:00:10 |___/ */ +/* */ +/* ************************************************************************** */ +#include "main.h" +#include + +/* ************************************************************************** */ +/* Error messages */ +/* ************************************************************************** */ +/* Couldn't initialize connexion to calculator. */ +const char error_noconnexion[] = +"Could not connect to the calculator.\n" +"- Is it plugged in and in Receive mode/OS Update?\n" +"- Have you tried changing the cable ?\n"; + +/* Calculator was found but program wasn't allowed to communicate with it. */ +const char error_noaccess[] = +"Could not get access to the calculator.\n" +"Install the appropriate udev rule, or run as root.\n"; + +/* The calculator acted in a weird way. */ +const char error_unplanned[] = +"The calculator didn't act as planned: %s.\n" +"Stop receive mode on calculator and start it again before re-running p7os.\n"; + +/* Unsupported operation -> OS Update, not receive mode! */ +const char error_unsupported[] = +"Required operation was unsupported.\n" +"- If you were using anything else than the *prepare* subcommand, are you\n" +" sure you've run *prepare* before?\n" +"- Otherwise, I gotta be honest, I'd like to know how you've managed that.\n"; + +/* ************************************************************************** */ +/* Main function */ +/* ************************************************************************** */ +/** + * main: + * User entry point of the program. + * + * @arg ac arguments count + * @arg av arguments values + * @return return code (0 if OK) + */ + +int main(int ac, char **av) +{ + /* parse args */ + args_t args; + if (parse_args(ac, av, &args)) + return (0); + + /* Initialize libp7 and communication */ + p7_handle_t *handle; int err; + if (args.device) err = p7_cinit(&handle, 1, 1, args.device, 0); + else err = p7_init(&handle, 1, 1); + if (err) { + /* displaying error */ + switch (err) { + case p7_error_nocalc: log(error_noconnexion); + case p7_error_noaccess: log(error_noaccess); + case p7_error_unsupported: log(error_unsupported); + default: log(error_unplanned, p7_strerror(err)); + } + + /* closing, removing if necessary */ + if (args.localpath) { + fclose(args.local); + remove(args.localpath); + } + return (1); + } + + /* check according to menu */ + FILE *update_exe; + switch (args.menu) { + /* prepare menu */ + case mn_prepare:; + /* open memory stream for the update.exe */ + size_t uexe_size = (size_t)&update_exe_end - (size_t)&update_exe_str; + update_exe = fmemopen(update_exe_str, uexe_size, "r"); + if (!update_exe) break; + + /* send the update.exe */ + if ((err = p7_sendexe_stream(handle, update_exe, (p7uint_t)uexe_size, + 0x88030000, 0x88030000))) + fprintf(stderr, "An error has occurred: %s\n", p7_strerror(err)); + break; + + /* get menu */ + case mn_get: + /* get the os */ + err = !get_os(handle, args.local); + fclose(args.local); + /* if there was an error, delete created file */ + if (err) unlink(args.localpath); + break; + } + + /* exit libp7 + * due to the nature of command 0x56, + * no need to terminate communication! */ + p7_exit(handle, 0); + + /* then we're good */ + return (0); +} diff --git a/src/p7os/main.h b/src/p7os/main.h new file mode 100644 index 0000000..10cefe8 --- /dev/null +++ b/src/p7os/main.h @@ -0,0 +1,54 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* main.h |_ _|__ _ _| |__ ___ _ _ */ +/* | Project: p7os | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/09/28 06:00:10 |___/ */ +/* */ +/* ************************************************************************** */ +#ifndef MAIN_H +# define MAIN_H +# include +# include +# define Q(x) #x +# define QUOTE(x) Q(x) +# define log(S, ...) fprintf(stderr, S, ##__VA_ARGS__) + +/* ************************************************************************** */ +/* Embedded update.exe */ +/* ************************************************************************** */ +#define update_exe_str (_binary_update_exe_bin_start) +#define update_exe_end (_binary_update_exe_bin_end) +extern char _binary_update_exe_bin_start[]; +extern char _binary_update_exe_bin_end[]; + +/* ************************************************************************** */ +/* CLI options */ +/* ************************************************************************** */ +/* Menu */ +typedef enum { + mn_prepare, + mn_get +} menu_t; + +/* Arguments */ +typedef struct { + /* basic things */ + menu_t menu; + + /* others */ + char *device; + FILE *local; const char *localpath; +} args_t; + +/* Parsing function */ +int parse_args(int ac, char **av, args_t *args); + +/* ************************************************************************** */ +/* Actual things */ +/* ************************************************************************** */ +/* Main functions */ +int get_os(p7_handle_t *handle, FILE *dest); + +#endif /* MAIN_H */ diff --git a/src/p7os/stuff.c b/src/p7os/stuff.c new file mode 100644 index 0000000..0f8aafb --- /dev/null +++ b/src/p7os/stuff.c @@ -0,0 +1,30 @@ +/* ************************************************************************** */ +/* _____ _ */ +/* stuff.c |_ _|__ _ _| |__ ___ _ _ */ +/* | Project: p7os | |/ _ \| | | | '_ \ / _ \ | | | */ +/* | | (_) | |_| | | | | __/ |_| | */ +/* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ +/* Last updated: 2016/09/28 06:00:10 |___/ */ +/* */ +/* ************************************************************************** */ +#include "main.h" + +/** + * get_os: + * Gather the OS. + * + * @arg handle the libp7 handle + * @arg dest the destination file + * @return if it worked + */ + +int get_os(p7_handle_t *handle, FILE *dest) +{ + /* TODO */ + fprintf(stderr, "Unimplemented yet.\n"); + (void)handle; + (void)dest; + + /* then nothing */ + return (0); +} diff --git a/src/p7os/update.exe b/src/p7os/update.exe new file mode 160000 index 0000000..787d8bb --- /dev/null +++ b/src/p7os/update.exe @@ -0,0 +1 @@ +Subproject commit 787d8bb1083c7608bd62ff06a73b441e920c6105