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
6064static 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+
62312static 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
387645static 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
471731static 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
508768static 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