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 bool_t sigabort = FALSE;
00206
00207 static void signal_abort(int arg)
00208 {
00209 sigabort = TRUE;
00210 }
00211
00212 static void cmd_scan_printf(FILE *fp, const char *fmt, ...)
00213 {
00214 va_list ap;
00215 va_start(ap, fmt);
00216
00217 if (fp) {
00218 va_list apc;
00219 va_copy(apc, ap);
00220
00221 vfprintf(fp, fmt, apc);
00222 fflush(fp);
00223
00224 va_end(apc);
00225 }
00226
00227 vprintf(fmt, ap);
00228 fflush(stdout);
00229
00230 va_end(ap);
00231 }
00232
00233 static int cmd_scan(const char *tuner_str, const char *filename)
00234 {
00235 if (hdhomerun_device_set_tuner_from_str(hd, tuner_str) <= 0) {
00236 fprintf(stderr, "invalid tuner number\n");
00237 return -1;
00238 }
00239
00240 char *ret_error;
00241 if (hdhomerun_device_tuner_lockkey_request(hd, &ret_error) <= 0) {
00242 fprintf(stderr, "failed to lock tuner\n");
00243 if (ret_error) {
00244 fprintf(stderr, "%s\n", ret_error);
00245 }
00246 return -1;
00247 }
00248
00249 hdhomerun_device_set_tuner_target(hd, "none");
00250
00251 char *channelmap;
00252 if (hdhomerun_device_get_tuner_channelmap(hd, &channelmap) <= 0) {
00253 fprintf(stderr, "failed to query channelmap from device\n");
00254 return -1;
00255 }
00256
00257 const char *channelmap_scan_group = hdhomerun_channelmap_get_channelmap_scan_group(channelmap);
00258 if (!channelmap_scan_group) {
00259 fprintf(stderr, "unknown channelmap '%s'\n", channelmap);
00260 return -1;
00261 }
00262
00263 if (hdhomerun_device_channelscan_init(hd, channelmap_scan_group) <= 0) {
00264 fprintf(stderr, "failed to initialize channel scan\n");
00265 return -1;
00266 }
00267
00268 FILE *fp = NULL;
00269 if (filename) {
00270 fp = fopen(filename, "w");
00271 if (!fp) {
00272 fprintf(stderr, "unable to create file: %s\n", filename);
00273 return -1;
00274 }
00275 }
00276
00277 signal(SIGINT, signal_abort);
00278 signal(SIGPIPE, signal_abort);
00279
00280 int ret = 0;
00281 while (!sigabort) {
00282 struct hdhomerun_channelscan_result_t result;
00283 ret = hdhomerun_device_channelscan_advance(hd, &result);
00284 if (ret <= 0) {
00285 break;
00286 }
00287
00288 cmd_scan_printf(fp, "SCANNING: %lu (%s)\n",
00289 result.frequency, result.channel_str
00290 );
00291
00292 ret = hdhomerun_device_channelscan_detect(hd, &result);
00293 if (ret <= 0) {
00294 break;
00295 }
00296
00297 cmd_scan_printf(fp, "LOCK: %s (ss=%u snq=%u seq=%u)\n",
00298 result.status.lock_str, result.status.signal_strength,
00299 result.status.signal_to_noise_quality, result.status.symbol_error_quality
00300 );
00301
00302 if (result.transport_stream_id_detected) {
00303 cmd_scan_printf(fp, "TSID: 0x%04X\n", result.transport_stream_id);
00304 }
00305
00306 int i;
00307 for (i = 0; i < result.program_count; i++) {
00308 struct hdhomerun_channelscan_program_t *program = &result.programs[i];
00309 cmd_scan_printf(fp, "PROGRAM %s\n", program->program_str);
00310 }
00311 }
00312
00313 hdhomerun_device_tuner_lockkey_release(hd);
00314
00315 if (fp) {
00316 fclose(fp);
00317 }
00318 if (ret < 0) {
00319 fprintf(stderr, "communication error sending request to hdhomerun device\n");
00320 }
00321 return ret;
00322 }
00323
00324 static int cmd_save(const char *tuner_str, const char *filename)
00325 {
00326 if (hdhomerun_device_set_tuner_from_str(hd, tuner_str) <= 0) {
00327 fprintf(stderr, "invalid tuner number\n");
00328 return -1;
00329 }
00330
00331 FILE *fp;
00332 if (strcmp(filename, "null") == 0) {
00333 fp = NULL;
00334 } else if (strcmp(filename, "-") == 0) {
00335 fp = stdout;
00336 } else {
00337 fp = fopen(filename, "wb");
00338 if (!fp) {
00339 fprintf(stderr, "unable to create file %s\n", filename);
00340 return -1;
00341 }
00342 }
00343
00344 int ret = hdhomerun_device_stream_start(hd);
00345 if (ret <= 0) {
00346 fprintf(stderr, "unable to start stream\n");
00347 if (fp && fp != stdout) {
00348 fclose(fp);
00349 }
00350 return ret;
00351 }
00352
00353 signal(SIGINT, signal_abort);
00354 signal(SIGPIPE, signal_abort);
00355
00356 struct hdhomerun_video_stats_t stats_old, stats_cur;
00357 hdhomerun_device_get_video_stats(hd, &stats_old);
00358
00359 uint64_t next_progress = getcurrenttime() + 1000;
00360
00361 while (!sigabort) {
00362 uint64_t loop_start_time = getcurrenttime();
00363
00364 size_t actual_size;
00365 uint8_t *ptr = hdhomerun_device_stream_recv(hd, VIDEO_DATA_BUFFER_SIZE_1S, &actual_size);
00366 if (!ptr) {
00367 msleep(64);
00368 continue;
00369 }
00370
00371 if (fp) {
00372 if (fwrite(ptr, 1, actual_size, fp) != actual_size) {
00373 fprintf(stderr, "error writing output\n");
00374 return -1;
00375 }
00376 }
00377
00378 if (loop_start_time >= next_progress) {
00379 next_progress += 1000;
00380 if (loop_start_time >= next_progress) {
00381 next_progress = loop_start_time + 1000;
00382 }
00383
00384 hdhomerun_device_get_video_stats(hd, &stats_cur);
00385
00386 if (stats_cur.overflow_error_count > stats_old.overflow_error_count) {
00387 fprintf(stderr, "o");
00388 } else if (stats_cur.network_error_count > stats_old.network_error_count) {
00389 fprintf(stderr, "n");
00390 } else if (stats_cur.transport_error_count > stats_old.transport_error_count) {
00391 fprintf(stderr, "t");
00392 } else if (stats_cur.sequence_error_count > stats_old.sequence_error_count) {
00393 fprintf(stderr, "s");
00394 } else {
00395 fprintf(stderr, ".");
00396 }
00397
00398 stats_old = stats_cur;
00399 fflush(stderr);
00400 }
00401
00402 int32_t delay = 64 - (int32_t)(getcurrenttime() - loop_start_time);
00403 if (delay <= 0) {
00404 continue;
00405 }
00406
00407 msleep(delay);
00408 }
00409
00410 if (fp) {
00411 fclose(fp);
00412 }
00413
00414 hdhomerun_device_stream_stop(hd);
00415 hdhomerun_device_get_video_stats(hd, &stats_cur);
00416
00417 fprintf(stderr, "\n");
00418 fprintf(stderr, "-- Video statistics --\n");
00419 fprintf(stderr, "%u packets received, %u overflow errors, %u network errors, %u transport errors, %u sequence errors\n",
00420 (unsigned int)stats_cur.packet_count,
00421 (unsigned int)stats_cur.overflow_error_count,
00422 (unsigned int)stats_cur.network_error_count,
00423 (unsigned int)stats_cur.transport_error_count,
00424 (unsigned int)stats_cur.sequence_error_count);
00425
00426 return 0;
00427 }
00428
00429 static int cmd_upgrade(const char *filename)
00430 {
00431 FILE *fp = fopen(filename, "rb");
00432 if (!fp) {
00433 fprintf(stderr, "unable to open file %s\n", filename);
00434 return -1;
00435 }
00436
00437 printf("uploading firmware...\n");
00438 if (hdhomerun_device_upgrade(hd, fp) <= 0) {
00439 fprintf(stderr, "error sending upgrade file to hdhomerun device\n");
00440 fclose(fp);
00441 return -1;
00442 }
00443 sleep(2);
00444
00445 printf("upgrading firmware...\n");
00446 sleep(8);
00447
00448 printf("rebooting...\n");
00449 int count = 0;
00450 char *version_str;
00451 while (1) {
00452 if (hdhomerun_device_get_version(hd, &version_str, NULL) >= 0) {
00453 break;
00454 }
00455
00456 count++;
00457 if (count > 30) {
00458 fprintf(stderr, "error finding device after firmware upgrade\n");
00459 fclose(fp);
00460 return -1;
00461 }
00462
00463 sleep(1);
00464 }
00465
00466 printf("upgrade complete - now running firmware %s\n", version_str);
00467 return 0;
00468 }
00469
00470 static int cmd_execute(void)
00471 {
00472 char *ret_value;
00473 char *ret_error;
00474 if (hdhomerun_device_get_var(hd, "/sys/boot", &ret_value, &ret_error) < 0) {
00475 fprintf(stderr, "communication error sending request to hdhomerun device\n");
00476 return -1;
00477 }
00478
00479 if (ret_error) {
00480 printf("%s\n", ret_error);
00481 return 0;
00482 }
00483
00484 char *end = ret_value + strlen(ret_value);
00485 char *pos = ret_value;
00486
00487 while (1) {
00488 if (pos >= end) {
00489 break;
00490 }
00491
00492 char *eol_r = strchr(pos, '\r');
00493 if (!eol_r) {
00494 eol_r = end;
00495 }
00496
00497 char *eol_n = strchr(pos, '\n');
00498 if (!eol_n) {
00499 eol_n = end;
00500 }
00501
00502 char *eol = eol_r;
00503 if (eol_n < eol) {
00504 eol = eol_n;
00505 }
00506
00507 char *sep = strchr(pos, ' ');
00508 if (!sep || sep > eol) {
00509 pos = eol + 1;
00510 continue;
00511 }
00512
00513 *sep = 0;
00514 *eol = 0;
00515
00516 char *item = pos;
00517 char *value = sep + 1;
00518
00519 printf("set %s \"%s\"\n", item, value);
00520
00521 cmd_set_internal(item, value);
00522
00523 pos = eol + 1;
00524 }
00525
00526 return 1;
00527 }
00528
00529 static int main_cmd(int argc, char *argv[])
00530 {
00531 if (argc < 1) {
00532 return help();
00533 }
00534
00535 char *cmd = *argv++; argc--;
00536
00537 if (contains(cmd, "key")) {
00538 if (argc < 2) {
00539 return help();
00540 }
00541 uint32_t lockkey = strtoul(argv[0], NULL, 0);
00542 hdhomerun_device_tuner_lockkey_use_value(hd, lockkey);
00543
00544 cmd = argv[1];
00545 argv+=2; argc-=2;
00546 }
00547
00548 if (contains(cmd, "get")) {
00549 if (argc < 1) {
00550 return help();
00551 }
00552 return cmd_get(argv[0]);
00553 }
00554
00555 if (contains(cmd, "set")) {
00556 if (argc < 2) {
00557 return help();
00558 }
00559 return cmd_set(argv[0], argv[1]);
00560 }
00561
00562 if (contains(cmd, "scan")) {
00563 if (argc < 1) {
00564 return help();
00565 }
00566 if (argc < 2) {
00567 return cmd_scan(argv[0], NULL);
00568 } else {
00569 return cmd_scan(argv[0], argv[1]);
00570 }
00571 }
00572
00573 if (contains(cmd, "save")) {
00574 if (argc < 2) {
00575 return help();
00576 }
00577 return cmd_save(argv[0], argv[1]);
00578 }
00579
00580 if (contains(cmd, "upgrade")) {
00581 if (argc < 1) {
00582 return help();
00583 }
00584 return cmd_upgrade(argv[0]);
00585 }
00586
00587 if (contains(cmd, "execute")) {
00588 return cmd_execute();
00589 }
00590
00591 return help();
00592 }
00593
00594 static int main_internal(int argc, char *argv[])
00595 {
00596 #if defined(__WINDOWS__)
00597
00598 WORD wVersionRequested = MAKEWORD(2, 0);
00599 WSADATA wsaData;
00600 WSAStartup(wVersionRequested, &wsaData);
00601 #endif
00602
00603 extract_appname(argv[0]);
00604 argv++;
00605 argc--;
00606
00607 if (argc == 0) {
00608 return help();
00609 }
00610
00611 char *id_str = *argv++; argc--;
00612 if (contains(id_str, "help")) {
00613 return help();
00614 }
00615 if (contains(id_str, "discover")) {
00616 if (argc < 1) {
00617 return discover_print(NULL);
00618 } else {
00619 return discover_print(argv[0]);
00620 }
00621 }
00622
00623
00624 hd = hdhomerun_device_create_from_str(id_str, NULL);
00625 if (!hd) {
00626 fprintf(stderr, "invalid device id: %s\n", id_str);
00627 return -1;
00628 }
00629
00630
00631 uint32_t device_id_requested = hdhomerun_device_get_device_id_requested(hd);
00632 if (!hdhomerun_discover_validate_device_id(device_id_requested)) {
00633 fprintf(stderr, "invalid device id: %08lX\n", (unsigned long)device_id_requested);
00634 }
00635
00636
00637 const char *model = hdhomerun_device_get_model_str(hd);
00638 if (!model) {
00639 fprintf(stderr, "unable to connect to device\n");
00640 hdhomerun_device_destroy(hd);
00641 return -1;
00642 }
00643
00644
00645 int ret = main_cmd(argc, argv);
00646
00647
00648 hdhomerun_device_destroy(hd);
00649
00650
00651 return ret;
00652 }
00653
00654 int main(int argc, char *argv[])
00655 {
00656 int ret = main_internal(argc, argv);
00657 if (ret <= 0) {
00658 return 1;
00659 }
00660 return 0;
00661 }