From 7cb3627754910ddf4d26bb095e875c33d7741b5a Mon Sep 17 00:00:00 2001 From: francois Date: Wed, 20 May 2026 22:22:05 +0200 Subject: [PATCH] phase 1: cleanup, REPL polish, README - fix format-string in jprint/script_print (printf(msg) -> fputs) - fix bsexp_deinit: deinit_script guarded by sctx, not jc - silence "config.script not found" when the optional override is absent - remove dead code: bs/cmds/, bs/args.{c,h}, bs/utils.h, commented blocks - REPL: welcome banner with version, readline tab-completion on script_commands_list, skip blank lines, clean Ctrl-D exit - README: build, REPL usage, typical SPI flow, command table, probes, Xilinx STARTUPE3/CCLK caveat Co-Authored-By: Claude Opus 4.7 --- README.md | 135 ++++++++++++++++++++++++++++-- bs/args.c | 91 --------------------- bs/args.h | 7 -- bs/cmds/exit.c | 11 --- bs/cmds/exit.h | 10 --- bs/cmds/help.c | 8 -- bs/cmds/help.h | 8 -- bs/cmds/list_probes.c | 69 ---------------- bs/cmds/list_probes.h | 10 --- bs/cmds/scan.c | 39 --------- bs/cmds/scan.h | 11 --- bs/cmds/select_probe.c | 16 ---- bs/cmds/select_probe.h | 10 --- bs/init.c | 96 ++++++++-------------- bs/init.h | 11 +-- bs/main.c | 182 ++++++++++++++++++++--------------------- bs/utils.h | 14 ---- 17 files changed, 247 insertions(+), 481 deletions(-) delete mode 100644 bs/args.c delete mode 100644 bs/args.h delete mode 100644 bs/cmds/exit.c delete mode 100644 bs/cmds/exit.h delete mode 100644 bs/cmds/help.c delete mode 100644 bs/cmds/help.h delete mode 100644 bs/cmds/list_probes.c delete mode 100644 bs/cmds/list_probes.h delete mode 100644 bs/cmds/scan.c delete mode 100644 bs/cmds/scan.h delete mode 100644 bs/cmds/select_probe.c delete mode 100644 bs/cmds/select_probe.h delete mode 100644 bs/utils.h diff --git a/README.md b/README.md index ad38bd5..9835cc4 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,135 @@ -# My application for boundary scan +# bs_explorer — Boundary Scan Explorer -Basée sur la librairie de l'application [jtag-boundary-scanner](https://github.com/viveris/jtag-boundary-scanner). +Outil en ligne de commande pour explorer une chaîne JTAG, contrôler les +broches d'un FPGA via le boundary scan (BSDL), et — à terme — programmer +les mémoires SPI connectées au FPGA (Xilinx et autres) en bit-bangeant +SPI sur les pins d'I/O placés en EXTEST. -## Usage +Basé sur la librairie [jtag-boundary-scanner](https://github.com/viveris/jtag-boundary-scanner) +de Viveris (LGPL). -TBD +## État -## How to compile +- Détection de la chaîne JTAG via sondes FTDI / J-Link / Linux GPIO : OK +- Chargement automatique d'un BSDL par IDCODE : OK +- Pilotage de pins en SAMPLE / EXTEST : OK +- SPI bit-bang sur 4 pins du FPGA (MOSI/MISO/CS/CLK) : OK (primitive bas niveau) +- Programmation de flash SPI (détection JEDEC, erase, page program, verify) : **à venir** +- Abstraction multi-FPGA (mapping pins → flash par cible) : **à venir** -``` -mkdir build -cd build +Seul BSDL embarqué pour l'instant : Xilinx Kintex UltraScale+ KU15P +(`bsdl_files/xcku15p_ffve1517.bsd`). En ajouter d'autres se fait en +déposant le `.bsd` dans `bsdl_files/`. + +## Dépendances + +- CMake ≥ 3.10, gcc/clang +- `readline` (Arch : `readline`, Debian/Ubuntu : `libreadline-dev`) +- `libftd2xx` pour les sondes FTDI (vendoré dans `libs/libftd2xx/`) + +## Compilation + +```sh +mkdir build && cd build cmake .. make ``` + +Le binaire est produit dans `build/bs/bs`. + +## Lancement + +```sh +cd build +./bs/bs +``` + +Au démarrage, `bs_explorer` cherche un fichier `config.script` dans le +répertoire courant pour surcharger les variables par défaut (clock FTDI, +mapping des broches TRST/SRST, etc.). Voir `modules/config/config.script` +pour la liste des variables disponibles. + +## REPL + +- `` complète les noms de commande. +- Historique géré par GNU readline (flèches haut/bas, Ctrl-R…). +- `help` ou `?` liste les commandes ; `help ` détaille une commande. +- `exit`, `quit` ou Ctrl-D pour quitter. + +## Flow typique + +```sh +# 1. Ouvrir une sonde (1 = première sonde détectée) +bs_explorer> jtag_get_probes_list +bs_explorer> jtag_open_probe 1 + +# 2. Scanner la chaîne et charger les BSDL automatiquement +bs_explorer> jtag_autoinit + +# 3. Passer le device 0 en EXTEST (contrôle direct des broches) +bs_explorer> jtag_set_mode 0 EXTEST + +# 4. Configurer les 4 pins SPI sur les broches BSDL du FPGA +# (les noms exacts dépendent du BSDL chargé) +bs_explorer> jtag_set_spi_cs_pin 0 0 +bs_explorer> jtag_set_spi_clk_pin 0 0 +bs_explorer> jtag_set_spi_mosi_pin 0 0 +bs_explorer> jtag_set_spi_miso_pin 0 0 + +# 5. Lire le JEDEC ID de la flash (0x9F + 3 dummies) +bs_explorer> jtag_spi_rd_wr 9F000000 +``` + +Un fichier d'exemple minimal est fourni dans `scripts/example_script.txt`. + +## Commandes principales + +| Catégorie | Commandes | +|---------------|-----------| +| Contrôle script | `set`, `print`, `print_env_var`, `if`, `goto`, `call`, `return`, `rand`, `init_array`, `system`, `pause` | +| Sonde / chaîne | `jtag_get_probes_list`, `jtag_open_probe`, `jtag_init_scan`, `jtag_autoinit`, `jtag_get_nb_of_devices`, `jtag_get_devices_list` | +| BSDL / pins | `jtag_load_bsdl`, `jtag_get_pins_list`, `jtag_set_mode`, `jtag_set_pin_dir`, `jtag_set_pin_state`, `jtag_get_pin_state`, `jtag_push_pop` | +| I²C / MDIO / SPI sur pins BS | `jtag_set_i2c_*_pin`, `jtag_i2c_rd`, `jtag_i2c_wr`, `jtag_set_mdio_*_pin`, `jtag_mdio_rd`, `jtag_mdio_wr`, `jtag_set_spi_*_pin`, `jtag_spi_rd_wr` | +| Misc | `help`, `?`, `version`, `exit` | + +Utiliser `help ` pour l'aide détaillée. + +## Sondes supportées + +- **FTDI** MPSSE (FT2232D/H, FT4232H…) — voir le bloc `PROBE_FTDI_*` dans + `modules/config/config.script` pour le mapping des broches et la fréquence TCK. +- **SEGGER J-Link** +- **Linux GPIO** (sysfs ; déprécié sur kernels récents, à migrer vers libgpiod) + +## Limitations connues sur Xilinx + +Sur 7-Series / UltraScale / UltraScale+, `CCLK` n'est pas une broche +d'I/O classique et passe par la primitive `STARTUPE3`. Elle n'est donc +pas directement pilotable en EXTEST. Workarounds connus : +- utiliser l'instruction privée `ISC_DISABLE` ; +- câbler l'horloge SPI sur un autre pin utilisateur du FPGA. + +À traiter lors de l'ajout de la couche d'abstraction FPGA. + +## Structure du dépôt + +``` +bs/ Application (REPL readline) +modules/ +├── jtag_core/ TAP state machine, IR/DR shifts +├── bsdl_parser/ Chargement de .bsd +├── bus_over_jtag/ SPI / I²C / MDIO / mem parallèle bit-bangés +├── drivers/ FTDI, J-Link, Linux GPIO, LPT +├── script/ Moteur de scripts (40+ commandes) +├── config/ config.script intégré +├── os_interface/ Wrappers fs/network portables +└── natsort/ Tri naturel des noms de pin +bsdl_files/ Fichiers BSDL des FPGAs cibles +scripts/ Exemples de scripts +libs/libftd2xx/ SDK FTDI vendoré +``` + +## Licence + +`modules/jtag_core/` et les fichiers d'origine Viveris sont sous LGPL 2.1. +Voir `LICENSE` et `modules/jtag_core/COPYING.LESSER`. diff --git a/bs/args.c b/bs/args.c deleted file mode 100644 index d71e9f8..0000000 --- a/bs/args.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include -#include -#include - -#include "utils.h" -#include "args.h" - -// void global_help() { -// printf("Usage: bs [[-l] [--list]] [[-n] [--nprobe]] [[-b] [--bsdl]] [[-d] [--device_id]] PROBE_NUM] [] []\n"); -// } - -// int parse_args(struct args *a, int argc, char *argv[]) { -// int opt = 0; -// int i = 0; -// unsigned int devid = 0; -// int command = 0; -// __uint8_t option_arg[MAX_PATH] = {0}; - -// // Définir les options longues -// static struct option long_options[] = { -// {"list", no_argument, 0, 'l'}, -// {"nprobe", required_argument, 0, 'n'}, -// {"bsdl", required_argument, 0, 'b'}, -// {"device_id", required_argument, 0, 'd'}, -// {0, 0, 0, 0} -// }; - -// // Utilisation de getopt_long pour parser les options -// while ((opt = getopt_long(argc, argv, "ln:b:d:", long_options, NULL)) != -1) { -// switch (opt) { -// case 'h': -// a->list = 1; -// break; -// case 'n': -// snprintf(option_arg, sizeof(option_arg)-2, "%s", optarg); -// a->probe = atoi(option_arg); -// break; -// case 'b': -// snprintf(a->bsdl, MAX_PATH-2, "%s", optarg); -// break; -// case 'd': -// i = sscanf(optarg, "0x%x", &devid); -// if (i == 0) { -// printf("Device ID must be an hex value like '0xABCD'"); -// return EXIT_FAILURE; -// } -// break; -// default: -// usage(); -// return EXIT_FAILURE; -// } -// } - -// // Positional arguments -// if (optind < argc) { -// /* Positional arguments */ -// while (optind < argc) { -// for (i=0;icmds[command] = (enum commands)i+1; -// } -// } -// optind++; -// } -// } - -// // Si aucune option n'est fournie, afficher l'aide -// if (optind == 1) { -// usage(); -// } - -// return EXIT_SUCCESS; -// } - -// void parse_command(char *line, int *argc, char **argv) { -// *argc = 0; -// while (*line != '\0') { -// while (*line == ' ' || *line == '\t' || *line == '\n') { -// *line++ = '\0'; -// } -// if (*argc >= MAX_ARGS) break; -// (*argc)++; -// *argv++ = line; -// while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n') { -// line++; -// } -// } -// *argv = '\0'; -// } diff --git a/bs/args.h b/bs/args.h deleted file mode 100644 index 244e59c..0000000 --- a/bs/args.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _ARGS_H -#define _ARGS_H - -// int parse_args(struct args *a, int argc, char *argv[]); -// void parse_command(char *line, int *argc, char **argv); - -#endif diff --git a/bs/cmds/exit.c b/bs/cmds/exit.c deleted file mode 100644 index 7a0c561..0000000 --- a/bs/cmds/exit.c +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include - -#include "exit.h" - -const char cmd_exit_help[] = "Bla bla."; - -int cmd_exit(jtag_core *jc, int argc, char **argv) { - jtagcore_deinit(jc); - exit(0); -} \ No newline at end of file diff --git a/bs/cmds/exit.h b/bs/cmds/exit.h deleted file mode 100644 index e9c09d5..0000000 --- a/bs/cmds/exit.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _CMD_EXIT_H -#define _CMD_EXIT_H - -#include "jtag_core/jtag_core.h" - -extern const char cmd_exit_help[]; - -int cmd_exit(jtag_core *jc, int argc, char *argv[]); - -#endif /* _CMD_EXIT_H */ \ No newline at end of file diff --git a/bs/cmds/help.c b/bs/cmds/help.c deleted file mode 100644 index 4c55d7c..0000000 --- a/bs/cmds/help.c +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -#include "help.h" - -int cmd_help(jtag_core *jc, int argc, char **argv) { - -} \ No newline at end of file diff --git a/bs/cmds/help.h b/bs/cmds/help.h deleted file mode 100644 index ae679db..0000000 --- a/bs/cmds/help.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _CMD_HELP_H -#define _CMD_HELP_H - -#include "jtag_core/jtag_core.h" - -int cmd_help(jtag_core *jc, int argc, char *argv[]); - -#endif /* _CMD_help_H */ \ No newline at end of file diff --git a/bs/cmds/list_probes.c b/bs/cmds/list_probes.c deleted file mode 100644 index cd78a35..0000000 --- a/bs/cmds/list_probes.c +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include - -#include "list_probes.h" -#include "../utils.h" - -const char cmd_list_probes_help[] = "Bla bla."; - -struct probe { - int drv; - int probe; - int probe_id; - char name[PROBE_NAME_SIZE]; -}; - -int list_probes(jtag_core *jc, struct probe probes[], int show) { - int i = 0; - int j = 0; - int n = 0; - - char probe_name[PROBE_NAME_SIZE] = {0}; - int n_probe_drv = 0; - int n_probes = 0; - - /* Drivers and probes */ - n_probe_drv = jtagcore_get_number_of_probes_drv(jc); - if (n_probe_drv > 0) { - if (0 != show) printf("Found a debug probe driver:\n"); - } else { - if (0 != show) printf("No probes driver found\n"); - return n; - } - - for (i=0;i 0) { - - if (0 != show) printf("Found a debug probe:\n"); - - } else { - - if (0 != show) printf("No probe found.\n"); - continue; - } - for (j=0;j - -#include "scan.h" -#include "../utils.h" - -const char cmd_scan_help[] = "Bla bla."; - -int scan(jtag_core *jc, int probe_id, int *ndevs, unsigned long ids[], int show) { - int err = JTAG_CORE_NO_ERROR; - int n = 0; - int i = 0; - - if (0 != show) printf("Devices scan in progress...\n"); - - err = jtagcore_scan_and_init_chain(jc); - if (err < 0) { - if (0 != show) printf("Impossible to scan the JTAG chain"); - return err; - } - - n = jtagcore_get_number_of_devices(jc); - if (n < 0) { - if (0 != show) printf("Error while getting the number of devices on the chain.\n"); - return err; - } - *ndevs = n; - for (i=0;i < MIN(n, DEVICES_SCAN_MAX);i++) { - ids[i] = jtagcore_get_dev_id(jc, i); - if (0 != show) printf(" device %d : 0x%08x\n", i, ids[i]); - } - - if (0 != show) printf("Done.\n"); - return err; -} - -int cmd_scan(jtag_core *jc, int argc, char *argv[]) { - -} \ No newline at end of file diff --git a/bs/cmds/scan.h b/bs/cmds/scan.h deleted file mode 100644 index 1c6b03a..0000000 --- a/bs/cmds/scan.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _CMDS_SCAN_H -#define _CMDS_SCAN_H - -#include "jtag_core/jtag_core.h" - -extern const char cmd_scan_help[]; - -int cmd_scan(jtag_core *jc, int argc, char *argv[]); - - -#endif diff --git a/bs/cmds/select_probe.c b/bs/cmds/select_probe.c deleted file mode 100644 index 6478373..0000000 --- a/bs/cmds/select_probe.c +++ /dev/null @@ -1,16 +0,0 @@ -#include - -#include "select_probe.h" - -const char cmd_select_probe_help[] = "Bla bla."; - -int cmd_select_probe(jtag_core *jc, int argc, char *argv[]) { - int error = 0; - - // error = jtagcore_select_and_open_probe(jc, probe_id); - if (error < 0) { - printf("Impossible to open the selected probe.\n"); - return error; - } - -} \ No newline at end of file diff --git a/bs/cmds/select_probe.h b/bs/cmds/select_probe.h deleted file mode 100644 index a045a63..0000000 --- a/bs/cmds/select_probe.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _SELECT_PROBE_H -#define _SELECT_PROBE_H - -#include "jtag_core/jtag_core.h" - -extern const char cmd_select_probe_help[]; - -int cmd_select_probe(jtag_core *jc, int argc, char *argv[]); - -#endif \ No newline at end of file diff --git a/bs/init.c b/bs/init.c index 00ac27c..dcb1069 100644 --- a/bs/init.c +++ b/bs/init.c @@ -1,24 +1,4 @@ #include -#include - -/* -#include "common.h" -#include "cmds/help.h" -#include "cmds/scan.h" -#include "cmds/list_probes.h" -#include "cmds/exit.h" -#include "cmds/select_probe.h" - -// Table des commandes internes -Command commands[] = { - {"help", cmd_help, NULL}, - {"list_probes", cmd_list_probes, cmd_list_probes_help}, - {"scan", cmd_scan, cmd_scan_help}, - {"select_probe", cmd_select_probe, cmd_select_probe_help}, - {"exit", cmd_exit, cmd_exit_help}, - {NULL, NULL}}; -*/ - #include #include #include @@ -27,42 +7,33 @@ Command commands[] = { #include "config/version.h" #include "script/env.h" #include "script/script.h" - #include "config/config_script.h" +#include "init.h" + void jprint(jtag_core *jc, const char *msg) { - printf(msg); + fputs(msg, stdout); } int script_print(script_ctx *sctx, enum MSGTYPE typ, char *string, ...) { int ret = 0; - char msg[64] = {0}; + const char *prefix = ""; va_list args; switch (typ) { - case MSG_DEBUG: - strcpy(msg, "DEBUG :"); - break; - case MSG_INFO_1: - strcpy(msg, "INFO :"); - break; - case MSG_WARNING: - strcpy(msg, "WARNING :"); - break; - case MSG_ERROR: - strcpy(msg, "ERROR :"); - break; + case MSG_DEBUG: prefix = "DEBUG : "; break; + case MSG_INFO_1: prefix = "INFO : "; break; + case MSG_WARNING: prefix = "WARNING : "; break; + case MSG_ERROR: prefix = "ERROR : "; break; case MSG_INFO_0: - default: - strcpy(msg, ""); - break; + default: prefix = ""; break; } va_start(args, string); - ret = printf(msg); + ret = fputs(prefix, stdout); ret += vprintf(string, args); va_end(args); return ret; @@ -70,64 +41,61 @@ int script_print(script_ctx *sctx, enum MSGTYPE typ, char *string, ...) void bsexp_init(jtag_core **jc, script_ctx **sctx) { - // JTAG core initialization *jc = jtagcore_init(); if (NULL == *jc) - goto end; + return; - // Environment initialization (*jc)->envvar = (void *)initEnv(NULL, NULL); if (NULL == (*jc)->envvar) { jtagcore_deinit(*jc); *jc = NULL; - goto end; + return; } jtagcore_setEnvVar(*jc, "VERSION", "v" APP_VER_STR(APP_VER)); - // Scripts initialization *sctx = jtagcore_initScript(*jc); if (NULL == *sctx) { deinitEnv((*jc)->envvar); jtagcore_deinit(*jc); *jc = NULL; - goto end; + return; } setOutputFunc_script(*sctx, script_print); execute_ram_script(*sctx, config_script, config_script_len); - execute_file_script(*sctx, "config.script"); + /* User override is optional — silence the engine's "not found" message. */ + { + FILE *f = fopen("config.script", "r"); + if (f) { + fclose(f); + execute_file_script(*sctx, "config.script"); + } + } - /* Log printing callback */ if (jtagcore_set_logs_callback(*jc, jprint) < 0) { - printf("Impossible to define the logs callback!\n"); + fputs("Impossible to define the logs callback!\n", stderr); + return; } - else + + if (jtagcore_getEnvVar(*jc, "LOG_MESSAGES_FILTER_LEVEL", NULL)) { - if (jtagcore_getEnvVar(*jc, "LOG_MESSAGES_FILTER_LEVEL", NULL)) - { - jtagcore_set_logs_level(*jc, jtagcore_getEnvVarValue(*jc, "LOG_MESSAGES_FILTER_LEVEL")); - } - if (jtagcore_getEnvVar(*jc, "LOG_MESSAGES_FILE_OUTPUT", NULL)) - { - jtagcore_set_logs_file(*jc, jtagcore_getEnvVar(*jc, "LOG_MESSAGES_FILE_OUTPUT", NULL)); - } + jtagcore_set_logs_level(*jc, jtagcore_getEnvVarValue(*jc, "LOG_MESSAGES_FILTER_LEVEL")); + } + if (jtagcore_getEnvVar(*jc, "LOG_MESSAGES_FILE_OUTPUT", NULL)) + { + jtagcore_set_logs_file(*jc, jtagcore_getEnvVar(*jc, "LOG_MESSAGES_FILE_OUTPUT", NULL)); } -end: - return; } void bsexp_deinit(jtag_core *jc, script_ctx *sctx) { - envvar_entry *env = NULL; - - if (NULL != jc) + if (NULL != sctx) { deinit_script(sctx); } if (NULL != jc) { - env = jc->envvar; - deinitEnv(env); + deinitEnv((envvar_entry *)jc->envvar); jtagcore_deinit(jc); } } diff --git a/bs/init.h b/bs/init.h index c19689a..a325413 100644 --- a/bs/init.h +++ b/bs/init.h @@ -3,17 +3,8 @@ #include "config/bs_defines.h" -typedef int (*command_call)(jtag_core *jc, int argc, char *argv[]); - -typedef struct { - char *name; - command_call cmd_call; -} Command; - -extern Command commands[]; - int script_print(script_ctx *sctx, enum MSGTYPE typ, char *string, ...); void bsexp_init(jtag_core **jc, script_ctx **sctx); void bsexp_deinit(jtag_core *jc, script_ctx *sctx); -#endif \ No newline at end of file +#endif diff --git a/bs/main.c b/bs/main.c index 49090c2..5481aa1 100644 --- a/bs/main.c +++ b/bs/main.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -7,123 +6,116 @@ #include #include "config/bs_defines.h" -#include "utils.h" -#include "init.h" -#include "args.h" +#include "config/version.h" +#include "jtag_core/jtag_core.h" #include "script/script.h" +#include "init.h" -// int main(int argc, char *argv[]) { -// int success = 0; -// int n_probes = 0; -// int i = 0; -// int error = 0; -// jtag_core *jc = NULL; +#define PROMPT "bs_explorer> " -// struct args a = {0}; -// struct probe probes[PROBES_MAX_NUM] = {0}; -// unsigned long dev_ids[DEVICES_SCAN_MAX] = {0}; -// int ndevs = 0; +static jtag_core *jc = NULL; +static script_ctx *sctx = NULL; -// success = parse_args(&a, argc, argv); -// if (EXIT_FAILURE == success) exit(EXIT_FAILURE); - -// jc = bsexp_init(); -// if (NULL == jc) { -// error = JTAG_CORE_MEM_ERROR; -// goto err; -// } - -// /* List the probes (and display if asked) */ -// n_probes = list_probes(jc, probes, a.list); - -// i = 0; -// while ((i= a.probe) || (a.probe > MIN(n_probes, PROBES_MAX_NUM))) goto err; -// error = scan(jc, probes[a.probe-1].probe_id, &ndevs, dev_ids, 1); -// break; -// default: -// printf("Unknown command."); -// goto err; -// } -// i++; -// } - -// goto end; - -// err: -// printf("Exited with error (%d).\n", error); - -// end: -// jtagcore_deinit(jc); -// return error; -// } - -jtag_core *jc = NULL; -script_ctx *sctx = NULL; - -// Fonction de gestion du signal -void handle_sigint(int sig) { +static void handle_sigint(int sig) +{ + (void)sig; + fputs("\n", stdout); bsexp_deinit(jc, sctx); exit(0); } -int execute_command(script_ctx *sctx, char *line) +static char *command_generator(const char *text, int state) { - int error = 0; - error = execute_line_script(sctx, line); - return error; + static int idx; + static size_t len; + const char *name; + + if (!state) { + idx = 0; + len = strlen(text); + } + + while ((name = script_commands_list[idx].command) != NULL) { + idx++; + if (strncmp(name, text, len) == 0) { + return strdup(name); + } + } + return NULL; } -int main() +static char **bs_completion(const char *text, int start, int end) +{ + (void)end; + rl_attempted_completion_over = 0; + if (start == 0) { + return rl_completion_matches(text, command_generator); + } + return NULL; +} + +static int is_blank(const char *s) +{ + while (*s) { + if (*s != ' ' && *s != '\t') return 0; + s++; + } + return 1; +} + +static void print_banner(void) +{ + int n_drv = jtagcore_get_number_of_probes_drv(jc); + + printf("\n"); + printf(" Boundary Scan Explorer " APP_VER_STR(APP_VER) "\n"); + printf(" Based on Viveris jtag-boundary-scanner\n"); + printf("\n"); + printf(" %d probe driver(s) available.\n", n_drv); + printf(" Type 'help' or '?' to list commands, 'exit' or Ctrl-D to quit.\n"); + printf(" completes commands.\n"); + printf("\n"); +} + +int main(void) { char *line = NULL; int error = 0; - int cmd_argc = 0; bsexp_init(&jc, &sctx); - script_print(sctx, MSG_NONE, "\n"); - if ((NULL == jc) || (NULL == sctx)) - { - error = JTAG_CORE_MEM_ERROR; - printf("JTAG Core initialization failed!\n"); - goto err_no_deinit; + if (NULL == jc || NULL == sctx) { + fputs("JTAG Core initialization failed!\n", stderr); + return JTAG_CORE_MEM_ERROR; } signal(SIGINT, handle_sigint); - while (1) - { - line = readline("bs_explorer> "); - if (line == NULL) - { + rl_attempted_completion_function = bs_completion; + rl_readline_name = "bs_explorer"; + + print_banner(); + + while ((line = readline(PROMPT)) != NULL) { + if (is_blank(line)) { + free(line); + continue; + } + if (strcmp(line, "exit") == 0 || strcmp(line, "quit") == 0) { + free(line); break; } - if (*line) - { - add_history(line); - } - if (strcmp(line, "exit") == 0) { - break; - } - if (strcmp(line, "quit") == 0) { - break; - } - error = execute_command(sctx, line); - if (0 == error) - { - } - else - { + add_history(line); + + error = execute_line_script(sctx, line); + if (error != JTAG_CORE_NO_ERROR && error != JTAG_CORE_NOT_FOUND) { printf("Command failed with code: %d\n", error); } + free(line); + } + + if (line == NULL) { + fputs("\n", stdout); } -err_no_deinit: - exit(error); -err: - printf("Failed with error (%d)", error); -end: bsexp_deinit(jc, sctx); - exit(error); -} \ No newline at end of file + return 0; +} diff --git a/bs/utils.h b/bs/utils.h deleted file mode 100644 index 284df5c..0000000 --- a/bs/utils.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _UTILS_H -#define _UTILS_H - - -#define MAX_LINE 1024 -#define MAX_ARGS 16 -#define PROBES_MAX_NUM 16 -#define PROBE_NAME_SIZE 64 -#define DEVICES_SCAN_MAX 32 - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - -#endif \ No newline at end of file