00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "hdhomerun.h"
00034
00035
00036
00037
00038
00039
00040 #if defined(__WINDOWS__)
00041 #define printf console_printf
00042 #define vprintf console_vprintf
00043 #endif
00044
00045 static const char *appname;
00046
00047 struct hdhomerun_device_t *hd;
00048
00049 static int help(void)
00050 {
00051 printf("Usage:\n");
00052 printf("\t%s discover\n", appname);
00053 printf("\t%s <id> get help\n", appname);
00054 printf("\t%s <id> get <item>\n", appname);
00055 printf("\t%s <id> set <item> <value>\n", appname);
00056 printf("\t%s <id> scan <tuner> [<filename>]\n", appname);
00057 printf("\t%s <id> save <tuner> <filename>\n", appname);
00058 printf("\t%s <id> upgrade <filename>\n", appname);
00059 return -1;
00060 }
00061
00062 static void extract_appname(const char *argv0)
00063 {
00064 const char *ptr = strrchr(argv0, '/');
00065 if (ptr) {
00066 argv0 = ptr + 1;
00067 }
00068 ptr = strrchr(argv0, '\\');
00069 if (ptr) {
00070 argv0 = ptr + 1;
00071 }
00072 appname = argv0;
00073 }
00074
00075 static bool_t contains(const char *arg, const char *cmpstr)
00076 {
00077 if (strcmp(arg, cmpstr) == 0) {
00078 return TRUE;
00079 }
00080
00081 if (*arg++ != '-') {
00082 return FALSE;
00083 }
00084 if (*arg++ != '-') {
00085 return FALSE;
00086 }
00087 if (strcmp(arg, cmpstr) == 0) {
00088 return TRUE;
00089 }
00090
00091 return FALSE;
00092 }
00093
00094 static uint32_t parse_ip_addr(const char *str)
00095 {
00096 unsigned long a[4];
00097 if (sscanf(str, "%lu.%lu.%lu.%lu", &a[0], &a[1], &a[2], &a[3]) != 4) {
00098 return 0;
00099 }
00100
00101 return (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
00102 }
00103
00104 static int discover_print(char *target_ip_str)
00105 {
00106 uint32_t target_ip = 0;
00107 if (target_ip_str) {
00108 target_ip = parse_ip_addr(target_ip_str);
00109 if (target_ip == 0) {
00110 fprintf(stderr, "invalid ip address: %s\n", target_ip_str);
00111 return -1;
00112 }
00113 }
00114
00115 struct hdhomerun_discover_device_t result_list[64];
00116 int count = hdhomerun_discover_find_devices_custom(target_ip, HDHOMERUN_DEVICE_TYPE_TUNER, HDHOMERUN_DEVICE_ID_WILDCARD, result_list, 64);
00117 if (count < 0) {
00118 fprintf(stderr, "error sending discover request\n");
00119 return -1;
00120 }
00121 if (count == 0) {
00122 printf("no devices found\n");
00123 return 0;
00124 }
00125
00126 int index;
00127 for (index = 0; index < count; index++) {
00128 struct hdhomerun_discover_device_t *result = &result_list[index];
00129 printf("hdhomerun device %08lX found at %u.%u.%u.%u\n",
00130 (unsigned long)result->device_id,
00131 (unsigned int)(result->ip_addr >> 24) & 0x0FF, (unsigned int)(result->ip_addr >> 16) & 0x0FF,
00132 (unsigned int)(result->ip_addr >> 8) & 0x0FF, (unsigned int)(result->ip_addr >> 0) & 0x0FF
00133 );
00134 }
00135
00136 return count;
00137 }
00138
00139 static int cmd_get(const char *item)
00140 {
00141 char *ret_value;
00142 char *ret_error;
00143 if (hdhomerun_device_get_var(hd, item, &ret_value, &ret_error) < 0) {
00144 fprintf(stderr, "communication error sending request to hdhomerun device\n");
00145 return -1;
00146 }
00147
00148 if (ret_error) {
00149 printf("%s\n", ret_error);
00150 return 0;
00151 }
00152
00153 printf("%s\n", ret_value);
00154 return 1;
00155 }
00156
00157 static int cmd_set_internal(const char *item, const char *value)
00158 {
00159 char *ret_error;
00160 if (hdhomerun_device_set_var(hd, item, value, NULL, &ret_error) < 0) {
00161 fprintf(stderr, "communication error sending request to hdhomerun device\n");
00162 return -1;
00163 }
00164
00165 if (ret_error) {
00166 printf("%s\n", ret_error);
00167 return 0;
00168 }
00169
00170 return 1;
00171 }
00172
00173 static int cmd_set(const char *item, const char *value)
00174 {
00175 if (strcmp(value, "-") == 0) {
00176 char *buffer = NULL;
00177 size_t pos = 0;
00178
00179 while (1) {
00180 buffer = (char *)realloc(buffer, pos + 1024);
00181 if (!buffer) {
00182 fprintf(stderr, "out of memory\n");
00183 return -1;
00184 }
00185
00186 size_t size = fread(buffer + pos, 1, 1024, stdin);
00187 pos += size;
00188
00189 if (size < 1024) {
00190 break;
00191 }
00192 }
00193
00194 buffer[pos] = 0;
00195
00196 int ret = cmd_set_internal(item, buffer);
00197
00198 free(buffer);
00199 return ret;
00200 }
00201
00202 return cmd_set_internal(item, value);
00203 }
00204
00205 static volatile sig_atomic_t sigabort_flag = FALSE;
00206 static volatile sig_atomic_t siginfo_flag = FALSE;
00207
00208 static void sigabort_handler(int arg)
00209 {
00210 sigabort_flag = TRUE;
00211 }
00212
00213 static void siginfo_handler(int arg)
00214 {
00215 siginfo_flag = TRUE;
00216 }
00217
00218 static void register_signal_handlers(sig_t sigpipe_handler, sig_t sigint_handler, sig_t siginfo_handler)
00219 {
00220 #if defined(SIGPIPE)
00221 signal(SIGPIPE, sigpipe_handler);
00222 #endif
00223 #if defined(SIGINT)
00224 signal(SIGINT, sigint_handler);
00225 #endif
00226 #if defined(SIGINFO)
00227 signal(SIGINFO, siginfo_handler);
00228 #endif
00229 }
00230
00231 static void cmd_scan_printf(FILE *fp, const char *fmt, ...)
00232 {
00233 va_list ap;
00234 va_start(ap, fmt);
00235
00236 if (fp) {
00237 va_list apc;
00238 va_copy(apc, ap);
00239
00240 vfprintf(fp, fmt, apc);
00241 fflush(fp);
00242
00243 va_end(apc);
00244 }
00245
00246 vprintf(fmt, ap);
00247 fflush(stdout);
00248
00249 va_end(ap);
00250 }
00251
00252 static int cmd_scan(const char *tuner_str, const char *filename)
00253 {
00254 if (hdhomerun_device_set_tuner_from_str(hd, tuner_str) <= 0) {
00255 fprintf(stderr, "invalid tuner number\n");
00256 return -1;
00257 }
00258
00259 char *ret_error;
00260 if (hdhomerun_device_tuner_lockkey_request(hd, &ret_error) <= 0) {
00261 fprintf(stderr, "failed to lock tuner\n");
00262 if (ret_error) {
00263 fprintf(stderr, "%s\n", ret_error);
00264 }
00265 return -1;
00266 }
00267
00268 hdhomerun_device_set_tuner_target(hd, "none");
00269
00270 char *channelmap;
00271 if (hdhomerun_device_get_tuner_channelmap(hd, &channelmap) <= 0) {
00272 fprintf(stderr, "failed to query channelmap from device\n");
00273 return -1;
00274 }
00275
00276 const char *channelmap_scan_group = hdhomerun_channelmap_get_channelmap_scan_group(channelmap);
00277 if (!channelmap_scan_group) {
00278 fprintf(stderr, "unknown channelmap '%s'\n", channelmap);
00279 return -1;
00280 }
00281
00282 if (hdhomerun_device_channelscan_init(hd, channelmap_scan_group) <= 0) {
00283 fprintf(stderr, "failed to initialize channel scan\n");
00284 return -1;
00285 }
00286
00287 FILE *fp = NULL;
00288 if (filename) {
00289 fp = fopen(filename, "w");
00290 if (!fp) {
00291 fprintf(stderr, "unable to create file: %s\n", filename);
00292 return -1;
00293 }
00294 }
00295
00296 register_signal_handlers(sigabort_handler, sigabort_handler, siginfo_handler);
00297
00298 int ret = 0;
00299 while (!sigabort_flag) {
00300 struct hdhomerun_channelscan_result_t result;
00301 ret = hdhomerun_device_channelscan_advance(hd, &result);
00302 if (ret <= 0) {
00303 break;
00304 }
00305
00306 cmd_scan_printf(fp, "SCANNING: %lu (%s)\n",
00307 (unsigned long)result.frequency, result.channel_str
00308 );
00309
00310 ret = hdhomerun_device_channelscan_detect(hd, &result);
00311 if (ret < 0) {
00312 break;
00313 }
00314 if (ret == 0) {
00315 continue;
00316 }
00317
00318 cmd_scan_printf(fp, "LOCK: %s (ss=%u snq=%u seq=%u)\n",
00319 result.status.lock_str, result.status.signal_strength,
00320 result.status.signal_to_noise_quality, result.status.symbol_error_quality
00321 );
00322
00323 if (result.transport_stream_id_detected) {
00324 cmd_scan_printf(fp, "TSID: 0x%04X\n", result.transport_stream_id);
00325 }
00326
00327 int i;
00328 for (i = 0; i < result.program_count; i++) {
00329 struct hdhomerun_channelscan_program_t *program = &result.programs[i];
00330 cmd_scan_printf(fp, "PROGRAM %s\n", program->program_str);
00331 }
00332 }
00333
00334 hdhomerun_device_tuner_lockkey_release(hd);
00335
00336 if (fp) {
00337 fclose(fp);
00338 }
00339 if (ret < 0) {
00340 fprintf(stderr, "communication error sending request to hdhomerun device\n");
00341 }
00342 return ret;
00343 }
00344
00345 static void cmd_save_print_stats(void)
00346 {
00347 struct hdhomerun_video_stats_t stats;
00348 hdhomerun_device_get_video_stats(hd, &stats);
00349
00350 fprintf(stderr, "%u packets received, %u overflow errors, %u network errors, %u transport errors, %u sequence errors\n",
00351 (unsigned int)stats.packet_count,
00352 (unsigned int)stats.overflow_error_count,
00353 (unsigned int)stats.network_error_count,
00354 (unsigned int)stats.transport_error_count,
00355 (unsigned int)stats.sequence_error_count
00356 );
00357 }
00358
00359 static int cmd_save(const char *tuner_str, const char *filename)
00360 {
00361 if (hdhomerun_device_set_tuner_from_str(hd, tuner_str) <= 0) {
00362 fprintf(stderr, "invalid tuner number\n");
00363 return -1;
00364 }
00365
00366 FILE *fp;
00367 if (strcmp(filename, "null") == 0) {
00368 fp = NULL;
00369 } else if (strcmp(filename, "-") == 0) {
00370 fp = stdout;
00371 } else {
00372 fp = fopen(filename, "wb");
00373 if (!fp) {
00374 fprintf(stderr, "unable to create file %s\n", filename);
00375 return -1;
00376 }
00377 }
00378
00379 int ret = hdhomerun_device_stream_start(hd);
00380 if (ret <= 0) {
00381 fprintf(stderr, "unable to start stream\n");
00382 if (fp && fp != stdout) {
00383 fclose(fp);
00384 }
00385 return ret;
00386 }
00387
00388 register_signal_handlers(sigabort_handler, sigabort_handler, siginfo_handler);
00389
00390 struct hdhomerun_video_stats_t stats_old, stats_cur;
00391 hdhomerun_device_get_video_stats(hd, &stats_old);
00392
00393 uint64_t next_progress = getcurrenttime() + 1000;
00394
00395 while (!sigabort_flag) {
00396 uint64_t loop_start_time = getcurrenttime();
00397
00398 if (siginfo_flag) {
00399 fprintf(stderr, "\n");
00400 cmd_save_print_stats();
00401 siginfo_flag = FALSE;
00402 }
00403
00404 size_t actual_size;
00405 uint8_t *ptr = hdhomerun_device_stream_recv(hd, VIDEO_DATA_BUFFER_SIZE_1S, &actual_size);
00406 if (!ptr) {
00407 msleep_approx(64);
00408 continue;
00409 }
00410
00411 if (fp) {
00412 if (fwrite(ptr, 1, actual_size, fp) != actual_size) {
00413 fprintf(stderr, "error writing output\n");
00414 return -1;
00415 }
00416 }
00417
00418 if (loop_start_time >= next_progress) {
00419 next_progress += 1000;
00420 if (loop_start_time >= next_progress) {
00421 next_progress = loop_start_time + 1000;
00422 }
00423
00424
00425 #if defined(__WINDOWS__)
00426 SetThreadExecutionState(ES_SYSTEM_REQUIRED);
00427 #endif
00428
00429
00430 hdhomerun_device_get_video_stats(hd, &stats_cur);
00431
00432 if (stats_cur.overflow_error_count > stats_old.overflow_error_count) {
00433 fprintf(stderr, "o");
00434 } else if (stats_cur.network_error_count > stats_old.network_error_count) {
00435 fprintf(stderr, "n");
00436 } else if (stats_cur.transport_error_count > stats_old.transport_error_count) {
00437 fprintf(stderr, "t");
00438 } else if (stats_cur.sequence_error_count > stats_old.sequence_error_count) {
00439 fprintf(stderr, "s");
00440 } else {
00441 fprintf(stderr, ".");
00442 }
00443
00444 stats_old = stats_cur;
00445 fflush(stderr);
00446 }
00447
00448 int32_t delay = 64 - (int32_t)(getcurrenttime() - loop_start_time);
00449 if (delay <= 0) {
00450 continue;
00451 }
00452
00453 msleep_approx(delay);
00454 }
00455
00456 if (fp) {
00457 fclose(fp);
00458 }
00459
00460 hdhomerun_device_stream_stop(hd);
00461
00462 fprintf(stderr, "\n");
00463 fprintf(stderr, "-- Video statistics --\n");
00464 cmd_save_print_stats();
00465
00466 return 0;
00467 }
00468
00469 static int cmd_upgrade(const char *filename)
00470 {
00471 FILE *fp = fopen(filename, "rb");
00472 if (!fp) {
00473 fprintf(stderr, "unable to open file %s\n", filename);
00474 return -1;
00475 }
00476
00477 printf("uploading firmware...\n");
00478 if (hdhomerun_device_upgrade(hd, fp) <= 0) {
00479 fprintf(stderr, "error sending upgrade file to hdhomerun device\n");
00480 fclose(fp);
00481 return -1;
00482 }
00483
00484 fclose(fp);
00485 msleep_minimum(2000);
00486
00487 printf("upgrading firmware...\n");
00488 msleep_minimum(8000);
00489
00490 printf("rebooting...\n");
00491 int count = 0;
00492 char *version_str;
00493 while (1) {
00494 if (hdhomerun_device_get_version(hd, &version_str, NULL) >= 0) {
00495 break;
00496 }
00497
00498 count++;
00499 if (count > 30) {
00500 fprintf(stderr, "error finding device after firmware upgrade\n");
00501 return -1;
00502 }
00503
00504 msleep_minimum(1000);
00505 }
00506
00507 printf("upgrade complete - now running firmware %s\n", version_str);
00508 return 0;
00509 }
00510
00511 static int cmd_execute(void)
00512 {
00513 char *ret_value;
00514 char *ret_error;
00515 if (hdhomerun_device_get_var(hd, "/sys/boot", &ret_value, &ret_error) < 0) {
00516 fprintf(stderr, "communication error sending request to hdhomerun device\n");
00517 return -1;
00518 }
00519
00520 if (ret_error) {
00521 printf("%s\n", ret_error);
00522 return 0;
00523 }
00524
00525 char *end = ret_value + strlen(ret_value);
00526 char *pos = ret_value;
00527
00528 while (1) {
00529 if (pos >= end) {
00530 break;
00531 }
00532
00533 char *eol_r = strchr(pos, '\r');
00534 if (!eol_r) {
00535 eol_r = end;
00536 }
00537
00538 char *eol_n = strchr(pos, '\n');
00539 if (!eol_n) {
00540 eol_n = end;
00541 }
00542
00543 char *eol = eol_r;
00544 if (eol_n < eol) {
00545 eol = eol_n;
00546 }
00547
00548 char *sep = strchr(pos, ' ');
00549 if (!sep || sep > eol) {
00550 pos = eol + 1;
00551 continue;
00552 }
00553
00554 *sep = 0;
00555 *eol = 0;
00556
00557 char *item = pos;
00558 char *value = sep + 1;
00559
00560 printf("set %s \"%s\"\n", item, value);
00561
00562 cmd_set_internal(item, value);
00563
00564 pos = eol + 1;
00565 }
00566
00567 return 1;
00568 }
00569
00570 static int main_cmd(int argc, char *argv[])
00571 {
00572 if (argc < 1) {
00573 return help();
00574 }
00575
00576 char *cmd = *argv++; argc--;
00577
00578 if (contains(cmd, "key")) {
00579 if (argc < 2) {
00580 return help();
00581 }
00582 uint32_t lockkey = strtoul(argv[0], NULL, 0);
00583 hdhomerun_device_tuner_lockkey_use_value(hd, lockkey);
00584
00585 cmd = argv[1];
00586 argv+=2; argc-=2;
00587 }
00588
00589 if (contains(cmd, "get")) {
00590 if (argc < 1) {
00591 return help();
00592 }
00593 return cmd_get(argv[0]);
00594 }
00595
00596 if (contains(cmd, "set")) {
00597 if (argc < 2) {
00598 return help();
00599 }
00600 return cmd_set(argv[0], argv[1]);
00601 }
00602
00603 if (contains(cmd, "scan")) {
00604 if (argc < 1) {
00605 return help();
00606 }
00607 if (argc < 2) {
00608 return cmd_scan(argv[0], NULL);
00609 } else {
00610 return cmd_scan(argv[0], argv[1]);
00611 }
00612 }
00613
00614 if (contains(cmd, "save")) {
00615 if (argc < 2) {
00616 return help();
00617 }
00618 return cmd_save(argv[0], argv[1]);
00619 }
00620
00621 if (contains(cmd, "upgrade")) {
00622 if (argc < 1) {
00623 return help();
00624 }
00625 return cmd_upgrade(argv[0]);
00626 }
00627
00628 if (contains(cmd, "execute")) {
00629 return cmd_execute();
00630 }
00631
00632 return help();
00633 }
00634
00635 static int main_internal(int argc, char *argv[])
00636 {
00637 #if defined(__WINDOWS__)
00638
00639 WORD wVersionRequested = MAKEWORD(2, 0);
00640 WSADATA wsaData;
00641 WSAStartup(wVersionRequested, &wsaData);
00642 #endif
00643
00644 extract_appname(argv[0]);
00645 argv++;
00646 argc--;
00647
00648 if (argc == 0) {
00649 return help();
00650 }
00651
00652 char *id_str = *argv++; argc--;
00653 if (contains(id_str, "help")) {
00654 return help();
00655 }
00656 if (contains(id_str, "discover")) {
00657 if (argc < 1) {
00658 return discover_print(NULL);
00659 } else {
00660 return discover_print(argv[0]);
00661 }
00662 }
00663
00664
00665 hd = hdhomerun_device_create_from_str(id_str, NULL);
00666 if (!hd) {
00667 fprintf(stderr, "invalid device id: %s\n", id_str);
00668 return -1;
00669 }
00670
00671
00672 uint32_t device_id_requested = hdhomerun_device_get_device_id_requested(hd);
00673 if (!hdhomerun_discover_validate_device_id(device_id_requested)) {
00674 fprintf(stderr, "invalid device id: %08lX\n", (unsigned long)device_id_requested);
00675 }
00676
00677
00678 const char *model = hdhomerun_device_get_model_str(hd);
00679 if (!model) {
00680 fprintf(stderr, "unable to connect to device\n");
00681 hdhomerun_device_destroy(hd);
00682 return -1;
00683 }
00684
00685
00686 int ret = main_cmd(argc, argv);
00687
00688
00689 hdhomerun_device_destroy(hd);
00690
00691
00692 return ret;
00693 }
00694
00695 int main(int argc, char *argv[])
00696 {
00697 int ret = main_internal(argc, argv);
00698 if (ret <= 0) {
00699 return 1;
00700 }
00701 return 0;
00702 }