MythTV  0.26-pre
hdhomerun_config.c
Go to the documentation of this file.
00001 /*
00002  * hdhomerun_config.c
00003  *
00004  * Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
00005  *
00006  * This library is free software; you can redistribute it and/or 
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 3 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
00018  * 
00019  * As a special exception to the GNU Lesser General Public License,
00020  * you may link, statically or dynamically, an application with a
00021  * publicly distributed version of the Library to produce an
00022  * executable file containing portions of the Library, and
00023  * distribute that executable file under terms of your choice,
00024  * without any of the additional requirements listed in clause 4 of
00025  * the GNU Lesser General Public License.
00026  * 
00027  * By "a publicly distributed version of the Library", we mean
00028  * either the unmodified Library as distributed by Silicondust, or a
00029  * modified version of the Library that is distributed under the
00030  * conditions defined in the GNU Lesser General Public License.
00031  */
00032 
00033 #include "hdhomerun.h"
00034 
00035 /*
00036  * The console output format should be set to UTF-8, however in XP and Vista this breaks batch file processing.
00037  * Attempting to restore on exit fails to restore if the program is terminated by the user.
00038  * Solution - set the output format each printf.
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                         /* Windows - indicate activity to suppress auto sleep mode. */
00425                         #if defined(__WINDOWS__)
00426                         SetThreadExecutionState(ES_SYSTEM_REQUIRED);
00427                         #endif
00428 
00429                         /* Video stats. */
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         /* Initialize network socket support. */
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         /* Device object. */
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         /* Device ID check. */
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         /* Connect to device and check model. */
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         /* Command. */
00686         int ret = main_cmd(argc, argv);
00687 
00688         /* Cleanup. */
00689         hdhomerun_device_destroy(hd);
00690 
00691         /* Complete. */
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends