1 /*
2 * vim:tw=80:ai:tabstop=4:softtabstop=4:shiftwidth=4:expandtab
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * (C) Copyright Kevin Timmerman 2007
19 * (C) Copyright Phil Dibowitz 2007
20 */
21
22 /* Platform-agnostic includes */
23 #define _SVID_SOURCE
24 #include <getopt.h>
25 #include <libconcord.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #ifdef _WIN32
31 /* Windows includes*/
32 #include <conio.h>
33 #include <winsock.h>
34
35 /*
36 * (see below) Windows does not need this, since _getch already
37 * does what we need - just make set_canon do nothing
38 * Note that _getch returns '\r' when user hits the <Enter> key
39 */
set_canon(int flag)40 int set_canon(int flag)
41 {
42 return(1);
43 }
44
45 #define read_key _getch
46 #define ENTER_KEY '\r'
47
48 #else
49 /* NON-Windows */
50
51 #include <strings.h>
52 #include <libgen.h>
53 #include <unistd.h>
54 #include <termios.h>
55
56 /*
57 * set_canon in LINUX modifies stdin such that getchar behaves like
58 * _getch in Windows, i.e. the next key is returned immediately.
59 * Note that getchar returns '\n' when user hits the <Enter> key
60 */
set_canon(int flag)61 int set_canon(int flag)
62 {
63 struct termios t;
64
65 tcgetattr(0, &t);
66 if (flag) {
67 t.c_lflag |= ICANON;
68 } else {
69 t.c_lflag &= ~ICANON;
70 }
71 tcsetattr(0, TCSANOW, &t);
72 return(1);
73 }
74 /* Thus, we can define: */
75 #define read_key getchar
76 #define ENTER_KEY '\n'
77
78 #endif
79
80 #define MAX_WAIT_FOR_BOOT 5
81 #define WAIT_FOR_BOOT_SLEEP 5
82
83 #if defined(__APPLE__) || defined(__FreeBSD__)
84 #include <libgen.h>
85 #endif
86
87 #define DEFAULT_CONFIG_FILENAME "Update.EZHex"
88 #define DEFAULT_CONFIG_FILENAME_BIN "update.bin"
89 #define DEFAULT_FW_FILENAME "firmware.EZUp"
90 #define DEFAULT_FW_FILENAME_BIN "firmware.bin"
91 #define DEFAULT_SAFE_FILENAME "safe.bin"
92
93 const char * const VERSION = "1.2";
94
95 struct options_t {
96 int binary;
97 int verbose;
98 int noweb;
99 int direct;
100 int noreset;
101 int force;
102 };
103
104 enum {
105 MODE_UNSET,
106 MODE_CONNECTIVITY,
107 MODE_DUMP_CONFIG,
108 MODE_WRITE_CONFIG,
109 MODE_DUMP_FIRMWARE,
110 MODE_WRITE_FIRMWARE,
111 MODE_DUMP_SAFEMODE,
112 MODE_HELP,
113 MODE_LEARN_IR,
114 MODE_RESET,
115 MODE_GET_TIME,
116 MODE_SET_TIME,
117 MODE_PRINT_INFO,
118 MODE_VERSION
119 };
120
121 /*
122 * Tack a colon onto the end, and then right-pad it out to 23-chars. This means
123 * we can have strings of up to 21 chars (21 chars, 1 colon, and 1 space before
124 * the percentages start).
125 */
print_stage_name(int stage_id)126 void print_stage_name(int stage_id)
127 {
128 const char *stage;
129 char output[24]; /* 21 + 1 + 1 + \0 */
130 stage = lc_cb_stage_str(stage_id);
131 strcpy(output, stage);
132 strcat(output, ":");
133 /* -22 is a 23-char-wide left-aligned string */
134 printf("%-22s", output);
135 return;
136 }
137
138 /*
139 * Start callbacks
140 *
141 * stage_id => integer of the stage
142 * count => number of times we've been invoked
143 * curr => current position (bytes, precent, etc.)
144 * total => the number 'curr' is trying to reach
145 * counter_type => what 'curr' represents (bytes, percent, etc.)
146 * arg => an extra value we can ask libconcord to pass us.
147 * stages => the array of stages that will be performed
148 */
cb_print_percent_status(uint32_t stage_id,uint32_t count,uint32_t curr,uint32_t total,uint32_t counter_type,void * arg,const uint32_t * stages)149 void cb_print_percent_status(uint32_t stage_id, uint32_t count, uint32_t curr,
150 uint32_t total, uint32_t counter_type, void *arg,
151 const uint32_t *stages)
152 {
153 if (stage_id == LC_CB_STAGE_NUM_STAGES) {
154 #if _DEBUG
155 printf("Num stages: %d\n", count);
156 #endif
157 return;
158 }
159
160 if (count != 0) {
161 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
162 } else {
163 print_stage_name(stage_id);
164 }
165
166 if (counter_type == LC_CB_COUNTER_TYPE_BYTES) {
167 printf("%3i%% %4i KiB", curr*100/total, curr>>10);
168 } else {
169 printf("%3i%% ", curr*100/total);
170 }
171
172 if (curr == total) {
173 printf(" done\n");
174 }
175
176 fflush(stdout);
177 }
178
179 /*
180 * Start helper functions
181 */
direct_warning()182 void direct_warning()
183 {
184 printf("WARNING: You have requested direct mode. This only affects\n");
185 printf("\t firmware updates. To do this safely you MUST be in\n");
186 printf("\t SAFEMODE! This is only for those devices where we\n");
187 printf("\t don't yet support live firmware updates. See the docs\n");
188 printf("\t for information on how to boot into safemode.\n");
189 printf("\t Press <enter> to continue.\n");
190 getchar();
191 }
192
set_mode(int * mode,int val)193 void set_mode(int *mode, int val)
194 {
195 if (*mode == MODE_UNSET) {
196 *mode = val;
197 } else {
198 fprintf(stderr,
199 "ERROR: More than one mode specified. Please specify");
200 fprintf(stderr, " only one.\n");
201 exit(1);
202 }
203 }
204
print_ir_burst(uint32_t length)205 void print_ir_burst(uint32_t length)
206 {
207 if (length < 250) {
208 printf("|");
209 } else if (length < 1000) {
210 printf("#");
211 } else {
212 printf("##");
213 }
214 }
215
print_ir_space(uint32_t length)216 void print_ir_space(uint32_t length)
217 {
218 if (length < 250) {
219 printf(".");
220 } else if (length < 1000) {
221 printf("_");
222 } else if (length < 10000) {
223 printf("__");
224 } else {
225 printf("\n");
226 }
227 }
228
print_received_ir_signal(uint32_t carrier_clock,uint32_t * ir_signal,uint32_t ir_signal_length,struct options_t * options)229 void print_received_ir_signal(uint32_t carrier_clock, uint32_t *ir_signal,
230 uint32_t ir_signal_length, struct options_t *options)
231 {
232 uint32_t index;
233 printf("\nASCII-graph of received IR signal:\n");
234 for (index = 0; index < ir_signal_length; index += 2){
235 print_ir_burst(ir_signal[index]);
236 print_ir_space(ir_signal[index+1]);
237 }
238 printf("\n");
239 printf("Carrier clock : %u Hz\n", carrier_clock);
240 printf("Total mark/space pairs : %u\n\n", ir_signal_length/2);
241 #ifdef _DEBUG
242 /*
243 * full dump of new IR signal:
244 */
245 for (index=0; index < ir_signal_length; index += 2){
246 printf("\tP:%6u\tS:%6u\n", ir_signal[index],
247 ir_signal[index+1]);
248 }
249 #endif
250 }
251
get_cmd(char * prompt,char * allowed,char def)252 char get_cmd(char *prompt, char *allowed, char def) {
253 char result = 0;
254 char got_key;
255 uint32_t index;
256 set_canon(0);
257 while ( result == 0 ) {
258 printf("%s (%c)?", prompt, def);
259 got_key = read_key();
260 printf("\n");
261 if (got_key == ENTER_KEY) {
262 result = def;
263 } else {
264 for (index = 0; index < strlen(allowed); index++) {
265 if (allowed[index] == got_key) {
266 result = got_key;
267 break;
268 }
269 }
270 }
271 }
272 set_canon(1);
273 return result;
274 }
275
mode_string(int mode)276 char *mode_string(int mode)
277 {
278 switch (mode) {
279 case MODE_CONNECTIVITY:
280 return "Connectivity Check";
281 break;
282 case MODE_WRITE_CONFIG:
283 return "Write Configuration";
284 break;
285 case MODE_WRITE_FIRMWARE:
286 return "Write Firmware";
287 break;
288 case MODE_LEARN_IR:
289 return "Learn IR";
290 break;
291 default:
292 return "Unknown";
293 break;
294 }
295 }
296
report_mode_mismatch(int mode,int file_mode,int force)297 void report_mode_mismatch(int mode, int file_mode, int force)
298 {
299 if (force) {
300 fprintf(stderr, "WARNING:");
301 } else {
302 fprintf(stderr, "ERROR:");
303 }
304 fprintf(stderr, " Requested mode is: %s\n", mode_string(mode));
305 fprintf(stderr, " but file detected as: %s\n",
306 mode_string(file_mode));
307 fprintf(stderr, "Try not specifying a mode at all, I will figure it");
308 fprintf(stderr, " out for you.\n");
309 }
310
311 /*
312 * In certain cases we don't require filenames, this provides
313 * sane defaults.
314 */
populate_default_filename(int mode,int binary,char ** file_name)315 void populate_default_filename(int mode, int binary, char **file_name)
316 {
317 switch (mode) {
318 case MODE_DUMP_CONFIG:
319 if (binary) {
320 *file_name = (char *)
321 strdup(DEFAULT_CONFIG_FILENAME_BIN);
322 } else {
323 *file_name = (char *)
324 strdup(DEFAULT_CONFIG_FILENAME);
325 }
326 break;
327
328 case MODE_DUMP_FIRMWARE:
329 if (binary) {
330 *file_name = (char *)
331 strdup(DEFAULT_FW_FILENAME_BIN);
332 } else {
333 *file_name = (char *)
334 strdup(DEFAULT_FW_FILENAME);
335 }
336 break;
337
338 case MODE_DUMP_SAFEMODE:
339 *file_name = (char *) strdup(DEFAULT_SAFE_FILENAME);
340 break;
341 }
342 }
343
344 /*
345 * Parse our options.
346 */
parse_options(struct options_t * options,int * mode,char ** file_name,int argc,char * argv[])347 void parse_options(struct options_t *options, int *mode, char **file_name,
348 int argc, char *argv[])
349 {
350 int tmpint, option_index;
351
352 static struct option long_options[] = {
353 {"binary", no_argument, 0, 'b'},
354 {"dump-config", optional_argument, 0, 'c'},
355 {"write-config", required_argument, 0, 'C'},
356 {"direct", no_argument, 0, 'd'},
357 {"force", no_argument, 0, 0},
358 {"dump-firmware", optional_argument, 0, 'f'},
359 {"write-firmware", required_argument, 0, 'F'},
360 {"help", no_argument, 0, 'h'},
361 {"print-remote-info", no_argument, 0, 'i'},
362 {"learn-ir", required_argument, 0, 'l'},
363 {"reset", no_argument, 0, 'r'},
364 {"no-reset", no_argument, 0, 'R'},
365 {"dump-safemode", optional_argument, 0, 's'},
366 {"connectivity-test", required_argument, 0, 't'},
367 {"get-time", no_argument, 0, 'k' },
368 {"set-time", no_argument, 0, 'K' },
369 {"verbose", no_argument, 0, 'v'},
370 {"version", no_argument, 0, 'V'},
371 {"no-web", no_argument, 0, 'w'},
372 {0,0,0,0} /* terminating null entry */
373 };
374
375 (*options).verbose = 0;
376 (*options).binary = 0;
377 (*options).noweb = 0;
378 (*options).direct = 0;
379 (*options).force = 0;
380 (*options).noreset = 0;
381
382 *mode = MODE_UNSET;
383
384 tmpint = 0;
385 option_index = 0;
386
387 while ((tmpint = getopt_long(argc, argv, "bc::C:df::F:hil:rs::t:kKvVw",
388 long_options, &option_index)) != EOF) {
389 switch (tmpint) {
390 case 0:
391 /* Long-only options go here */
392 if (!strcmp(long_options[option_index].name, "force")) {
393 (*options).force = 1;
394 break;
395 }
396 break;
397 case 'b':
398 (*options).binary = 1;
399 break;
400 case 'c':
401 set_mode(mode, MODE_DUMP_CONFIG);
402 if (optarg != NULL) {
403 *file_name = optarg;
404 }
405 break;
406 case 'C':
407 if (optarg == NULL) {
408 fprintf(stderr, "Missing config file to read from.\n");
409 exit(1);
410 }
411 set_mode(mode, MODE_WRITE_CONFIG);
412 *file_name = optarg;
413 break;
414 case 'd':
415 (*options).direct = 1;
416 break;
417 case 'f':
418 set_mode(mode, MODE_DUMP_FIRMWARE);
419 if (optarg != NULL)
420 *file_name = optarg;
421 break;
422 case 'F':
423 if (optarg == NULL) {
424 fprintf(stderr, "Missing firmware file to read from.\n");
425 exit(1);
426 }
427 set_mode(mode, MODE_WRITE_FIRMWARE);
428 *file_name = optarg;
429 break;
430 case 'h':
431 set_mode(mode, MODE_HELP);
432 break;
433 case 'i':
434 set_mode(mode, MODE_PRINT_INFO);
435 break;
436 case 'l':
437 if (optarg == NULL) {
438 fprintf(stderr, "Missing config file to read from.\n");
439 exit(1);
440 }
441 set_mode(mode, MODE_LEARN_IR);
442 *file_name = optarg;
443 break;
444 case 'r':
445 set_mode(mode, MODE_RESET);
446 break;
447 case 'R':
448 (*options).noreset = 1;
449 break;
450 case 's':
451 set_mode(mode, MODE_DUMP_SAFEMODE);
452 if (optarg != NULL)
453 *file_name = optarg;
454 break;
455 case 't':
456 if (optarg == NULL) {
457 fprintf(stderr, "Missing connectivity file to read from.\n");
458 exit(1);
459 }
460 set_mode(mode, MODE_CONNECTIVITY);
461 *file_name = optarg;
462 break;
463 case 'k':
464 set_mode(mode, MODE_GET_TIME);
465 break;
466 case 'K':
467 set_mode(mode, MODE_SET_TIME);
468 break;
469 case 'v':
470 (*options).verbose = 1;
471 break;
472 case 'V':
473 set_mode(mode, MODE_VERSION);
474 break;
475 case 'w':
476 (*options).noweb = 1;
477 break;
478 default:
479 exit(1);
480 }
481 }
482
483 if (optind + 1 < argc) {
484 fprintf(stderr, "Multiple arguments were");
485 fprintf(stderr, " specified after the options");
486 fprintf(stderr, " - not sure what to do,");
487 fprintf(stderr, " exiting\n");
488 exit(1);
489 } else if (optind < argc) {
490 /* one thing left over, lets hope its a filename */
491 *file_name = argv[optind];
492 }
493
494 }
495
help()496 void help()
497 {
498 printf("There are two ways to invoke this software. The primary");
499 printf(" way is \"automatic\nmode\" where you just pass in a file");
500 printf(" and the software figures out what to\ndo for you:\n");
501 printf("\tconcordance <file>\n\n");
502 printf("Or you can specify what to do:\n");
503 printf("\tconcordance <options>\n\n");
504
505 printf("When specifying options, you must first choose a mode:\n\n");
506
507 printf(" -c, --dump-config [<filename>]\n");
508 printf("\tRead the config from the remote and write it to a file.");
509 printf("\n\tIf no filename is specified, config.EZHex is used.\n\n");
510 printf(" -C, --write-config <filename>\n");
511 printf("\tRead a config from <filename> and write it to the");
512 printf(" remote.\n\n");
513 printf(" -f, --dump-firmware [<filename>]\n");
514 printf("\tRead firmware from the remote and write it to a file.\n");
515 printf("\tIf no filename is specified firmware.EZUp is used.\n\n");
516 printf(" -F, --write-firmware <filename>\n");
517 printf("\tRead firmware from <filename> and write it to the");
518 printf(" remote\n\n");
519 printf(" -h, --help\n");
520 printf("\tPrint this help message and exit.\n\n");
521 printf(" -i, --print-remote-info\n");
522 printf("\tPrint information about the remote. Additional");
523 printf(" information will\n\tbe printed if -v is also used.\n\n");
524 printf(" -k, --get-time\n");
525 printf("\t Get time from the remote\n\n");
526 printf(" -K, --set-time\n");
527 printf("\t Set the remote's time clock\n\n");
528 printf(" -l, --learn-ir <filename>\n");
529 printf("\t Learn IR from other remotes. Use <filename>.\n\n");
530 printf(" -r, --reset\n");
531 printf("\tReset (power-cycle) the remote control\n\n");
532 printf(" -s, --dump-safemode [<filename>]\n");
533 printf("\tRead the safemode firmware from the remote and write it");
534 printf(" to a file.\n\tIf no filename is psecified, safe.bin is");
535 printf(" used.\n\n");
536 printf(" -t, --connectivity-test <filename>\n");
537 printf("\tDo a connectivity test using <filename>\n\n");
538 printf(" -V, --version\n");
539 printf("\t Print the version and exit\n\n");
540
541 printf("NOTE: If using the short options (e.g. -c), then *optional*");
542 printf(" arguments\nmust be adjacent: -c/path/to/file.");
543 printf(" Required arguments don't have this\nlimitation, and can");
544 printf(" be specified -C /path/to/file or -C/path/to/file.\n\n");
545
546 printf("Additionally, you can specify options to adjust the behavior");
547 printf(" of the software:\n\n");
548
549 printf(" -b, --binary-only\n");
550 printf("\tWhen dumping a config or firmware, this specifies to dump");
551 printf(" only the\n\tbinary portion. When use without a specific");
552 printf(" filename, the default\n\tfilename's extension is changed to");
553 printf(" .bin.\n");
554 printf("\tWhen writing a config or firmware, this specifies the");
555 printf(" filename\n\tpassed in has just the binary blob, not the");
556 printf(" XML.\n\n");
557
558 printf(" --force\n");
559 printf("\tForce. This forces concordance to use the file the way\n");
560 printf("\tyou specified, even if it doesn't think that's the kind\n");
561 printf("\tof file it is. This is necessary for files dumped by\n");
562 printf("\tconcordance.\n\n");
563
564 printf(" -R, --no-reset\n");
565 printf("\tFor config or firmware updates, do not reboot the device");
566 printf(" when done.\n\tThis is generally only for debugging.\n\n");
567
568 printf(" -v, --verbose\n");
569 printf("\tEnable verbose output.\n\n");
570
571 printf(" -w, --no-web\n");
572 printf("\tDo not attempt to talk to the website. This is useful");
573 printf(" for\n\tre-programming the remote from a saved file, or for");
574 printf(" debugging.\n\n");
575
576 }
577
578
579 /*
580 * Begin functions to actually do work
581 */
learn_ir_commands(struct options_t * options,lc_callback cb,void * cb_arg)582 int learn_ir_commands(struct options_t *options, lc_callback cb, void *cb_arg)
583 {
584 int err = 0;
585 uint32_t carrier_clock = 0;
586 uint32_t *ir_signal = NULL;
587 uint32_t ir_signal_length = 0;
588 uint32_t index = 0;
589 char **key_names;
590 uint32_t key_names_length = 0;
591 char *post_string = NULL;
592 char user_cmd;
593
594 err = get_key_names(&key_names, &key_names_length);
595 if ((err != 0) || (key_names_length == 0))
596 return err;
597
598 printf("Received file contains %u key names to be learned.\n",
599 key_names_length);
600
601 while (1) {
602 if (index >= key_names_length) {
603 index--;
604 printf("Last key in list!\n");
605 }
606 printf("\nKey name : <%s> : \n", key_names[index]);
607 user_cmd = get_cmd("[L]earn, [N]ext, [P]revious, [Q]uit", "LlHhNnPpQq",
608 'L');
609 err = -1;
610 /* have no code yet */
611 switch (user_cmd) {
612 case 'L':
613 case 'l':
614 /* learn from remote: */
615 printf("press corresponding key ");
616 printf("on original remote within 5 sec:\n");
617 err = learn_from_remote(&carrier_clock,
618 &ir_signal, &ir_signal_length,
619 cb_print_percent_status, NULL);
620 break;
621 case 'P':
622 case 'p':
623 if (index > 0) {
624 index--;
625 } else {
626 printf("First key in list!\n");
627 }
628 break;
629 case 'N':
630 case 'n':
631 index++;
632 break;
633 default:
634 break;
635 }
636 if ( err == 0 ) {
637 /* have new IR code: */
638 if ((*options).verbose) {
639 print_received_ir_signal(carrier_clock,
640 ir_signal, ir_signal_length, options);
641 }
642 err = encode_for_posting(carrier_clock, ir_signal,
643 ir_signal_length, &post_string);
644 /* done with learned signal, free memory: */
645 delete_ir_signal(ir_signal);
646 }
647
648 if ( err == 0 ) {
649 /* have successfully encoded new code: */
650 #ifdef _DEBUG
651 if ((*options).verbose) {
652 printf("%s\n\n", post_string );
653 }
654 #endif
655 if (!(*options).noweb) {
656 user_cmd = get_cmd(
657 "[U]pload new code, [R]etry same key, [N]ext key, [Q]uit",
658 "UuRrNnQq", 'U');
659 } else {
660 /* no upload: just skip to next key */
661 user_cmd = 'N';
662 }
663 switch (user_cmd) {
664 case 'U':
665 case 'u':
666 /*printf("Upload to website: ");
667 cb_print_percent_status(
668 0, 0, 0, 1,
669 LC_CB_COUNTER_TYPE_STEPS, NULL);
670 */
671 err = post_new_code(key_names[index],
672 post_string, cb, cb_arg);
673 if ( err == 0 ) {
674 /*
675 cb_print_percent_status(
676 0, 1, 1, 1,
677 LC_CB_COUNTER_TYPE_STEPS,
678 NULL);
679 printf(" done\n");*/
680 index++;
681 } else {
682 printf(" failed\n");
683 }
684 break;
685 case 'N':
686 case 'n':
687 index++;
688 break;
689 default:
690 break;
691 }
692 /* done, free memory */
693 delete_encoded_signal(post_string);
694 } else {
695 if (err > 0) {
696 fprintf(stderr, "\nERROR:Problemreceiving IR");
697 fprintf(stderr, " signal:\n\t%s \n",
698 lc_strerror(err));
699 }
700 }
701
702 if (user_cmd == 'Q' || user_cmd == 'q') {
703 break;
704 }
705 }
706 /* done, free memory */
707 delete_key_names(key_names, key_names_length);
708 return 0;
709 }
710
dump_config(struct options_t * options,char * file_name,lc_callback cb,void * cb_arg)711 int dump_config(struct options_t *options, char *file_name, lc_callback cb,
712 void *cb_arg)
713 {
714 int err = 0;
715 uint8_t *config;
716 uint32_t size = 0;
717
718 if ((err = read_config_from_remote(&config, &size, cb, NULL))) {
719 return err;
720 }
721
722 if ((err = write_config_to_file(config, size, file_name,
723 (*options).binary))) {
724 return err;
725 }
726
727 delete_blob(config);
728
729 return 0;
730 }
731
print_time(int action)732 void print_time(int action)
733 {
734 static const char * const dow[8] =
735 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "" };
736
737 if (action == 0) {
738 printf("Remote time is ");
739 } else {
740 printf("Remote time has been set to ");
741 }
742
743 printf("%04i/%02i/%02i %s %02i:%02i:%02i %+i %s\n", get_time_year(),
744 get_time_month(), get_time_day(), dow[get_time_dow() & 7],
745 get_time_hour(), get_time_minute(), get_time_second(),
746 get_time_utc_offset(), get_time_timezone());
747 }
748
upload_config(struct options_t * options,lc_callback cb,void * cb_arg)749 int upload_config(struct options_t *options, lc_callback cb, void *cb_arg)
750 {
751 int err;
752
753 /*
754 * Tell the website we're going to start. This, it seems creates a
755 * session object on their side, because if you miss the pre-config
756 * communication, you get a no-session error.
757 */
758 if (!(*options).binary && !(*options).noweb) {
759 if ((err = post_preconfig(cb, cb_arg)))
760 return err;
761 }
762
763 if ((err = update_configuration(cb, cb_arg, (*options).noreset))) {
764 return err;
765 }
766
767 /* Tell the website we're done */
768 if (!(*options).binary && !(*options).noweb) {
769 if ((err = post_postconfig(cb, cb_arg))) {
770 return err;
771 }
772 }
773
774 return 0;
775 }
776
dump_safemode(char * file_name,lc_callback cb,void * cb_arg)777 int dump_safemode(char *file_name, lc_callback cb, void *cb_arg)
778 {
779 uint8_t * safe = 0;
780 uint32_t safe_size;
781 int err = 0;
782
783 if ((err = read_safemode_from_remote(&safe, &safe_size, cb,
784 cb_arg))) {
785 delete_blob(safe);
786 return err;
787 }
788
789 if ((err = write_safemode_to_file(safe, safe_size, file_name))) {
790 delete_blob(safe);
791 return err;
792 }
793
794 delete_blob(safe);
795 return 0;
796 }
797
upload_firmware(struct options_t * options,lc_callback cb,void * cb_arg)798 int upload_firmware(struct options_t *options, lc_callback cb, void *cb_arg)
799 {
800 int err;
801
802 if ((err = is_fw_update_supported((*options).direct))) {
803 fprintf(stderr, "Sorry, firmware upgrades are not yet");
804 fprintf(stderr, " on your remote model yet.\n");
805 return err;
806 }
807
808 if ((*options).direct) {
809 direct_warning();
810 } else {
811 if (is_config_safe_after_fw() != 0) {
812 printf("NOTE: A firmware upgrade, will erase your");
813 printf(" remote's config and you will need to update");
814 printf(" it. You may want to make a backup with -c");
815 printf(" or otherwise just use the website.\n");
816 printf("Press <enter> to continue.\n");
817 getchar();
818 }
819 }
820
821 if ((err = update_firmware(cb, cb_arg, (*options).noreset,
822 (*options).direct)))
823 return err;
824
825 if (!(*options).binary && !(*options).noweb) {
826 if ((err = post_postfirmware(cb, cb_arg)))
827 return err;
828 }
829
830 return 0;
831 }
832
dump_firmware(struct options_t * options,char * file_name,lc_callback cb,void * cb_arg)833 int dump_firmware(struct options_t *options, char *file_name,
834 lc_callback cb, void *cb_arg)
835 {
836 int err = 0;
837 uint8_t *firmware = 0;
838 uint32_t firmware_size;
839
840 if ((err = read_firmware_from_remote(&firmware, &firmware_size, cb,
841 cb_arg))) {
842 delete_blob(firmware);
843 return err;
844 }
845
846 if ((err = write_firmware_to_file(firmware, firmware_size, file_name,
847 (*options).binary))) {
848 delete_blob(firmware);
849 return err;
850 }
851
852 delete_blob(firmware);
853 return 0;
854 }
855
print_version_info(struct options_t * options)856 int print_version_info(struct options_t *options)
857 {
858 const char *cn;
859 int used, total;
860
861 printf(" Model: %s %s", get_mfg(), get_model());
862 cn = get_codename();
863
864 if (strcmp(cn,"")) {
865 printf(" (%s)\n", cn);
866 } else {
867 printf("\n");
868 }
869
870 if ((*options).verbose)
871 printf(" Skin: %i\n", get_skin());
872
873 printf(" Firmware Version: %i.%i\n", get_fw_ver_maj(), get_fw_ver_min());
874
875 if ((*options).verbose)
876 printf(" Firmware Type: %i\n", get_fw_type());
877
878 printf(" Hardware Version: %i.%i.%i\n", get_hw_ver_maj(), get_hw_ver_min(),
879 get_hw_ver_mic());
880
881 if ((*options).verbose) {
882 int size = get_flash_size();
883 printf(" External Flash: ");
884 if (size >> 10 != 0) {
885 printf("%i MiB", size >> 10);
886 } else {
887 printf("%i KiB", size);
888 }
889 printf(" - %02X:%02X %s\n", get_flash_mfg(), get_flash_id(),
890 get_flash_part_num());
891
892 printf(" Architecture: %i\n", get_arch());
893 printf(" Protocol: %i\n", get_proto());
894
895 printf(" Manufacturer: %s\n", get_hid_mfg_str());
896 printf(" Product: %s\n", get_hid_prod_str());
897 printf(" IRL, ORL, FRL: %i, %i, %i\n", get_hid_irl(), get_hid_orl(),
898 get_hid_frl());
899 printf(" USB VID: %04X\n", get_usb_vid());
900 printf(" USB PID: %04X\n", get_usb_pid());
901 printf(" USB Ver: %04X\n", get_usb_bcd());
902
903 /*
904 * Certain MH remotes (but not all) such as the Harmony Touch use a
905 * different serial number format. In this case, mh_get_serial() will
906 * return a non-zero-length string which will have the correct serial.
907 */
908 if (strlen(mh_get_serial()) != 0)
909 printf(" Serial Number: %s\n", mh_get_serial());
910 else
911 printf(" Serial Number: %s\n\t%s\n\t%s\n", get_serial(1),
912 get_serial(2), get_serial(3));
913 }
914
915 used = get_config_bytes_used();
916 total = get_config_bytes_total();
917 printf(" Config Flash Used: %i%% (%i of %i KiB)\n\n",
918 (used*100+99) / total, (used+1023)>>10, (total+1023)>>10);
919
920 return 0;
921 }
922
923 /*
924 * MAIN
925 */
main(int argc,char * argv[])926 int main(int argc, char *argv[])
927 {
928 struct options_t options;
929 char *file_name;
930 int mode, file_mode, err;
931
932 printf("Concordance %s\n", VERSION);
933 printf("Copyright 2007 Kevin Timmerman and Phil Dibowitz\n");
934 printf("This software is distributed under the GPLv3.\n\n");
935
936 file_name = NULL;
937 mode = MODE_UNSET;
938
939 parse_options(&options, &mode, &file_name, argc, argv);
940
941 /*
942 * Handle the few simple modes that don't require any files
943 * to be read in or any realy work to be done first...
944 */
945 if (mode == MODE_VERSION) {
946 exit(0);
947 }
948
949 if (mode == MODE_HELP) {
950 help();
951 exit(0);
952 }
953
954 /*
955 * We used to delay remote initialization until after all the figuring
956 * out the mode and read the file stuff... but with zwave support we
957 * need to know what type of remote we're dealing with early on.
958 */
959
960 err = init_concord();
961 if (err != 0) {
962 fprintf(stderr, "ERROR: Couldn't initializing libconcord: %s\n",
963 lc_strerror(err));
964 exit(1);
965 }
966
967 /*
968 * Alright, at this point, if there's going to be a filename,
969 * we have one, so lets read the file.
970 */
971 if (file_name && (mode != MODE_DUMP_CONFIG && mode != MODE_DUMP_FIRMWARE &&
972 mode != MODE_DUMP_SAFEMODE)) {
973
974 int type;
975 if ((err = read_and_parse_file(file_name, &type))) {
976 fprintf(stderr, "ERROR: Cannot read input file: %s\n", file_name);
977 exit(1);
978 }
979
980 switch (type) {
981 case LC_FILE_TYPE_CONNECTIVITY:
982 file_mode = MODE_CONNECTIVITY;
983 break;
984 case LC_FILE_TYPE_CONFIGURATION:
985 file_mode = MODE_WRITE_CONFIG;
986 break;
987 case LC_FILE_TYPE_FIRMWARE:
988 file_mode = MODE_WRITE_FIRMWARE;
989 break;
990 case LC_FILE_TYPE_LEARN_IR:
991 file_mode = MODE_LEARN_IR;
992 break;
993 default:
994 fprintf(stderr, "ERROR: Couldn't determine filetype");
995 exit(1);
996 break;
997 }
998
999 /*
1000 * If we don't have a mode, lets detect that mode based on
1001 * the file.
1002 */
1003 if (mode == MODE_UNSET) {
1004 mode = file_mode;
1005 } else if (mode != file_mode) {
1006 report_mode_mismatch(mode, file_mode, options.force);
1007 if (!options.force) {
1008 exit(1);
1009 }
1010 }
1011 }
1012
1013 /*
1014 * We have checked the parameters and the input file:
1015 * If we still don't know what to do, failure is the only option:
1016 */
1017 if (mode == MODE_UNSET) {
1018 fprintf(stderr, "ERROR: No mode requested. No work to do.\n");
1019 exit(1);
1020 }
1021
1022 /*
1023 * The is..supported() functions return 0 for yes, so 1 is NO
1024 */
1025 if ((mode == MODE_DUMP_CONFIG && is_config_dump_supported()) ||
1026 ((mode == MODE_DUMP_FIRMWARE || mode == MODE_DUMP_SAFEMODE) &&
1027 is_fw_dump_supported())) {
1028 fprintf(stderr, "Sorry, that mode is not yet supported on your"
1029 " remote by libconcord.\n");
1030 exit(1);
1031 }
1032
1033 /*
1034 * In a few special cases, we populate a default filename. NEVER
1035 * if we're are writing to the device, however.
1036 *
1037 * But wait - we already read in the file! Indeed, if we have a default
1038 * filename, then we'll be writing to the file, not reading it, which
1039 * is why we do this down here.
1040 */
1041 if (!file_name) {
1042 populate_default_filename(mode, options.binary, &file_name);
1043 }
1044
1045
1046 /*
1047 * If we're in reset mode, not only do we not need to read and print
1048 * data from the device, but it may not even be fully functional, so
1049 * we probably want to do as little as possible. This we do this
1050 * near the beginning instead of the SWITCH below.
1051 *
1052 * Note that we don't look at or save the return value of reset_remote()
1053 * because the usb write for reset doesn't always return successful on
1054 * all remotes even though it works.
1055 */
1056 if (mode == MODE_RESET) {
1057 printf("Resetting...\n");
1058 reset_remote(NULL, NULL);
1059 goto cleanup;
1060 }
1061
1062 /*
1063 * Get and print all the version info
1064 */
1065
1066 err = get_identity(cb_print_percent_status, NULL);
1067 if (err != 0 && err != LC_ERROR_INVALID_CONFIG) {
1068 fprintf(stderr, "ERROR: failed to requesting identity\n");
1069 goto cleanup;
1070 }
1071 if (err == LC_ERROR_INVALID_CONFIG) {
1072 printf("WARNING: Invalid config found.\n");
1073 }
1074
1075 /*
1076 * Now do whatever we've been asked to do
1077 */
1078
1079 switch (mode) {
1080 case MODE_PRINT_INFO:
1081 err = print_version_info(&options);
1082 break;
1083
1084 case MODE_CONNECTIVITY:
1085 if (!options.noweb) {
1086 err = post_connect_test_success(cb_print_percent_status, NULL);
1087 }
1088 break;
1089
1090 case MODE_DUMP_CONFIG:
1091 err = dump_config(&options, file_name, cb_print_percent_status,
1092 NULL);
1093 if (err != 0) {
1094 printf("Failed to dump config: %s\n", lc_strerror(err));
1095 }
1096 break;
1097
1098 case MODE_WRITE_CONFIG:
1099 err = upload_config(&options, cb_print_percent_status, NULL);
1100 if (err != 0) {
1101 printf("Failed to upload config: %s\n", lc_strerror(err));
1102 }
1103 break;
1104
1105 case MODE_DUMP_FIRMWARE:
1106 err = dump_firmware(&options, file_name, cb_print_percent_status,
1107 NULL);
1108 if (err != 0) {
1109 printf("Failed to dump firmware: %s\n", lc_strerror(err));
1110 }
1111 break;
1112
1113 case MODE_WRITE_FIRMWARE:
1114 err = upload_firmware(&options, cb_print_percent_status, NULL);
1115 if (err != 0) {
1116 printf("Failed to upload firmware: %s\n", lc_strerror(err));
1117 }
1118 break;
1119
1120 case MODE_DUMP_SAFEMODE:
1121 err = dump_safemode(file_name, cb_print_percent_status, NULL);
1122 if (err != 0) {
1123 printf("Failed to dump safemode: %s\n", lc_strerror(err));
1124 }
1125 break;
1126
1127 case MODE_LEARN_IR:
1128 err = learn_ir_commands(&options, cb_print_percent_status, NULL);
1129 break;
1130
1131 case MODE_GET_TIME:
1132 err = get_time();
1133 print_time(0);
1134 break;
1135
1136 case MODE_SET_TIME:
1137 err = set_time(cb_print_percent_status, NULL);
1138 print_time(1);
1139 break;
1140
1141 default:
1142 fprintf(stderr,
1143 "ERROR: Got to a place I don't understand!\n");
1144 break;
1145 }
1146
1147 cleanup:
1148
1149 delete_opfile_obj();
1150
1151 deinit_concord();
1152
1153 if (err) {
1154 printf("Failed with error %i\n", err);
1155 } else {
1156 printf("Success!\n");
1157 }
1158
1159 #ifdef _WIN32
1160 /* Shutdown WinSock */
1161 WSACleanup();
1162 #endif
1163
1164 #ifdef _DEBUG
1165 printf("Press <enter> key to exit\n");
1166 getchar();
1167 #endif /* debug */
1168
1169 return err;
1170 }
1171