diff --git a/minichlink/flag.h b/minichlink/flag.h new file mode 100644 index 00000000..1aeb9c8b --- /dev/null +++ b/minichlink/flag.h @@ -0,0 +1,1010 @@ +// clang-format off +// flag.h -- v1.7.1 -- command-line flag parsing +// +// Inspired by Go's flag module: https://pkg.go.dev/flag +// +// # Macros API +// +// - FLAG_LIST_INIT_CAP - initial capacity of the Flag_List and Flag_List_Mut Dynamic Arrays. +// - FLAGS_CAP - how many flags you can define within a single context. +// - FLAG_PUSH_DASH_DASH_BACK - make flag_parse() retain "--" in the rest args +// (available via flag_rest_argc() and flag_rest_argv()). Useful when you need +// to know whether flag_parse() has stopped due to encountering "--" or due to +// encountering a non-flag. Ideally this should've been a default behavior, +// but it breaks backward compatibility. Hence it's a feature macro. +// +// TODO: make FLAG_PUSH_DASH_DASH_BACK a default behavior on a major version upgrade. +// Or maybe even better just expose some sort of a flag that tells the user whether +// the dash-dash was encountered or not. +// +// # Ignoring Flags +// +// Flag.h implements an experimental syntax for ignoring flags. Consider the following command line: +// +// ```console +// $ ./command -arg1 value1 -arg2 -arg3 value3 +// ``` +// +// It provides three arguments `-arg1 value1`, `-arg2`, and `-arg3 value3`. By putting a forward slash `/` +// after the dash `-` in front of the argument you tell flag.h to parse the argument as usual, check the +// syntax check the type, but treat it as it was never provided. +// +// ```console +// $ ./command -/arg1 value1 -arg2 -arg3 value3 +// ``` +// +// In the above example only `-arg2` and `-arg3 value3` are provided, while `-/arg1 value1` is parsed, but +// ignored. +// +// This enables you to "comment out" certain arguments so you can reenable them later as you rerun the same +// command over and over again in the terminal by pressing Up and then Enter. +#ifndef FLAG_H_ +#define FLAG_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef FLAGS_CAP +#define FLAGS_CAP 256 +#endif // FLAGS_CAP + +#ifndef FLAG_LIST_INIT_CAP +#define FLAG_LIST_INIT_CAP 8 +#endif // FLAG_LIST_INIT_CAP + +// Works with both Flag_List and Flag_List_Mut +#define flag_list_append(type, list, item) \ + do { \ + if ((list)->count >= (list)->capacity) { \ + size_t new_capacity = (list)->capacity == 0 ? FLAG_LIST_INIT_CAP : (list)->capacity*2; \ + (list)->items = (type*)realloc((list)->items, new_capacity*sizeof(*(list)->items)); \ + (list)->capacity = new_capacity; \ + } \ + \ + (list)->items[(list)->count++] = item; \ + } while(0) + +typedef struct { + const char **items; + size_t count; + size_t capacity; +} Flag_List; + +// The only reason Flag_List_Mut exists is to enable recursive usage of flag[_c]_parse(..) in a backward compatible manner. +// That is using flag[_c]_parse(..) on Flag_List_Mut-s acquired from other flag[_c]_parse(..) calls, since argv must be mutable +// to enable -flag=value syntax. +// TODO: It was a mistake to make items const in Flag_List in the first place. In the next major release get rid of Flag_List_Mut and make items mutable in Flag_List +typedef struct { + char **items; + size_t count; + size_t capacity; +} Flag_List_Mut; + +/// API that operate on implicit global flag context + +char *flag_name(void *val); + +bool *flag_bool(const char *name, bool def, const char *desc); +void flag_bool_var(bool *var, const char *name, bool def, const char *desc); + +float *flag_float(const char *name, float def, const char *desc); +void flag_float_var(float *var, const char *name, float def, const char *desc); + +double *flag_double(const char *name, double def, const char *desc); +void flag_double_var(double *var, const char *name, double def, const char *desc); + +uint64_t *flag_uint64(const char *name, uint64_t def, const char *desc); +void flag_uint64_var(uint64_t *var, const char *name, uint64_t def, const char *desc); + +size_t *flag_size(const char *name, uint64_t def, const char *desc); +void flag_size_var(size_t *var, const char *name, uint64_t def, const char *desc); + +char **flag_str(const char *name, const char *def, const char *desc); +void flag_str_var(char **var, const char *name, const char *def, const char *desc); + +Flag_List *flag_list(const char *name, const char *desc); +void flag_list_var(Flag_List *var, const char *name, const char *desc); + +Flag_List_Mut *flag_list_mut(const char *name, const char *desc); +void flag_list_mut_var(Flag_List_Mut *var, const char *name, const char *desc); + +bool flag_parse(int argc, char **argv); +int flag_rest_argc(void); +char **flag_rest_argv(void); +const char *flag_program_name(void); +void flag_print_error(FILE *stream); +void flag_print_options(FILE *stream); + +/// API that operate on a custom opaque flag context. + +// Allocate a new opaque flag context. +// +// Setting `program_name` is needed so `flag_c_parse(c, argc, argv)` does not try to consume +// extra argument from `argv` as it usually does. If that's exactly what you want just pass +// `NULL` as the `program_name` then. +// +// If specific `program_name` does not make sense in your case, you can just set it to whatever, +// since internally it's not used for anything except consuming an extra argument at the beginning +// of flag[_c]_parse(..) and giving that argument back to the user via flag[_c]_program_name(..). +// If you are designing a subcommand system where each subcommand has its own context you can set +// `program_name` to the name of the corresponding subcommand. +void *flag_c_new(const char *program_name); +void flag_c_free(void *c); +char *flag_c_name(void *c, void *val); + +bool *flag_c_bool(void *c, const char *name, bool def, const char *desc); +void flag_c_bool_var(void *c, bool *var, const char *name, bool def, const char *desc); + +float *flag_c_float(void *c, const char *name, float def, const char *desc); +void flag_c_float_var(void *c, float *var, const char *name, float def, const char *desc); + +double *flag_c_double(void *c, const char *name, double def, const char *desc); +void flag_c_double_var(void *c, double *var, const char *name, double def, const char *desc); + +uint64_t *flag_c_uint64(void *c, const char *name, uint64_t def, const char *desc); +void flag_c_uint64_var(void *c, uint64_t *var, const char *name, uint64_t def, const char *desc); + +size_t *flag_c_size(void *c, const char *name, uint64_t def, const char *desc); +void flag_c_size_var(void *c, size_t *var, const char *name, uint64_t def, const char *desc); + +char **flag_c_str(void *c, const char *name, const char *def, const char *desc); +void flag_c_str_var(void *c, char **var, const char *name, const char *def, const char *desc); + +Flag_List *flag_c_list(void *c, const char *name, const char *desc); +void flag_c_list_var(void *c, Flag_List *var, const char *name, const char *desc); + +Flag_List_Mut *flag_c_list_mut(void *c, const char *name, const char *desc); +void flag_c_list_mut_var(void *c, Flag_List_Mut *var, const char *name, const char *desc); + +bool flag_c_parse(void *c, int argc, char **argv); +int flag_c_rest_argc(void *c); +char **flag_c_rest_argv(void *c); +const char *flag_c_program_name(void *c); +void flag_c_print_error(void *c, FILE *stream); +void flag_c_print_options(void *c, FILE *stream); + +#endif // FLAG_H_ + +////////////////////////////// + +#ifdef FLAG_IMPLEMENTATION + +typedef enum { + FLAG_BOOL = 0, + FLAG_UINT64, + FLAG_DOUBLE, + FLAG_FLOAT, + FLAG_SIZE, + FLAG_STR, + FLAG_LIST, + FLAG_LIST_MUT, + COUNT_FLAG_TYPES, +} Flag_Type; + +static_assert(COUNT_FLAG_TYPES == 8, "Exhaustive Flag_Value definition"); +typedef union { + char *as_str; + uint64_t as_uint64; + double as_double; + float as_float; + bool as_bool; + size_t as_size; + Flag_List as_list; + Flag_List_Mut as_list_mut; +} Flag_Value; + +typedef enum { + FLAG_NO_ERROR = 0, + FLAG_ERROR_UNKNOWN, + FLAG_ERROR_NO_VALUE, + FLAG_ERROR_INVALID_NUMBER, + FLAG_ERROR_INTEGER_OVERFLOW, + FLAG_ERROR_FLOAT_OVERFLOW, + FLAG_ERROR_DOUBLE_OVERFLOW, + FLAG_ERROR_INVALID_SIZE_SUFFIX, + COUNT_FLAG_ERRORS, +} Flag_Error; + +typedef struct { + Flag_Type type; + char *name; + char *desc; + + Flag_Value val; + void *ref; + + Flag_Value def; +} Flag; + +typedef struct { + Flag flags[FLAGS_CAP]; + size_t flags_count; + + Flag_Error flag_error; + char *flag_error_name; + + const char *program_name; + + int rest_argc; + char **rest_argv; +} Flag_Context; + +// Forward declaration of private functions +static Flag *flag__new_flag(Flag_Context *c, Flag_Type type, const char *name, const char *desc); +static void *flag__get_ref(Flag *flag); +static bool flag__size_calculate_multiplier(char* endptr, unsigned long long int* result); + +static Flag_Context flag_global_context; + +void *flag_c_new(const char *program_name) +{ + Flag_Context *fc = (Flag_Context *)malloc(sizeof(*fc)); + memset(fc, 0, sizeof(*fc)); + fc->program_name = program_name; + return fc; +} + +void flag_c_free(void *c) +{ + free(c); +} + +static Flag *flag__new_flag(Flag_Context *c, Flag_Type type, const char *name, const char *desc) +{ + assert(c->flags_count < FLAGS_CAP); + Flag *flag = &c->flags[c->flags_count++]; + memset(flag, 0, sizeof(*flag)); + flag->type = type; + // NOTE: I won't touch them I promise Kappa + flag->name = (char*) name; + flag->desc = (char*) desc; + return flag; +} + +static void *flag__get_ref(Flag *flag) +{ + if (flag->ref) return flag->ref; + return &flag->val; +} + +char *flag_name(void *val) +{ + return flag_c_name(&flag_global_context, val); +} + +char *flag_c_name(void *c, void *val) +{ + Flag_Context *fc = (Flag_Context*)c; + + for (size_t i = 0; i < fc->flags_count; ++i) { + Flag *flag = &fc->flags[i]; + if (flag__get_ref(flag) == val) { + return flag->name; + } + } + + return NULL; +} + +bool *flag_c_bool(void *c, const char *name, bool def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_BOOL, name, desc); + flag->def.as_bool = def; + flag->val.as_bool = def; + return &flag->val.as_bool; +} + +void flag_c_bool_var(void *c, bool *var, const char *name, bool def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_BOOL, name, desc); + flag->def.as_bool = def; + flag->ref = var; + *var = def; +} + +bool *flag_bool(const char *name, bool def, const char *desc) +{ + return flag_c_bool(&flag_global_context, name, def, desc); +} + +void flag_bool_var(bool *var, const char *name, bool def, const char *desc) +{ + flag_c_bool_var(&flag_global_context, var, name, def, desc); +} + +float *flag_c_float(void *c, const char *name, float def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_FLOAT, name, desc); + flag->def.as_float = def; + flag->val.as_float = def; + return &flag->val.as_float; +} + +void flag_c_float_var(void *c, float *var, const char *name, float def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_FLOAT, name, desc); + flag->def.as_float = def; + flag->ref = var; + *var = def; +} + +double *flag_c_double(void *c, const char *name, double def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_DOUBLE, name, desc); + flag->def.as_double = def; + flag->val.as_double = def; + return &flag->val.as_double; +} + +void flag_c_double_var(void *c, double *var, const char *name, double def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_DOUBLE, name, desc); + flag->def.as_double = def; + flag->ref = var; + *var = def; +} + +float *flag_float(const char *name, float def, const char *desc) +{ + return flag_c_float(&flag_global_context, name, def, desc); +} + +void flag_float_var(float *var, const char *name, float def, const char *desc) +{ + flag_c_float_var(&flag_global_context, var, name, def, desc); +} + +double *flag_double(const char *name, double def, const char *desc) +{ + return flag_c_double(&flag_global_context, name, def, desc); +} + +void flag_double_var(double *var, const char *name, double def, const char *desc) +{ + flag_c_double_var(&flag_global_context, var, name, def, desc); +} + +uint64_t *flag_c_uint64(void *c, const char *name, uint64_t def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_UINT64, name, desc); + flag->val.as_uint64 = def; + flag->def.as_uint64 = def; + return &flag->val.as_uint64; +} + +void flag_c_uint64_var(void *c, uint64_t *var, const char *name, uint64_t def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_UINT64, name, desc); + flag->def.as_uint64 = def; + flag->ref = var; + *var = def; +} + +uint64_t *flag_uint64(const char *name, uint64_t def, const char *desc) +{ + return flag_c_uint64(&flag_global_context, name, def, desc); +} + +void flag_uint64_var(uint64_t *var, const char *name, uint64_t def, const char *desc) +{ + flag_c_uint64_var(&flag_global_context, var, name, def, desc); +} + +size_t *flag_c_size(void *c, const char *name, uint64_t def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_SIZE, name, desc); + flag->val.as_size = def; + flag->def.as_size = def; + return &flag->val.as_size; +} + +void flag_c_size_var(void *c, size_t *var, const char *name, uint64_t def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_SIZE, name, desc); + flag->ref = var; + flag->def.as_size = def; + *var = def; +} + +size_t *flag_size(const char *name, uint64_t def, const char *desc) +{ + return flag_c_size(&flag_global_context, name, def, desc); +} + +void flag_size_var(size_t *var, const char *name, uint64_t def, const char *desc) +{ + flag_c_size_var(&flag_global_context, var, name, def, desc); +} + +char **flag_c_str(void *c, const char *name, const char *def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_STR, name, desc); + flag->val.as_str = (char*) def; + flag->def.as_str = (char*) def; + return &flag->val.as_str; +} + +void flag_c_str_var(void *c, char **var, const char *name, const char *def, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_STR, name, desc); + flag->ref = var; + flag->def.as_str = (char*) def; + *var = (char*) def; +} + +char **flag_str(const char *name, const char *def, const char *desc) +{ + return flag_c_str(&flag_global_context, name, def, desc); +} + +void flag_str_var(char **var, const char *name, const char *def, const char *desc) +{ + flag_c_str_var(&flag_global_context, var, name, def, desc); +} + +Flag_List *flag_c_list(void *c, const char *name, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_LIST, name, desc); + return &flag->val.as_list; +} + +void flag_c_list_var(void *c, Flag_List *var, const char *name, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_LIST, name, desc); + flag->ref = var; +} + +Flag_List_Mut *flag_c_list_mut(void *c, const char *name, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_LIST_MUT, name, desc); + return &flag->val.as_list_mut; +} + +void flag_c_list_mut_var(void *c, Flag_List_Mut *var, const char *name, const char *desc) +{ + Flag *flag = flag__new_flag((Flag_Context*)c, FLAG_LIST_MUT, name, desc); + flag->ref = var; +} + +Flag_List *flag_list(const char *name, const char *desc) +{ + return flag_c_list(&flag_global_context, name, desc); +} + +void flag_list_var(Flag_List *var, const char *name, const char *desc) +{ + flag_c_list_var(&flag_global_context, var, name, desc); +} + +Flag_List_Mut *flag_list_mut(const char *name, const char *desc) +{ + return flag_c_list_mut(&flag_global_context, name, desc); +} + +void flag_list_mut_var(Flag_List_Mut *var, const char *name, const char *desc) +{ + flag_c_list_mut_var(&flag_global_context, var, name, desc); +} + +static char *flag_shift_args(int *argc, char ***argv) +{ + assert(*argc > 0); + char *result = **argv; + *argv += 1; + *argc -= 1; + return result; +} + +int flag_rest_argc(void) +{ + return flag_global_context.rest_argc; +} + +int flag_c_rest_argc(void *c) +{ + return ((Flag_Context*)c)->rest_argc; +} + +char **flag_rest_argv(void) +{ + return flag_global_context.rest_argv; +} + +char **flag_c_rest_argv(void *c) +{ + return ((Flag_Context*)c)->rest_argv; +} + +const char *flag_program_name(void) +{ + return flag_global_context.program_name; +} + +const char *flag_c_program_name(void *c) +{ + return ((Flag_Context*)c)->program_name; +} + +void flag_c_set_program_name(void *c, const char *program_name) +{ + ((Flag_Context *)c)->program_name = program_name; +} + +static bool flag__size_calculate_multiplier(char* endptr, unsigned long long int* result) +{ + if (strcmp(endptr, "c") == 0) { + (*result) *= 1ULL; + } else if (strcmp(endptr, "w") == 0) { + (*result) *= 2ULL; + } else if (strcmp(endptr, "b") == 0) { + (*result) *= 512ULL; + } else if (strcmp(endptr, "kB") == 0) { + (*result) *= 1000ULL; + } else if (strcmp(endptr, "K") == 0 || strcmp(endptr, "KiB") == 0) { + (*result) *= 1024ULL; + } else if (strcmp(endptr, "MB") == 0) { + (*result) *= 1000ULL * 1000ULL; + } else if (strcmp(endptr, "M") == 0 || strcmp(endptr, "MiB") == 0 || strcmp(endptr, "xM") == 0) { + (*result) *= 1024ULL * 1024ULL; + } else if (strcmp(endptr, "GB") == 0) { + (*result) *= 1000ULL * 1000ULL * 1000ULL; + } else if (strcmp(endptr, "G") == 0 || strcmp(endptr, "GiB") == 0) { + (*result) *= 1024ULL * 1024ULL * 1024ULL; + } else if (strcmp(endptr, "TB") == 0) { + (*result) *= 1000ULL * 1000ULL * 1000ULL * 1000ULL; + } else if (strcmp(endptr, "T") == 0 || strcmp(endptr, "TiB") == 0) { + (*result) *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; + } else if (strcmp(endptr, "PB") == 0) { + (*result) *= 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL; + } else if (strcmp(endptr, "P") == 0 || strcmp(endptr, "PiB") == 0) { + (*result) *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; + } else if (strcmp(endptr, "EB") == 0) { + (*result) *= 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL; + } else if (strcmp(endptr, "E") == 0 || strcmp(endptr, "EiB") == 0) { + (*result) *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; + } else if (strcmp(endptr, "ZB") == 0) { + (*result) *= 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL; + } else if (strcmp(endptr, "Z") == 0 || strcmp(endptr, "ZiB") == 0) { + (*result) *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; + } else if (strcmp(endptr, "YB") == 0) { + (*result) *= 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL; + } else if (strcmp(endptr, "Y") == 0 || strcmp(endptr, "YiB") == 0) { + (*result) *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; + } else if (strcmp(endptr, "") != 0) { + return false; + } + return true; + } + +bool flag_c_parse(void *c, int argc, char **argv) +{ + Flag_Context *fc = (Flag_Context*)c; + + if (fc->program_name == NULL) { + fc->program_name = flag_shift_args(&argc, &argv); + } + + while (argc > 0) { + char *flag = flag_shift_args(&argc, &argv); + + if (*flag != '-') { + // NOTE: pushing flag back into args + fc->rest_argc = argc + 1; + fc->rest_argv = argv - 1; + return true; + } + + if (strcmp(flag, "--") == 0) { +#ifdef FLAG_PUSH_DASH_DASH_BACK + // NOTE: pushing dash dash back into args as requested by the user + fc->rest_argc = argc + 1; + fc->rest_argv = argv - 1; +#else + // NOTE: not pushing dash dash back into args for backward compatibility + fc->rest_argc = argc; + fc->rest_argv = argv; +#endif // FLAG_PUSH_DASH_DASH_BACK + return true; + } + + // NOTE: remove the dash + flag += 1; + + bool ignore = false; + if (*flag == '/') { + ignore = true; + flag += 1; + } + + char *equals = strchr(flag, '='); + if (equals != NULL) { + // trim off the '=' and the value from `flag`, + *equals = '\0'; + // and make `equals` be a pointer to just the value + equals += 1; + } + + bool found = false; + for (size_t i = 0; i < fc->flags_count; ++i) { + if (strcmp(fc->flags[i].name, flag) == 0) { + static_assert(COUNT_FLAG_TYPES == 8, "Exhaustive flag type parsing"); + switch (fc->flags[i].type) { + case FLAG_LIST: { + char *arg; + if (equals == NULL) { + if (argc == 0) { + fc->flag_error = FLAG_ERROR_NO_VALUE; + fc->flag_error_name = flag; + return false; + } + arg = flag_shift_args(&argc, &argv); + } else { + arg = equals; + } + + if (!ignore) { + Flag_List *list = (Flag_List*)flag__get_ref(&fc->flags[i]); + flag_list_append(const char *, list, arg); + } + } + break; + + case FLAG_LIST_MUT: { + char *arg; + if (equals == NULL) { + if (argc == 0) { + fc->flag_error = FLAG_ERROR_NO_VALUE; + fc->flag_error_name = flag; + return false; + } + arg = flag_shift_args(&argc, &argv); + } else { + arg = equals; + } + + if (!ignore) { + Flag_List_Mut *list = (Flag_List_Mut*)flag__get_ref(&fc->flags[i]); + flag_list_append(char *, list, arg); + } + } + break; + + case FLAG_BOOL: { + // TODO: when the -flag= syntax is used, the boolean should probably parse values such as + // "true", "false", "on", "off", etc. But I'm not sure how backward compatible it is to + // introduce such syntax at this point... + if (!ignore) *(bool*)flag__get_ref(&fc->flags[i]) = true; + } + break; + + case FLAG_STR: { + char *arg; + if (equals == NULL) { + if (argc == 0) { + fc->flag_error = FLAG_ERROR_NO_VALUE; + fc->flag_error_name = flag; + return false; + } + arg = flag_shift_args(&argc, &argv); + } else { + arg = equals; + } + + if (!ignore) *(char**)flag__get_ref(&fc->flags[i]) = arg; + } + break; + + case FLAG_UINT64: { + char *arg; + if (equals == NULL) { + if (argc == 0) { + fc->flag_error = FLAG_ERROR_NO_VALUE; + fc->flag_error_name = flag; + return false; + } + arg = flag_shift_args(&argc, &argv); + } else { + arg = equals; + } + + static_assert(sizeof(unsigned long long int) == sizeof(uint64_t), "The original author designed this for x86_64 machine with the compiler that expects unsigned long long int and uint64_t to be the same thing, so they could use strtoull() function to parse it. Please adjust this code for your case and maybe even send the patch to upstream to make it work on a wider range of environments."); + char *endptr; + unsigned long long int result = strtoull(arg, &endptr, 10); + + if (*endptr != '\0') { + fc->flag_error = FLAG_ERROR_INVALID_NUMBER; + fc->flag_error_name = flag; + return false; + } + + if (result == ULLONG_MAX && errno == ERANGE) { + fc->flag_error = FLAG_ERROR_INTEGER_OVERFLOW; + fc->flag_error_name = flag; + return false; + } + + if (!ignore) *(uint64_t*)flag__get_ref(&fc->flags[i]) = result; + } + break; + + case FLAG_FLOAT: { + char *arg; + if (equals == NULL) { + if (argc == 0) { + fc->flag_error = FLAG_ERROR_NO_VALUE; + fc->flag_error_name = flag; + return false; + } + arg = flag_shift_args(&argc, &argv); + } else { + arg = equals; + } + char *endptr; + float result = strtof(arg, &endptr); + + if (*endptr != '\0') { + fc->flag_error = FLAG_ERROR_INVALID_NUMBER; + fc->flag_error_name = flag; + return false; + } + + if (result == FLT_MAX && errno == ERANGE) { + fc->flag_error = FLAG_ERROR_FLOAT_OVERFLOW; + fc->flag_error_name = flag; + return false; + } + + if (!ignore) *(float*)flag__get_ref(&fc->flags[i]) = result; + } + break; + + case FLAG_DOUBLE: { + char *arg; + if (equals == NULL) { + if (argc == 0) { + fc->flag_error = FLAG_ERROR_NO_VALUE; + fc->flag_error_name = flag; + return false; + } + arg = flag_shift_args(&argc, &argv); + } else { + arg = equals; + } + char *endptr; + double result = strtod(arg, &endptr); + + if (*endptr != '\0') { + fc->flag_error = FLAG_ERROR_INVALID_NUMBER; + fc->flag_error_name = flag; + return false; + } + + if (result == DBL_MAX && errno == ERANGE) { + fc->flag_error = FLAG_ERROR_DOUBLE_OVERFLOW; + fc->flag_error_name = flag; + return false; + } + + if (!ignore) *(double*)flag__get_ref(&fc->flags[i]) = result; + } + break; + + case FLAG_SIZE: { + char *arg; + if (equals == NULL) { + if (argc == 0) { + fc->flag_error = FLAG_ERROR_NO_VALUE; + fc->flag_error_name = flag; + return false; + } + arg = flag_shift_args(&argc, &argv); + } else { + arg = equals; + } + + static_assert(sizeof(unsigned long long int) == sizeof(size_t), "The original author designed this for x86_64 machine with the compiler that expects unsigned long long int and size_t to be the same thing, so they could use strtoull() function to parse it. Please adjust this code for your case and maybe even send the patch to upstream to make it work on a wider range of environments."); + char *endptr; + unsigned long long int result = strtoull(arg, &endptr, 10); + + if (!flag__size_calculate_multiplier(endptr,&result)) { + fc->flag_error = FLAG_ERROR_INVALID_SIZE_SUFFIX; + fc->flag_error_name = flag; + return false; + } + + if (result == ULLONG_MAX && errno == ERANGE) { + fc->flag_error = FLAG_ERROR_INTEGER_OVERFLOW; + fc->flag_error_name = flag; + return false; + } + + if (!ignore) *(size_t*)flag__get_ref(&fc->flags[i]) = result; + } + break; + + case COUNT_FLAG_TYPES: + default: { + assert(0 && "unreachable"); + exit(69); + } + } + + found = true; + } + } + + // NOTE: BogdanTheGeek: save rest args on error so that another parser can pick them up + if (!found) { + fc->flag_error = FLAG_ERROR_UNKNOWN; + fc->flag_error_name = flag; + fc->rest_argc = argc + 1; + fc->rest_argv = argv - 1; + return false; + } + } + + fc->rest_argc = argc; + fc->rest_argv = argv; + return true; +} + +bool flag_parse(int argc, char **argv) +{ + return flag_c_parse(&flag_global_context, argc, argv); +} + +void flag_c_print_options(void *c, FILE *stream) +{ + Flag_Context *fc = (Flag_Context *)c; + for (size_t i = 0; i < fc->flags_count; ++i) { + Flag *flag = &fc->flags[i]; + + static_assert(COUNT_FLAG_TYPES == 8, "Exhaustive flag type defaults printing"); + switch (fc->flags[i].type) { + case FLAG_LIST_MUT: + case FLAG_LIST: + fprintf(stream, " -%s ... -%s ...\n", flag->name, flag->name); + fprintf(stream, " %s\n", flag->desc); + break; + case FLAG_BOOL: + fprintf(stream, " -%s\n", flag->name); + fprintf(stream, " %s\n", flag->desc); + if (flag->def.as_bool) { + fprintf(stream, " Default: %s\n", flag->def.as_bool ? "true" : "false"); + } + break; + case FLAG_UINT64: + fprintf(stream, " -%s \n", flag->name); + fprintf(stream, " %s\n", flag->desc); + fprintf(stream, " Default: %" PRIu64 "\n", flag->def.as_uint64); + break; + case FLAG_FLOAT: + fprintf(stream, " -%s \n", flag->name); + fprintf(stream, " %s\n", flag->desc); + fprintf(stream, " Default: %f\n", flag->def.as_float); + break; + case FLAG_DOUBLE: + fprintf(stream, " -%s \n", flag->name); + fprintf(stream, " %s\n", flag->desc); + fprintf(stream, " Default: %lf\n", flag->def.as_double); + break; + case FLAG_SIZE: + fprintf(stream, " -%s \n", flag->name); + fprintf(stream, " %s\n", flag->desc); + fprintf(stream, " Default: %zu\n", flag->def.as_size); + break; + case FLAG_STR: + fprintf(stream, " -%s \n", flag->name); + fprintf(stream, " %s\n", flag->desc); + if (flag->def.as_str) { + fprintf(stream, " Default: %s\n", flag->def.as_str); + } + break; + case COUNT_FLAG_TYPES: + default: + assert(0 && "unreachable"); + exit(69); + } + } +} + +void flag_print_options(FILE *stream) +{ + flag_c_print_options(&flag_global_context, stream); +} + +void flag_c_print_error(void *c, FILE *stream) +{ + Flag_Context *fc = (Flag_Context *)c; + static_assert(COUNT_FLAG_ERRORS == 8, "Exhaustive flag error printing"); + switch (fc->flag_error) { + case FLAG_NO_ERROR: + // NOTE: don't call flag_print_error() if flag_parse() didn't return false, okay? ._. + fprintf(stream, "Operation Failed Successfully! Please tell the developer of this software that they don't know what they are doing! :)"); + break; + case FLAG_ERROR_UNKNOWN: + fprintf(stream, "ERROR: -%s: unknown flag\n", fc->flag_error_name); + break; + case FLAG_ERROR_NO_VALUE: + fprintf(stream, "ERROR: -%s: no value provided\n", fc->flag_error_name); + break; + case FLAG_ERROR_INVALID_NUMBER: + fprintf(stream, "ERROR: -%s: invalid number\n", fc->flag_error_name); + break; + case FLAG_ERROR_INTEGER_OVERFLOW: + fprintf(stream, "ERROR: -%s: integer overflow\n", fc->flag_error_name); + break; + case FLAG_ERROR_FLOAT_OVERFLOW: + fprintf(stream, "ERROR: -%s: float overflow\n", fc->flag_error_name); + break; + case FLAG_ERROR_DOUBLE_OVERFLOW: + fprintf(stream, "ERROR: -%s: double overflow\n", fc->flag_error_name); + break; + case FLAG_ERROR_INVALID_SIZE_SUFFIX: + fprintf(stream, "ERROR: -%s: invalid size suffix\n", fc->flag_error_name); + break; + case COUNT_FLAG_ERRORS: + default: + assert(0 && "unreachable"); + exit(69); + } +} + +void flag_print_error(FILE *stream) +{ + flag_c_print_error(&flag_global_context, stream); +} + +#endif // FLAG_IMPLEMENTATION + +/* + Revision history: + + 1.7.1 (2025-10-24) Remove flag_error_value from Flag_Context (@rexim) + 1.7.0 (2025-09-27) Add float and double flags (by @ByXeno) + Add more size suffixes (by @ByXeno) + 1.6.1 (2025-09-23) Remove use_ref from Flag_Context + 1.6.0 (2025-09-22) Introduce *_var variants of flag functions + 1.5.0 (2025-09-22) Introduce -/flag syntax for ignoring flags + 1.4.1 (2025-09-05) Fix -Wswitch-enum warning for GCC/Clang + 1.4.0 (2025-07-23) Add support for explicit flag contexts + Add Flag_List_Mut + Implement `-key=value` syntax for flag lists + 1.3.0 (2025-07-21) Add support for `-key=value` syntax (by @ej-shafran) + 1.2.1 (2025-07-04) flag_print_options: denote expected argument types + flag_print_options: indicate flag list usage more clearly + 1.2.0 (2025-05-31) Introduce FLAG_PUSH_DASH_DASH_BACK (by @nullnominal) + 1.1.0 (2025-05-09) Introduce flag list + 1.0.0 (2025-03-03) Initial release + Save program_name in the context + +*/ + +// Copyright 2021 Alexey Kutepov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c index a18d0364..15aa744b 100644 --- a/minichlink/minichlink.c +++ b/minichlink/minichlink.c @@ -15,6 +15,9 @@ #include "chips.h" #include "ch5xx.h" +#define FLAG_IMPLEMENTATION +#include "flag.h" + #if defined(WINDOWS) || defined(WIN32) || defined(_WIN32) extern int isatty(int); #if !defined(_SYNCHAPI_H_) && !defined(__TINYC__) @@ -117,37 +120,105 @@ void * MiniCHLinkInitAsDLL( struct MiniChlinkFunctions ** MCFO, const init_hints #if !defined( MINICHLINK_AS_LIBRARY ) && !defined( MINICHLINK_IMPORT ) int main( int argc, char ** argv ) { - int i; + bool help = false; + bool enable_3v3 = false; + bool enable_5v = false; + bool disable_3v3 = false; + bool disable_5v = false; + bool skip_init = false; + char *serial_port = NULL; + char *specific_programmer = NULL; + bool unbrick = false; + bool erase_chip = false; + bool reboot_from_halt = false; + bool reboot_into_bootloader = false; + bool resume_from_halt = false; + bool reboot_into_halt = false; + bool halt_without_reboot = false; + bool nrst_as_gpio = false; + bool nrst_as_nrst = false; + bool chip_info = false; + bool terminal = false; + bool gdbserver = false; + bool cmdserver = false; + bool enable_read_protect = false; + bool disable_read_protect = false; + bool enable_debug_module = false; + bool disable_debug_module = false; + bool unlock_bootloader = false; + + flag_bool_var( &help, "h", help, "Print this help message" ); + flag_bool_var( &help, "help", help, "Print this help message" ); + flag_bool_var( &enable_3v3, "3", enable_3v3, "Enable 3.3V power" ); + flag_bool_var( &enable_5v, "5", enable_5v, "Enable 5V power" ); + flag_bool_var( &disable_3v3, "t", disable_3v3, "Disable 3.3V power" ); + flag_bool_var( &disable_5v, "f", disable_5v, "Disable 5V power" ); + flag_bool_var( &skip_init, "k", skip_init, "Skip programmer initialization" ); + flag_str_var( &serial_port, "c", serial_port, + "[serial port for Ardulink, try /dev/ttyACM0 or COM11 etc] or [VID+PID of USB for b003boot, try 0x1209b003]" ); + flag_str_var( &specific_programmer, "C", specific_programmer, + "Specify specific programmer to use (e.g., linke, isp, esp32s2chfun, nchlink, b003boot, ardulink)" ); + flag_bool_var( &unbrick, "u", unbrick, "Clear all code flash - by power off (also can unbrick)" ); + flag_bool_var( &erase_chip, "E", erase_chip, "Erase whole chip" ); + flag_bool_var( &reboot_from_halt, "b", reboot_from_halt, "Reboot from halt" ); + flag_bool_var( &reboot_into_bootloader, "B", reboot_into_bootloader, "Reboot into bootloader" ); + flag_bool_var( &resume_from_halt, "e", resume_from_halt, "Resume from halt" ); + flag_bool_var( &reboot_into_halt, "a", reboot_into_halt, "Reboot into halt" ); + flag_bool_var( &halt_without_reboot, "A", halt_without_reboot, "Halt without reboot" ); + flag_bool_var( &nrst_as_gpio, "D", nrst_as_gpio, "Configure NRST as GPIO" ); + flag_bool_var( &nrst_as_nrst, "d", nrst_as_nrst, "Configure NRST as NRST" ); + flag_bool_var( &chip_info, "i", chip_info, "Print chip info" ); + flag_bool_var( &enable_read_protect, "P", enable_read_protect, "Enable read protection" ); + flag_bool_var( &disable_read_protect, "p", disable_read_protect, "Disable read protection" ); + flag_bool_var( &enable_debug_module, "N", enable_debug_module, "Enable debug module" ); + flag_bool_var( &disable_debug_module, "n", disable_debug_module, "Disable debug module" ); + flag_bool_var( &unlock_bootloader, "U", unlock_bootloader, "Unlock bootloader (if supported)" ); + flag_bool_var( &terminal, "T", terminal, "Terminal Only" ); + flag_bool_var( &gdbserver, "G", gdbserver, "Terminal + GDB Server" ); + flag_bool_var( &cmdserver, "cmd", cmdserver, "Command Server" ); + // TODO: -r -w -m -s -X -S + + if ( !flag_parse( argc, argv ) ) + { + // Hack + if ( flag_global_context.flag_error != FLAG_ERROR_UNKNOWN ) + { + flag_print_error( stderr ); + goto help; + } + } - if( argc > 1 && argv[1][0] == '-' && argv[1][1] == 'h' ) + // pass unparsed to old parser + argc = flag_rest_argc(); + argv = flag_rest_argv(); + + +#if 0 + printf( "Left args: %d\n", argc ); + for ( int i = 0; i < argc; i++ ) { - goto help; + printf( " Arg %d: '%s'\n", i, argv[i] ); } - init_hints_t hints; - memset(&hints, 0, sizeof(hints)); +#endif - // Scan for possible hints. - for( i = 0; i < argc; i++ ) + if ( serial_port && strncmp( serial_port, "0x", 2 ) == 0 ) { - char * v = argv[i]; - if( strncmp( v, "-c", 2 ) == 0 ) - { - i++; - if( i < argc ) - { - hints.serial_port = argv[i]; - if( strncmp( hints.serial_port, "0x", 2 ) == 0 ) - { - hints.specific_programmer = "b003boot"; - } - } - } - else if( strncmp( v, "-C", 2 ) == 0 ) - { - i++; - if( i < argc ) - hints.specific_programmer = argv[i]; - } + specific_programmer = "b003boot"; + } + + const init_hints_t hints = { + .serial_port = serial_port, + .specific_programmer = specific_programmer, + }; + + if ( gdbserver ) + { + terminal = true; + } + + if ( help ) + { + goto help; } #if !defined(WINDOWS) && !defined(WIN32) && !defined(_WIN32) && !defined(__APPLE__) @@ -181,10 +252,8 @@ int main( int argc, char ** argv ) return -32; } - int status; - int must_be_end = 0; - - int skip_startup = +#if 0 + const int skip_startup = (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'k' ) | (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'e' ) | (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A' ) | @@ -193,6 +262,15 @@ int main( int argc, char ** argv ) (argc > 1 && argv[1][0] == '-' && argv[1][1] == 't' ) | (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f' ) | (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'X' ); +#else + skip_init |= resume_from_halt; + skip_init |= halt_without_reboot; + skip_init |= unbrick; + skip_init |= disable_3v3; + skip_init |= disable_5v; + // skip_init |= -X // TODO: implement + const int skip_startup = skip_init; +#endif if( !skip_startup && MCF.SetupInterface ) { @@ -214,7 +292,416 @@ int main( int argc, char ** argv ) // PostSetupConfigureInterface( dev ); - int iarg = 1; + // TODO: this now breaks combined operations. Fix it. + // TODO: this now break ordering of operations. But maybe that's acceptable? I'm sure some commands can only be done + // before others. + if ( enable_3v3 ) + { + if ( MCF.Control3v3 ) + MCF.Control3v3( dev, 1 ); + else + goto unimplemented; + } + if ( enable_5v ) + { + if ( MCF.Control5v ) + MCF.Control5v( dev, 1 ); + else + goto unimplemented; + } + if ( disable_3v3 ) + { + if ( MCF.Control3v3 ) + MCF.Control3v3( dev, 0 ); + else + goto unimplemented; + } + if ( disable_5v ) + { + if ( MCF.Control5v ) + MCF.Control5v( dev, 0 ); + else + goto unimplemented; + } + if ( unbrick ) + { + if ( MCF.Unbrick ) + MCF.Unbrick( dev ); + else + goto unimplemented; + } + if ( chip_info ) + { + if ( MCF.PrintChipInfo ) + MCF.PrintChipInfo( dev ); + else + goto unimplemented; + } + if ( erase_chip ) + { + if ( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); + if ( !MCF.Erase || MCF.Erase( dev, 0, 0, 1 ) ) goto unimplemented; + } + if ( reboot_from_halt ) + { + if ( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_REBOOT ) ) goto unimplemented; + } + if ( reboot_into_bootloader ) + { + if ( iss->target_chip_type != CHIP_CH32V003 && iss->target_chip_type != CHIP_CH32V00x && + iss->target_chip_type != CHIP_CH641 && iss->target_chip_type != CHIP_CH643 && + iss->target_chip_type != CHIP_CH32X03x ) + DefaultRebootIntoBootloader( dev ); + else if ( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_GO_TO_BOOTLOADER ) ) + goto unimplemented; + } + if ( resume_from_halt ) + { + if ( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_RESUME ) ) goto unimplemented; + } + if ( reboot_into_halt ) + { + if ( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ) ) goto unimplemented; + } + if ( halt_without_reboot ) + { + if ( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_HALT_BUT_NO_RESET ) ) goto unimplemented; + } + if ( nrst_as_gpio ) + { + if ( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); + if ( MCF.ConfigureNRSTAsGPIO ) + MCF.ConfigureNRSTAsGPIO( dev, 0 ); + else + goto unimplemented; + } + if ( nrst_as_nrst ) + { + if ( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); + if ( MCF.ConfigureNRSTAsGPIO ) + MCF.ConfigureNRSTAsGPIO( dev, 1 ); + else + goto unimplemented; + } + if ( enable_read_protect ) + { + if ( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); + if ( MCF.ConfigureReadProtection ) + MCF.ConfigureReadProtection( dev, 1 ); + else + goto unimplemented; + } + if ( disable_read_protect ) + { + if ( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); + if ( MCF.ConfigureReadProtection ) + { + fprintf( stderr, "This will erase the flash entirely!\n" ); + fprintf( stderr, "Press Enter to proceed, Ctrl+C to abort." ); + while ( !IsKBHit() ) + ; + // TODO: revert after testing + MCF.ConfigureReadProtection( dev, 0 ); + } + else + goto unimplemented; + } + if ( enable_debug_module ) + { + if ( MCF.EnableDebug ) + { + printf( "Enabling debug\n" ); + if ( MCF.EnableDebug( dev, 0 ) ) + { + return -13; + } + } + else + goto unimplemented; + } + if ( disable_debug_module ) + { + if ( MCF.EnableDebug ) + { + fprintf( stderr, "This will disable debug module. And you will be only able to program the chip using the " + "bootloader.\nPress Enter to continue\n" ); + while ( !IsKBHit() ) + ; + ReadKBByte(); + fprintf( stderr, "Are you absolutely sure about it?\nPress Enter to continue, or Ctrl+C to cancel\n" ); + while ( !IsKBHit() ) + ; + if ( MCF.EnableDebug( dev, 1 ) ) + { + return -13; + } + } + else + goto unimplemented; + } + if ( unlock_bootloader ) + { + if ( InternalUnlockBootloader( dev ) ) goto unimplemented; + } + if ( terminal || gdbserver || cmdserver ) + { + if ( !MCF.PollTerminal ) goto unimplemented; + + if ( gdbserver && SetupGDBServer( dev ) ) + { + fprintf( stderr, "Error: can't start GDB server\n" ); + return -1; + } + if ( gdbserver ) + { + fprintf( stderr, "GDBServer Running\n" ); + } + else if ( terminal ) + { + // In case we aren't running already. + if ( iss->target_chip_type == CHIP_CH58x || iss->target_chip_type == CHIP_CH32V10x ) + MCF.HaltMode( dev, HALT_MODE_RESUME ); + else + MCF.HaltMode( dev, HALT_MODE_REBOOT ); + } + + CaptureKeyboardInput(); + printf( "Terminal started\n\n" ); + + if ( cmdserver ) + { + CMDInit(); + } + +#if TERMINAL_INPUT_BUFFER + char pline_buf[256]; // Buffer that contains current line that is being printed to + char input_buf[128]; // Buffer that contains user input until it is sent out + memset( pline_buf, 0, sizeof( pline_buf ) ); + memset( input_buf, 0, sizeof( input_buf ) ); + uint8_t input_pos = 0; + uint8_t to_send = 0; + uint8_t nice_terminal = isatty( fileno( stdout ) ); +#if defined( WINDOWS ) || defined( WIN32 ) || defined( _WIN32 ) + unsigned long console_mode; + void *handle_output = GetStdHandle( STD_OUTPUT_HANDLE ); + GetConsoleMode( handle_output, &console_mode ); + console_mode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; + uint8_t set_result = SetConsoleMode( handle_output, console_mode ); + if ( set_result == 0 ) nice_terminal = 0; +#else + if ( nice_terminal > 0 ) + { + fflush( stdin ); + fprintf( stdout, "\x1b[6n" ); + fflush( stdout ); + read( fileno( stdin ), input_buf, 10 ); + if ( input_buf[0] != 27 ) + { + nice_terminal = 0; + } + else + { + printf( TERMINAL_SEND_LABEL ); + fflush( stdout ); + } + memset( input_buf, 0, sizeof( input_buf ) ); + } +#endif +#endif + uint32_t appendword = 0; + do + { + uint8_t buffer[256]; +#if TERMINAL_INPUT_BUFFER + char print_buf[TERMINAL_BUFFER_SIZE]; // Buffer that is filled with everything and will be written to stdout + // (basically it's for formatting) + uint8_t update = 0; +#endif + if ( !IsGDBServerInShadowHaltState( dev ) ) + { + // Handle keyboard input. +#if TERMINAL_INPUT_BUFFER + if ( nice_terminal > 0 ) + { + if ( IsKBHit() && to_send == 0 ) + { + uint8_t c = ReadKBByte(); + if ( c == 8 || c == 127 ) + { + input_buf[input_pos - 1] = 0; + if ( input_pos > 0 ) input_pos--; + } + else if ( c > 31 && c < 127 ) + { + input_buf[input_pos] = c; + input_pos++; + } + else if ( c == '\n' || c == 10 ) + { + to_send = input_pos; + } + update = 1; + } + // Process incomming buffer during sending + if ( to_send > 0 && appendword == 0 ) + { + for ( int i = 0; i < 3; i++ ) + { + appendword |= input_buf[input_pos - to_send] << ( i * 8 + 8 ); + to_send--; + if ( to_send == 0 ) break; + } + if ( to_send == 0 ) + { + snprintf( print_buf, TERMINAL_BUFFER_SIZE - 1, "%s%s%s\n%s%s", TERMINAL_CLEAR_CUR, + TERMIANL_INPUT_SENT, input_buf, pline_buf, TERMINAL_SEND_LABEL ); + fwrite( print_buf, strlen( print_buf ), 1, stdout ); + fflush( stdout ); + input_pos = 0; + memset( input_buf, 0, sizeof( input_buf ) ); + } + appendword |= i + 4; + } + } + else + { + if ( appendword == 0 ) + { + int i; + for ( i = 0; i < 3; i++ ) + { + if ( !IsKBHit() ) break; + appendword |= ReadKBByte() << ( i * 8 + 8 ); + } + appendword |= i + 4; // Will go into DATA0. + } + } +#else + if ( appendword == 0 ) + { + int i; + for ( i = 0; i < 3; i++ ) + { + if ( !IsKBHit() ) break; + appendword |= ReadKBByte() << ( i * 8 + 8 ); + } + + + // So, previously, we would always use 0x04 as the code for + // no bytes to send/receive remaining. But, it looks like 0x00 + // is a valid code too. So if there's no bytes to send, we can + // just let the programmer get more data. + // + // If we find out in the future there is some reason not to do + // this, we can undo this and push the responsibility onto the + // programmers to speed along. + if ( i ) appendword |= i + 4; // Will go into DATA0. + } +#endif + int r = MCF.PollTerminal( dev, buffer, sizeof( buffer ), appendword, 0 ); +#if TERMINAL_INPUT_BUFFER + if ( ( nice_terminal > 0 ) && ( r == -1 || r == 0 ) && update > 0 ) + { + strncpy( print_buf, TERMINAL_CLEAR_CUR, TERMINAL_BUFFER_SIZE - 1 ); + if ( to_send > 0 ) + strncat( print_buf, TERMINAL_DIM, TERMINAL_BUFFER_SIZE - 1 - strlen( print_buf ) ); + strncat( print_buf, TERMINAL_SEND_LABEL, TERMINAL_BUFFER_SIZE - 1 - strlen( print_buf ) ); + strncat( print_buf, input_buf, TERMINAL_BUFFER_SIZE - 1 - strlen( print_buf ) ); + fwrite( print_buf, strlen( print_buf ), 1, stdout ); + fflush( stdout ); + } +#endif + if ( r < -5 ) + { + fprintf( stderr, "Terminal dead. code %d\n", r ); + return -32; + } + else if ( r < 0 ) + { + // Other end ack'd without printf. (Or there is another situation) + appendword = 0; + } + else if ( r > 0 ) + { +#if TERMINAL_INPUT_BUFFER + if ( nice_terminal ) + { + int new_line = -1; + for ( int i = r; i > 0; i-- ) + { + if ( buffer[i - 1] == '\n' ) + { + new_line = r - i; + break; + } + } + if ( new_line < 0 ) + { + strncpy( print_buf, TERMINAL_CLEAR_PREV, + TERMINAL_BUFFER_SIZE - 1 ); // Go one line up and erase it + strncat( pline_buf, (char *)buffer, r ); // Add newly received chars to line buffer + } + else + { + strncpy( print_buf, TERMINAL_CLEAR_CUR, + TERMINAL_BUFFER_SIZE - 1 ); // Go to the start of the line and erase it + strncat( + pline_buf, (char *)buffer, r - new_line ); // Add newly received chars to line buffer + } + strncat( print_buf, pline_buf, + TERMINAL_BUFFER_SIZE - 1 - strlen( print_buf ) ); // Add line to buffer + if ( new_line >= 0 ) + { + memset( pline_buf, 0, sizeof( pline_buf ) ); + } + if ( new_line > 0 ) + { + strncat( pline_buf, (char *)buffer + r - new_line, new_line ); + strncat( print_buf, pline_buf, + TERMINAL_BUFFER_SIZE - 1 - strlen( print_buf ) ); // Add line to buffer + } + + if ( to_send > 0 ) + strncat( print_buf, TERMINAL_DIM, TERMINAL_BUFFER_SIZE - 1 - strlen( print_buf ) ); + strncat( print_buf, TERMINAL_SEND_LABEL, + TERMINAL_BUFFER_SIZE - 1 - strlen( print_buf ) ); // Print styled "Send" label + strncat( print_buf, input_buf, + TERMINAL_BUFFER_SIZE - 1 - strlen( print_buf ) ); // Print current input + fwrite( print_buf, strlen( print_buf ), 1, stdout ); + print_buf[0] = 0; + // memset( print_buf, 0, sizeof( print_buf ) ); + } + else + { + fwrite( buffer, r, 1, stdout ); + } +#else + fwrite( buffer, r, 1, stdout ); +#endif + fflush( stdout ); + // Otherwise it's basically just an ack for appendword. + appendword = 0; + } + } + + if ( gdbserver ) + { + PollGDBServer( dev ); + } + + if ( cmdserver && ( 1 == CMDPollServer( dev ) ) ) + { + // TODO: signal to GDB server that it should resume. + } + + } while ( 1 ); + + // Currently unreachable - consider reachable-ing + if ( gdbserver ) ExitGDBServer( dev ); + } + + + int iarg = 0; const char * lastcommand = 0; for( ; iarg < argc; iarg++ ) { @@ -226,135 +713,14 @@ int main( int argc, char ** argv ) fprintf( stderr, "Error: Need prefixing - before commands\n" ); goto help; } - if( must_be_end ) - { - fprintf( stderr, "Error: the command '%c' cannot be followed by other commands.\n", must_be_end ); - return -1; - } + + int status = 0; keep_going: switch( argchar[1] ) { default: fprintf( stderr, "Error: Unknown command %c\n", argchar[1] ); - case 'h': - goto help; - case 'k': - printf( "Skipping programmer initialization\n" ); - argchar++; - goto keep_going; - case '3': - if( MCF.Control3v3 ) - MCF.Control3v3( dev, 1 ); - else - goto unimplemented; - break; - case '5': - if( MCF.Control5v ) - MCF.Control5v( dev, 1 ); - else - goto unimplemented; - break; - case 't': - if( MCF.Control3v3 ) - MCF.Control3v3( dev, 0 ); - else - goto unimplemented; - break; - case 'f': - if( MCF.Control5v ) - MCF.Control5v( dev, 0 ); - else - goto unimplemented; - break; - case 'C': // For specifying programmer - case 'c': - // COM port or programmer argument already parsed previously - // we still need to skip the next argument - iarg+=1; - if( iarg >= argc ) - { - fprintf( stderr, "-c/C argument required 2 arguments\n" ); - goto unimplemented; - } - break; - case 'u': - if( MCF.Unbrick ) - MCF.Unbrick( dev ); - else - goto unimplemented; - break; - case 'U': - // Unlock Bootloader - if( InternalUnlockBootloader( dev ) ) - goto unimplemented; - break; - case 'b': //reBoot - if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_REBOOT ) ) - goto unimplemented; - break; - case 'B': //reBoot into Bootloader - if( iss->target_chip_type != CHIP_CH32V003 - && iss->target_chip_type != CHIP_CH32V00x - && iss->target_chip_type != CHIP_CH641 - && iss->target_chip_type != CHIP_CH643 - && iss->target_chip_type != CHIP_CH32X03x ) DefaultRebootIntoBootloader( dev ); - else if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_GO_TO_BOOTLOADER ) ) - goto unimplemented; - break; - case 'e': //rEsume - if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_RESUME ) ) - goto unimplemented; - break; - case 'E': //Erase whole chip. - if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); - if( !MCF.Erase || MCF.Erase( dev, 0, 0, 1 ) ) - goto unimplemented; - break; - case 'a': - if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ) ) - goto unimplemented; - break; - case 'A': // Halt without reboot - if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_HALT_BUT_NO_RESET ) ) - goto unimplemented; - break; - - // disable NRST pin (turn it into a GPIO) - case 'd': // see "RSTMODE" in datasheet - if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); - if( MCF.ConfigureNRSTAsGPIO ) - MCF.ConfigureNRSTAsGPIO( dev, 0 ); - else - goto unimplemented; - break; - case 'D': // see "RSTMODE" in datasheet - if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); - if( MCF.ConfigureNRSTAsGPIO ) - MCF.ConfigureNRSTAsGPIO( dev, 1 ); - else - goto unimplemented; - break; - case 'p': - if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); - if( MCF.ConfigureReadProtection ) - { - fprintf(stderr, "This will erase the flash entirely!\n"); - fprintf(stderr, "Press Enter to proceed, Ctrl+C to abort."); - while(!IsKBHit()); - // TODO: revert after testing - MCF.ConfigureReadProtection( dev, 0 ); - } - else - goto unimplemented; - - break; - case 'P': - if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ); - if( MCF.ConfigureReadProtection ) - MCF.ConfigureReadProtection( dev, 1 ); - else - goto unimplemented; break; case 'S': // Set FLASH/RAM split in option bytes { @@ -394,252 +760,6 @@ int main( int argc, char ** argv ) MCF.SetSplit(dev, split); break; } - case 'G': - case 'T': - { - if( !MCF.PollTerminal ) - goto unimplemented; - - if( argchar[1] == 'G' && SetupGDBServer( dev ) ) - { - fprintf( stderr, "Error: can't start GDB server\n" ); - return -1; - } - if( argchar[1] == 'G' ) - { - fprintf( stderr, "GDBServer Running\n" ); - } - else if( argchar[1] == 'T' ) - { - // In case we aren't running already. - if( iss->target_chip_type == CHIP_CH58x || iss->target_chip_type == CHIP_CH32V10x ) MCF.HaltMode( dev, HALT_MODE_RESUME ); - else MCF.HaltMode( dev, HALT_MODE_REBOOT ); - } - - CaptureKeyboardInput(); - printf( "Terminal started\n\n" ); - - CMDInit(); - -#if TERMINAL_INPUT_BUFFER - char pline_buf[256]; // Buffer that contains current line that is being printed to - char input_buf[128]; // Buffer that contains user input until it is sent out - memset( pline_buf, 0, sizeof(pline_buf) ); - memset( input_buf, 0, sizeof(input_buf) ); - uint8_t input_pos = 0; - uint8_t to_send = 0; - uint8_t nice_terminal = isatty( fileno(stdout) ); -#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32) - unsigned long console_mode; - void* handle_output = GetStdHandle(STD_OUTPUT_HANDLE); - GetConsoleMode(handle_output, &console_mode); - console_mode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; - uint8_t set_result = SetConsoleMode(handle_output, console_mode); - if ( set_result == 0 ) nice_terminal = 0; -#else - if( nice_terminal > 0 ) - { - fflush( stdin ); - fprintf( stdout, "\x1b[6n" ); - fflush( stdout ); - read( fileno(stdin), input_buf, 10 ); - if (input_buf[0] != 27) - { - nice_terminal = 0; - } - else - { - printf( TERMINAL_SEND_LABEL ); - fflush( stdout ); - } - memset( input_buf, 0, sizeof(input_buf) ); - } -#endif -#endif - uint32_t appendword = 0; - do - { - uint8_t buffer[256]; -#if TERMINAL_INPUT_BUFFER - char print_buf[TERMINAL_BUFFER_SIZE]; // Buffer that is filled with everything and will be written to stdout (basically it's for formatting) - uint8_t update = 0; -#endif - if( !IsGDBServerInShadowHaltState( dev ) ) - { - // Handle keyboard input. -#if TERMINAL_INPUT_BUFFER - if ( nice_terminal > 0 ) - { - if( IsKBHit() && to_send == 0 ) - { - uint8_t c = ReadKBByte(); - if ( c == 8 || c == 127 ) - { - input_buf[input_pos - 1] = 0; - if ( input_pos > 0 ) input_pos--; - } - else if ( c > 31 && c < 127 ) - { - input_buf[input_pos] = c; - input_pos++; - } - else if ( c == '\n' || c == 10 ) - { - to_send = input_pos; - } - update = 1; - } - // Process incomming buffer during sending - if( to_send > 0 && appendword == 0 ) - { - for( int i = 0; i < 3; i++ ) - { - appendword |= input_buf[input_pos - to_send] << ( i * 8 + 8 ); - to_send--; - if ( to_send == 0 ) break; - } - if( to_send == 0 ) - { - snprintf(print_buf, TERMINAL_BUFFER_SIZE - 1, "%s%s%s\n%s%s", TERMINAL_CLEAR_CUR, TERMIANL_INPUT_SENT, input_buf, pline_buf, TERMINAL_SEND_LABEL); - fwrite( print_buf, strlen( print_buf ), 1, stdout ); - fflush( stdout ); - input_pos = 0; - memset( input_buf, 0, sizeof( input_buf ) ); - } - appendword |= i + 4; - } - } - else - { - if( appendword == 0 ) - { - int i; - for( i = 0; i < 3; i++ ) - { - if( !IsKBHit() ) break; - appendword |= ReadKBByte() << (i*8+8); - } - appendword |= i+4; // Will go into DATA0. - } - } -#else - if( appendword == 0 ) - { - int i; - for( i = 0; i < 3; i++ ) - { - if( !IsKBHit() ) break; - appendword |= ReadKBByte() << (i*8+8); - } - - - // So, previously, we would always use 0x04 as the code for - // no bytes to send/receive remaining. But, it looks like 0x00 - // is a valid code too. So if there's no bytes to send, we can - // just let the programmer get more data. - // - // If we find out in the future there is some reason not to do - // this, we can undo this and push the responsibility onto the - // programmers to speed along. - if( i ) - appendword |= i+4; // Will go into DATA0. - } -#endif - int r = MCF.PollTerminal( dev, buffer, sizeof( buffer ), appendword, 0 ); -#if TERMINAL_INPUT_BUFFER - if( (nice_terminal > 0) && ( r == -1 || r == 0 ) && update > 0 ) - { - strncpy( print_buf, TERMINAL_CLEAR_CUR, TERMINAL_BUFFER_SIZE - 1 ); - if ( to_send > 0 ) strncat( print_buf, TERMINAL_DIM, TERMINAL_BUFFER_SIZE - 1 - strlen(print_buf) ); - strncat( print_buf, TERMINAL_SEND_LABEL, TERMINAL_BUFFER_SIZE - 1 - strlen(print_buf) ); - strncat( print_buf, input_buf, TERMINAL_BUFFER_SIZE - 1 - strlen(print_buf) ); - fwrite( print_buf, strlen( print_buf ), 1, stdout ); - fflush( stdout ); - } -#endif - if( r < -5 ) - { - fprintf( stderr, "Terminal dead. code %d\n", r ); - return -32; - } - else if( r < 0 ) - { - // Other end ack'd without printf. (Or there is another situation) - appendword = 0; - } - else if( r > 0 ) - { -#if TERMINAL_INPUT_BUFFER - if ( nice_terminal ) - { - int new_line = -1; - for( int i = r; i > 0; i-- ) - { - if( buffer[i-1] == '\n' ) - { - new_line = r - i; - break; - } - } - if( new_line < 0 ) - { - strncpy( print_buf, TERMINAL_CLEAR_PREV, TERMINAL_BUFFER_SIZE - 1 ); // Go one line up and erase it - strncat( pline_buf, (char *)buffer, r); // Add newly received chars to line buffer - } - else - { - strncpy( print_buf, TERMINAL_CLEAR_CUR, TERMINAL_BUFFER_SIZE - 1 ); // Go to the start of the line and erase it - strncat( pline_buf, (char *)buffer, r - new_line ); // Add newly received chars to line buffer - } - strncat( print_buf, pline_buf, TERMINAL_BUFFER_SIZE - 1 - strlen(print_buf) ); // Add line to buffer - if( new_line >= 0 ) - { - memset( pline_buf, 0, sizeof( pline_buf ) ); - } - if( new_line > 0) - { - strncat( pline_buf, (char *)buffer+r-new_line, new_line ); - strncat( print_buf, pline_buf, TERMINAL_BUFFER_SIZE - 1 - strlen(print_buf) ); // Add line to buffer - } - - if( to_send > 0 ) strncat( print_buf, TERMINAL_DIM, TERMINAL_BUFFER_SIZE - 1 - strlen(print_buf) ); - strncat( print_buf, TERMINAL_SEND_LABEL, TERMINAL_BUFFER_SIZE - 1 - strlen(print_buf) ); // Print styled "Send" label - strncat( print_buf, input_buf, TERMINAL_BUFFER_SIZE - 1 - strlen(print_buf) ); // Print current input - fwrite( print_buf, strlen( print_buf ), 1, stdout ); - print_buf[0] = 0; - // memset( print_buf, 0, sizeof( print_buf ) ); - - } - else - { - fwrite( buffer, r, 1, stdout ); - } -#else - fwrite( buffer, r, 1, stdout ); -#endif - fflush( stdout ); - // Otherwise it's basically just an ack for appendword. - appendword = 0; - } - } - - if( argchar[1] == 'G' ) - { - PollGDBServer( dev ); - } - - if(1 == CMDPollServer( dev )) - { - // TODO: signal to GDB server that it should resume. - } - - } while( 1 ); - - // Currently unreachable - consider reachable-ing - if( argchar[1] == 'G' ) - ExitGDBServer( dev ); - break; - } case 's': { iarg+=2; @@ -706,14 +826,6 @@ int main( int argc, char ** argv ) goto unimplemented; break; } - case 'i': - { - if( MCF.PrintChipInfo ) - MCF.PrintChipInfo( dev ); - else - goto unimplemented; - break; - } case 'X': { iarg++; @@ -935,34 +1047,6 @@ int main( int argc, char ** argv ) free( image ); break; } - case 'N': - { - if( MCF.EnableDebug ) - { - printf("Enabling debug\n"); - if( MCF.EnableDebug( dev, 0 ) ) - { - return -13; - } - } - break; - } - case 'n': - { - if( MCF.EnableDebug ) - { - fprintf( stderr, "This will disable debug module. And you will be only able to program the chip using the bootloader.\nPress Enter to continue\n"); - while(!IsKBHit()); - ReadKBByte(); - fprintf( stderr, "Are you absolutely sure about it?\nPress Enter to continue, or Ctrl+C to cancel\n"); - while(!IsKBHit()); - if( MCF.EnableDebug( dev, 1 ) ) - { - return -13; - } - } - break; - } case 'Y': { if( iss->target_chip_type == CHIP_CH570 || @@ -972,7 +1056,6 @@ int main( int argc, char ** argv ) iss->target_chip_type == CHIP_CH59x) CH5xxBlink(dev, 0, 8, 0); break; } - case 'y': { readCSR( dev, 0x300 ); @@ -995,30 +1078,31 @@ int main( int argc, char ** argv ) fprintf( stderr, "Usage: minichlink [args]\n" ); fprintf( stderr, " single-letter args may be combined, i.e. -3r\n" ); fprintf( stderr, " multi-part args cannot.\n" ); - fprintf( stderr, " -3 Enable 3.3V\n" ); - fprintf( stderr, " -5 Enable 5V\n" ); - fprintf( stderr, " -t Disable 3.3V\n" ); - fprintf( stderr, " -f Disable 5V\n" ); - fprintf( stderr, " -k Skip programmer initialization\n" ); - fprintf( stderr, " -c [serial port for Ardulink, try /dev/ttyACM0 or COM11 etc] or [VID+PID of USB for b003boot, try 0x1209b003]\n" ); - fprintf( stderr, " -C [specified programmer, eg. b003boot, ardulink, esp32s2chfun, isp]\n" ); - fprintf( stderr, " -u Clear all code flash - by power off (also can unbrick)\n" ); - fprintf( stderr, " -E Erase chip\n" ); - fprintf( stderr, " -b Reboot out of Halt\n" ); - fprintf( stderr, " -e Resume from halt\n" ); - fprintf( stderr, " -a Reboot into Halt\n" ); - fprintf( stderr, " -A Go into Halt without reboot\n" ); - fprintf( stderr, " -D Configure NRST as GPIO\n" ); - fprintf( stderr, " -d Configure NRST as NRST\n" ); - fprintf( stderr, " -i Show chip info\n" ); + flag_print_options(stderr); + // fprintf( stderr, " -3 Enable 3.3V\n" ); + // fprintf( stderr, " -5 Enable 5V\n" ); + // fprintf( stderr, " -t Disable 3.3V\n" ); + // fprintf( stderr, " -f Disable 5V\n" ); + // fprintf( stderr, " -k Skip programmer initialization\n" ); + // fprintf( stderr, " -c [serial port for Ardulink, try /dev/ttyACM0 or COM11 etc] or [VID+PID of USB for b003boot, try 0x1209b003]\n" ); + // fprintf( stderr, " -C [specified programmer, eg. b003boot, ardulink, esp32s2chfun, isp]\n" ); + // fprintf( stderr, " -u Clear all code flash - by power off (also can unbrick)\n" ); + // fprintf( stderr, " -E Erase chip\n" ); + // fprintf( stderr, " -b Reboot out of Halt\n" ); + // fprintf( stderr, " -e Resume from halt\n" ); + // fprintf( stderr, " -a Reboot into Halt\n" ); + // fprintf( stderr, " -A Go into Halt without reboot\n" ); + // fprintf( stderr, " -D Configure NRST as GPIO\n" ); + // fprintf( stderr, " -d Configure NRST as NRST\n" ); + // fprintf( stderr, " -i Show chip info\n" ); fprintf( stderr, " -s [debug register] [value]\n" ); fprintf( stderr, " -m [debug register]\n" ); fprintf( stderr, " -T Terminal Only (must be last arg)\n" ); fprintf( stderr, " -G Terminal + GDB (must be last arg)\n" ); - fprintf( stderr, " -P Enable Read Protection\n" ); - fprintf( stderr, " -p Disable Read Protection\n" ); - fprintf( stderr, " -N Enable Debug Module\n" ); - fprintf( stderr, " -n Disable Debug Module\n" ); + // fprintf( stderr, " -P Enable Read Protection\n" ); + // fprintf( stderr, " -p Disable Read Protection\n" ); + // fprintf( stderr, " -N Enable Debug Module\n" ); + // fprintf( stderr, " -n Disable Debug Module\n" ); fprintf( stderr, " -S set FLASH/SRAM split [FLASH kbytes] [SRAM kbytes]\n" ); fprintf( stderr, " -w [binary image to write] [address, decimal or 0x, try0x08000000]\n" ); fprintf( stderr, " -r [output binary image] [memory address, decimal or 0x, try 0x08000000] [size, decimal or 0x, try 16384]\n" ); @@ -2940,7 +3024,7 @@ static int DefaultHaltMode( void * dev, int mode ) iss->processor_in_mode = mode; // In case processor halt process needs to complete, i.e. if it was in the middle of a flash op. - DefaultDelayUS( dev, 10000 ); + DefaultDelayUS( dev, 10000 ); return 0; } @@ -3181,8 +3265,8 @@ int DefaultDelayUS( void * dev, int us ) static int DefaultSetClock( void * dev, uint32_t clock ) { - fprintf( stderr, "Will set clock here, when implemented\n" ); - return 0; + fprintf( stderr, "Will set clock here, when implemented\n" ); + return 0; } int SetupAutomaticHighLevelFunctions( void * dev ) @@ -3241,18 +3325,18 @@ int SetupAutomaticHighLevelFunctions( void * dev ) MCF.Unbrick = DefaultUnbrick; if( !MCF.ConfigureNRSTAsGPIO ) MCF.ConfigureNRSTAsGPIO = DefaultConfigureNRSTAsGPIO; - if( !MCF.ConfigureReadProtection ) - MCF.ConfigureReadProtection = DefaultConfigureReadProtection; + if( !MCF.ConfigureReadProtection ) + MCF.ConfigureReadProtection = DefaultConfigureReadProtection; if( !MCF.VoidHighLevelState ) MCF.VoidHighLevelState = DefaultVoidHighLevelState; if( !MCF.DelayUS ) MCF.DelayUS = DefaultDelayUS; if( !MCF.GetUUID ) MCF.GetUUID = DefaultGetUUID; - if( !MCF.SetClock ) - MCF.SetClock = DefaultSetClock; - if( !MCF.EnableDebug ) - MCF.EnableDebug = DefaultEnableDebug; + if( !MCF.SetClock ) + MCF.SetClock = DefaultSetClock; + if( !MCF.EnableDebug ) + MCF.EnableDebug = DefaultEnableDebug; return 0; } @@ -3533,5 +3617,5 @@ static int DefaultRebootIntoBootloader( void * dev ) printf( "Chip is halted in bootloader mode. Press enter to proceed\n"); while(!IsKBHit()); ReadKBByte(); - return 0; + return 0; }