Skip to content

Commit

Permalink
ux: Introduce progress indicator
Browse files Browse the repository at this point in the history
Not providing any user feedback during flashing is not very user
friendly, in particular when, in some situations, it can take minutes to
flash very large files.

Introduce a progress bar for the flashing and patching steps to provide
such feedback to the user, when stdout is determined to be a terminal of
sufficient size (i.e. tools programmatically invoking qdl should see no
difference).

Signed-off-by: Bjorn Andersson <[email protected]>
  • Loading branch information
quic-bjorande authored and andersson committed Dec 20, 2024
1 parent a0f9779 commit 8c32a74
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 1 deletion.
4 changes: 3 additions & 1 deletion firehose.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,8 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
}

left -= chunk_size;

ux_progress("%s", num_sectors - left, num_sectors, program->label);
}

t = time(NULL) - t0;
Expand Down Expand Up @@ -565,7 +567,7 @@ static int firehose_apply_patch(struct qdl_device *qdl, struct patch *patch)
xmlDoc *doc;
int ret;

ux_info("applying patch \"%s\"\n", patch->what);
ux_debug("applying patch \"%s\"\n", patch->what);

doc = xmlNewDoc((xmlChar*)"1.0");
root = xmlNewNode(NULL, (xmlChar*)"data");
Expand Down
12 changes: 12 additions & 0 deletions patch.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <unistd.h>

#include "patch.h"
#include "qdl.h"
Expand Down Expand Up @@ -100,17 +101,28 @@ int patch_load(const char *patch_file)
int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch))
{
struct patch *patch;
unsigned int count = 0;
unsigned int idx = 0;
int ret;

for (patch = patches; patch; patch = patch->next) {
if (!strcmp(patch->filename, "DISK"))
count++;
}

for (patch = patches; patch; patch = patch->next) {
if (strcmp(patch->filename, "DISK"))
continue;

ret = apply(qdl, patch);
if (ret)
return ret;

ux_progress("Applying patches", idx++, count);
}

ux_info("%d patches applied\n", idx);

return 0;
}

Expand Down
2 changes: 2 additions & 0 deletions qdl.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ int main(int argc, char **argv)
return 1;
}

ux_init();

prog_mbn = argv[optind++];

do {
Expand Down
2 changes: 2 additions & 0 deletions qdl.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ void print_hex_dump(const char *prefix, const void *buf, size_t len);
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
const char *attr_as_string(xmlNode *node, const char *attr, int *errors);

void ux_init(void);
void ux_err(const char *fmt, ...);
void ux_info(const char *fmt, ...);
void ux_log(const char *fmt, ...);
void ux_debug(const char *fmt, ...);
void ux_progress(const char *fmt, unsigned int value, unsigned int size, ...);

extern bool qdl_debug;

Expand Down
88 changes: 88 additions & 0 deletions ux.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
#include <stdarg.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <unistd.h>

#include "qdl.h"

#define MIN(x, y) ((x) < (y) ? (x) : (y))

#define UX_PROGRESS_REFRESH_RATE 10
#define UX_PROGRESS_SIZE_MAX 120

static const char * const progress_hashes = "########################################################################################################################";
static const char * const progress_dashes = "------------------------------------------------------------------------------------------------------------------------";

static unsigned int ux_width;
static unsigned int ux_cur_line_length;

/*
* Levels of output:
*
Expand All @@ -11,10 +25,33 @@
* debug: protocol logs
*/

/* Clear ux_cur_line_length characters of the progress bar from the screen */
static void ux_clear_line(void)
{
if (!ux_cur_line_length)
return;

printf("%*s\r", ux_cur_line_length, "");
fflush(stdout);
ux_cur_line_length = 0;
}

void ux_init(void)
{
struct winsize w;
int ret;

ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (!ret)
ux_width = MIN(w.ws_col, UX_PROGRESS_SIZE_MAX);
}

void ux_err(const char *fmt, ...)
{
va_list ap;

ux_clear_line();

va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
Expand All @@ -25,6 +62,8 @@ void ux_info(const char *fmt, ...)
{
va_list ap;

ux_clear_line();

va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
Expand All @@ -38,6 +77,8 @@ void ux_log(const char *fmt, ...)
if (!qdl_debug)
return;

ux_clear_line();

va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
Expand All @@ -51,8 +92,55 @@ void ux_debug(const char *fmt, ...)
if (!qdl_debug)
return;

ux_clear_line();

va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
fflush(stdout);
}

void ux_progress(const char *fmt, unsigned int value, unsigned int max, ...)
{
static struct timeval last_progress_update;
unsigned long elapsed_us;
unsigned int bar_length;
unsigned int bars;
unsigned int dashes;
struct timeval now;
char task_name[32];
float percent;
va_list ap;

/* Don't print progress is window is too narrow, or if stdout is redirected */
if (ux_width < 30)
return;

/* Avoid updating the console more than UX_PROGRESS_REFRESH_RATE per second */
if (last_progress_update.tv_sec) {
gettimeofday(&now, NULL);
elapsed_us = (now.tv_sec - last_progress_update.tv_sec) * 1000000 +
(now.tv_usec - last_progress_update.tv_usec);

if (elapsed_us < (1000000 / UX_PROGRESS_REFRESH_RATE))
return;
}

va_start(ap, max);
vsnprintf(task_name, sizeof(task_name), fmt, ap);
va_end(ap);

bar_length = ux_width - (20 + 4 + 6);
percent = (float)value / max;
bars = percent * bar_length;
dashes = bar_length - bars;

printf("%-20.20s [%.*s%.*s] %1.2f%%%n\r", task_name,
bars, progress_hashes,
dashes, progress_dashes,
percent * 100,
&ux_cur_line_length);
fflush(stdout);

gettimeofday(&last_progress_update, NULL);
}

0 comments on commit 8c32a74

Please sign in to comment.