Skip to content

Commit b8979de

Browse files
committed
cdba: Add progress bar
Uploading large imaged, in particular during flashing, can take time. Provide user feedback in the form of a progress bar. Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
1 parent 12ceef7 commit b8979de

1 file changed

Lines changed: 274 additions & 8 deletions

File tree

cdba.c

Lines changed: 274 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <sys/types.h>
1111
#include <sys/uio.h>
1212
#include <sys/wait.h>
13+
#include <sys/ioctl.h>
1314
#include <alloca.h>
1415
#include <err.h>
1516
#include <errno.h>
@@ -53,12 +54,261 @@ struct tx_item {
5354
uint16_t len;
5455

5556
int fd;
57+
char *upload_name;
58+
size_t upload_size;
59+
bool upload_eof;
5660

5761
uint8_t payload[];
5862
};
5963

6064
static struct list_head tx_queue = LIST_INIT(tx_queue);
6165

66+
enum upload_type {
67+
UPLOAD_NONE,
68+
UPLOAD_FASTBOOT,
69+
UPLOAD_EDL,
70+
UPLOAD_MIXED,
71+
};
72+
73+
struct upload_progress {
74+
bool enabled;
75+
bool active;
76+
77+
size_t total_bytes;
78+
size_t sent_bytes;
79+
size_t line_len;
80+
unsigned int tty_width;
81+
enum upload_type type;
82+
char name[64];
83+
84+
struct timeval last_update;
85+
};
86+
87+
static struct upload_progress upload_progress;
88+
89+
static void upload_progress_render(bool force);
90+
91+
static const char *upload_type_name(enum upload_type type)
92+
{
93+
switch (type) {
94+
case UPLOAD_FASTBOOT:
95+
return "fastboot";
96+
case UPLOAD_EDL:
97+
return "edl";
98+
case UPLOAD_MIXED:
99+
return "mixed";
100+
default:
101+
return "upload";
102+
}
103+
}
104+
105+
static bool is_upload_msg(uint8_t type)
106+
{
107+
return type == MSG_FASTBOOT_DOWNLOAD || type == MSG_EDL_DOWNLOAD;
108+
}
109+
110+
static enum upload_type msg_upload_type(uint8_t type)
111+
{
112+
if (type == MSG_FASTBOOT_DOWNLOAD)
113+
return UPLOAD_FASTBOOT;
114+
if (type == MSG_EDL_DOWNLOAD)
115+
return UPLOAD_EDL;
116+
117+
return UPLOAD_NONE;
118+
}
119+
120+
static const char *path_basename(const char *path)
121+
{
122+
const char *base;
123+
124+
base = strrchr(path, '/');
125+
if (!base || !base[1])
126+
return path;
127+
128+
return base + 1;
129+
}
130+
131+
static void upload_progress_clear_line(void)
132+
{
133+
char spaces[160];
134+
size_t to_clear;
135+
136+
if (!upload_progress.enabled || !upload_progress.line_len)
137+
return;
138+
139+
to_clear = MIN(upload_progress.line_len, sizeof(spaces));
140+
memset(spaces, ' ', to_clear);
141+
write(STDERR_FILENO, "\r", 1);
142+
write(STDERR_FILENO, spaces, to_clear);
143+
write(STDERR_FILENO, "\r", 1);
144+
upload_progress.line_len = 0;
145+
}
146+
147+
static void upload_progress_write(int fd, const void *buf, size_t len)
148+
{
149+
upload_progress_clear_line();
150+
write(fd, buf, len);
151+
upload_progress_render(true);
152+
}
153+
154+
static void upload_progress_render(bool force)
155+
{
156+
struct timeval now;
157+
unsigned long elapsed_us;
158+
char bar[40];
159+
char line[192];
160+
double percent;
161+
double sent_mb;
162+
double total_mb;
163+
size_t filled;
164+
size_t empty;
165+
size_t line_len;
166+
int line_n;
167+
int bar_n;
168+
169+
if (!upload_progress.enabled || !upload_progress.active || !upload_progress.total_bytes)
170+
return;
171+
172+
if (!force && upload_progress.last_update.tv_sec) {
173+
gettimeofday(&now, NULL);
174+
elapsed_us = (now.tv_sec - upload_progress.last_update.tv_sec) * 1000000 +
175+
(now.tv_usec - upload_progress.last_update.tv_usec);
176+
if (elapsed_us < 100000)
177+
return;
178+
}
179+
180+
percent = (double)upload_progress.sent_bytes / upload_progress.total_bytes;
181+
if (percent > 1.0)
182+
percent = 1.0;
183+
184+
filled = percent * (sizeof(bar) - 1);
185+
empty = (sizeof(bar) - 1) - filled;
186+
187+
bar_n = snprintf(bar, sizeof(bar), "%.*s%.*s",
188+
(int)filled,
189+
"#######################################",
190+
(int)empty,
191+
"---------------------------------------");
192+
if (bar_n < 0)
193+
return;
194+
195+
sent_mb = (double)upload_progress.sent_bytes / 1000000.0;
196+
total_mb = (double)upload_progress.total_bytes / 1000000.0;
197+
198+
line_n = snprintf(line, sizeof(line), "%s %6.2f%% [%s] %.2f/%.2f MB",
199+
upload_progress.name[0] ? upload_progress.name :
200+
upload_type_name(upload_progress.type),
201+
percent * 100.0,
202+
bar,
203+
sent_mb, total_mb);
204+
if (line_n < 0)
205+
return;
206+
207+
line_len = MIN((size_t)line_n, sizeof(line) - 1);
208+
if (line_len > upload_progress.tty_width - 1)
209+
line_len = upload_progress.tty_width - 1;
210+
211+
write(STDERR_FILENO, "\r", 1);
212+
write(STDERR_FILENO, line, line_len);
213+
if (upload_progress.line_len > line_len) {
214+
size_t tail = upload_progress.line_len - line_len;
215+
char spaces_tail[160];
216+
217+
tail = MIN(tail, sizeof(spaces_tail));
218+
memset(spaces_tail, ' ', tail);
219+
write(STDERR_FILENO, spaces_tail, tail);
220+
}
221+
upload_progress.line_len = line_len;
222+
223+
gettimeofday(&upload_progress.last_update, NULL);
224+
}
225+
226+
static void upload_progress_finish(void)
227+
{
228+
if (!upload_progress.active)
229+
return;
230+
231+
upload_progress_render(true);
232+
if (upload_progress.line_len)
233+
write(STDERR_FILENO, "\n", 1);
234+
235+
upload_progress.active = false;
236+
upload_progress.type = UPLOAD_NONE;
237+
upload_progress.sent_bytes = 0;
238+
upload_progress.total_bytes = 0;
239+
upload_progress.line_len = 0;
240+
upload_progress.name[0] = '\0';
241+
upload_progress.last_update = (struct timeval){ 0 };
242+
}
243+
244+
static void upload_progress_start(const struct tx_item *item)
245+
{
246+
const char *name = item->upload_name;
247+
size_t max_name;
248+
249+
upload_progress.active = true;
250+
upload_progress.type = msg_upload_type(item->type);
251+
upload_progress.sent_bytes = 0;
252+
upload_progress.total_bytes = item->upload_size;
253+
upload_progress.line_len = 0;
254+
upload_progress.last_update = (struct timeval){ 0 };
255+
256+
if (!name || !name[0])
257+
name = upload_type_name(upload_progress.type);
258+
259+
max_name = sizeof(upload_progress.name) - 1;
260+
snprintf(upload_progress.name, sizeof(upload_progress.name), "%.*s",
261+
(int)max_name, name);
262+
}
263+
264+
static bool upload_progress_is_same_file(const struct tx_item *item)
265+
{
266+
const char *name = item->upload_name;
267+
268+
if (!name || !name[0])
269+
name = upload_type_name(msg_upload_type(item->type));
270+
271+
return upload_progress.active &&
272+
upload_progress.type == msg_upload_type(item->type) &&
273+
!strcmp(upload_progress.name, name);
274+
}
275+
276+
static void upload_progress_sent(const struct tx_item *item)
277+
{
278+
if (!is_upload_msg(item->type))
279+
return;
280+
281+
if (!upload_progress_is_same_file(item)) {
282+
if (upload_progress.active)
283+
upload_progress_finish();
284+
upload_progress_start(item);
285+
}
286+
287+
if (item->len) {
288+
upload_progress.sent_bytes += item->len;
289+
upload_progress_render(false);
290+
}
291+
292+
if (item->upload_eof)
293+
upload_progress_finish();
294+
}
295+
296+
static void upload_progress_init(void)
297+
{
298+
struct winsize w = { };
299+
300+
if (!isatty(STDERR_FILENO))
301+
return;
302+
303+
if (!ioctl(STDERR_FILENO, TIOCGWINSZ, &w) && w.ws_col > 0)
304+
upload_progress.tty_width = w.ws_col;
305+
else
306+
upload_progress.tty_width = 80;
307+
308+
upload_progress.tty_width = MIN(upload_progress.tty_width, 120);
309+
upload_progress.enabled = upload_progress.tty_width >= 60;
310+
}
311+
62312
static struct termios *tty_unbuffer(void)
63313
{
64314
static struct termios orig_tios;
@@ -198,14 +448,20 @@ static void cdba_queue_data(int type, size_t len, const void *buf)
198448
list_append(&tx_queue, &item->node);
199449
}
200450

201-
static void cdba_queue_fd(int type, size_t len, int fd)
451+
static void cdba_queue_fd(int type, size_t len, int fd,
452+
const char *upload_name, size_t upload_size,
453+
bool upload_eof)
202454
{
203455
struct tx_item *item;
204456

205457
item = calloc(1, sizeof(*item) + len);
206458
item->type = type;
207459
item->len = len;
208460
item->fd = fd;
461+
if (upload_name)
462+
item->upload_name = strdup(upload_name);
463+
item->upload_size = upload_size;
464+
item->upload_eof = upload_eof;
209465

210466
list_append(&tx_queue, &item->node);
211467
}
@@ -379,9 +635,11 @@ static void request_fastboot_files(void)
379635

380636
for (offset = 0; offset < sb.st_size; offset += TX_DATA_CHUNK_SIZE) {
381637
len = MIN(TX_DATA_CHUNK_SIZE, sb.st_size - offset);
382-
cdba_queue_fd(MSG_FASTBOOT_DOWNLOAD, len, fd);
638+
cdba_queue_fd(MSG_FASTBOOT_DOWNLOAD, len, fd,
639+
path_basename(fastboot_file), sb.st_size, false);
383640
}
384-
cdba_queue_fd(MSG_FASTBOOT_DOWNLOAD, 0, fd);
641+
cdba_queue_fd(MSG_FASTBOOT_DOWNLOAD, 0, fd,
642+
path_basename(fastboot_file), sb.st_size, true);
385643
}
386644

387645
static void edl_submit_one(struct edl_file *edl)
@@ -399,9 +657,11 @@ static void edl_submit_one(struct edl_file *edl)
399657

400658
for (offset = 0; offset < sb.st_size; offset += TX_DATA_CHUNK_SIZE) {
401659
len = MIN(TX_DATA_CHUNK_SIZE, sb.st_size - offset);
402-
cdba_queue_fd(MSG_EDL_DOWNLOAD, len, fd);
660+
cdba_queue_fd(MSG_EDL_DOWNLOAD, len, fd,
661+
path_basename(edl->filename), sb.st_size, false);
403662
}
404-
cdba_queue_fd(MSG_EDL_DOWNLOAD, 0, fd);
663+
cdba_queue_fd(MSG_EDL_DOWNLOAD, 0, fd,
664+
path_basename(edl->filename), sb.st_size, true);
405665

406666
cdba_queue_data(MSG_EDL_WRITE, strlen(edl->target) + 1, edl->target);
407667
}
@@ -465,7 +725,7 @@ static void handle_list_devices(const void *data, size_t len)
465725
board = alloca(len + 1);
466726
memcpy(board, data, len);
467727
board[len] = '\n';
468-
write(STDOUT_FILENO, board, len + 1);
728+
upload_progress_write(STDOUT_FILENO, board, len + 1);
469729
}
470730

471731
static void handle_board_info(const void *data, size_t len)
@@ -475,7 +735,7 @@ static void handle_board_info(const void *data, size_t len)
475735
info = alloca(len + 1);
476736
memcpy(info, data, len);
477737
info[len] = '\n';
478-
write(STDOUT_FILENO, info, len + 1);
738+
upload_progress_write(STDOUT_FILENO, info, len + 1);
479739

480740
quit = true;
481741
}
@@ -502,7 +762,7 @@ static void handle_console(const void *data, size_t len)
502762
}
503763
}
504764

505-
write(STDOUT_FILENO, data, len);
765+
upload_progress_write(STDOUT_FILENO, data, len);
506766
}
507767

508768
static bool auto_power_on;
@@ -744,6 +1004,7 @@ int main(int argc, char **argv)
7441004
if (ret)
7451005
err(1, "failed to connect to \"%s\"", host);
7461006

1007+
upload_progress_init();
7471008
orig_tios = tty_unbuffer();
7481009

7491010
timeout_total_tv = get_timeout(timeout_total);
@@ -836,6 +1097,7 @@ int main(int argc, char **argv)
8361097
write(STDERR_FILENO, blue, sizeof(blue) - 1);
8371098
write(STDERR_FILENO, buf, n);
8381099
write(STDERR_FILENO, reset, sizeof(reset) - 1);
1100+
upload_progress_render(true);
8391101

8401102
bump_inactivity_timer = true;
8411103
}
@@ -860,8 +1122,10 @@ int main(int argc, char **argv)
8601122
n = cdba_tx_one(ssh_fds[0], tx_item);
8611123
if (n < 0)
8621124
err(1, "failed to write to SSH pipe");
1125+
upload_progress_sent(tx_item);
8631126

8641127
list_del(&tx_item->node);
1128+
free(tx_item->upload_name);
8651129
free(tx_item);
8661130

8671131
bump_inactivity_timer = true;
@@ -881,6 +1145,8 @@ int main(int argc, char **argv)
8811145
printf("Waiting for ssh to finish\n");
8821146

8831147
wait(NULL);
1148+
upload_progress_finish();
1149+
upload_progress_clear_line();
8841150

8851151
tty_reset(orig_tios);
8861152

0 commit comments

Comments
 (0)