MythTV  0.26-pre
hdhomerun_device_selector.c
Go to the documentation of this file.
00001 /*
00002  * hdhomerun_device_selector.c
00003  *
00004  * Copyright © 2009-2010 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 struct hdhomerun_device_selector_t {
00036         struct hdhomerun_device_t **hd_list;
00037         size_t hd_count;
00038         struct hdhomerun_debug_t *dbg;
00039 };
00040 
00041 struct hdhomerun_device_selector_t *hdhomerun_device_selector_create(struct hdhomerun_debug_t *dbg)
00042 {
00043         struct hdhomerun_device_selector_t *hds = (struct hdhomerun_device_selector_t *)calloc(1, sizeof(struct hdhomerun_device_selector_t));
00044         if (!hds) {
00045                 hdhomerun_debug_printf(dbg, "hdhomerun_device_selector_create: failed to allocate selector object\n");
00046                 return NULL;
00047         }
00048 
00049         hds->dbg = dbg;
00050 
00051         return hds;
00052 }
00053 
00054 void hdhomerun_device_selector_destroy(struct hdhomerun_device_selector_t *hds, bool_t destroy_devices)
00055 {
00056         if (destroy_devices) {
00057                 size_t index;
00058                 for (index = 0; index < hds->hd_count; index++) {
00059                         struct hdhomerun_device_t *entry = hds->hd_list[index];
00060                         hdhomerun_device_destroy(entry);
00061                 }
00062         }
00063 
00064         if (hds->hd_list) {
00065                 free(hds->hd_list);
00066         }
00067 
00068         free(hds);
00069 }
00070 
00071 LIBTYPE int hdhomerun_device_selector_get_device_count(struct hdhomerun_device_selector_t *hds)
00072 {
00073         return (int)hds->hd_count;
00074 }
00075 
00076 void hdhomerun_device_selector_add_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd)
00077 {
00078         size_t index;
00079         for (index = 0; index < hds->hd_count; index++) {
00080                 struct hdhomerun_device_t *entry = hds->hd_list[index];
00081                 if (entry == hd) {
00082                         return;
00083                 }
00084         }
00085 
00086         hds->hd_list = (struct hdhomerun_device_t **)realloc(hds->hd_list, (hds->hd_count + 1) * sizeof(struct hdhomerun_device_selector_t *));
00087         if (!hds->hd_list) {
00088                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_add_device: failed to allocate device list\n");
00089                 return;
00090         }
00091 
00092         hds->hd_list[hds->hd_count++] = hd;
00093 }
00094 
00095 void hdhomerun_device_selector_remove_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd)
00096 {
00097         size_t index = 0;
00098         while (1) {
00099                 if (index >= hds->hd_count) {
00100                         return;
00101                 }
00102 
00103                 struct hdhomerun_device_t *entry = hds->hd_list[index];
00104                 if (entry == hd) {
00105                         break;
00106                 }
00107 
00108                 index++;
00109         }
00110 
00111         while (index + 1 < hds->hd_count) {
00112                 hds->hd_list[index] = hds->hd_list[index + 1];
00113                 index++;
00114         }
00115 
00116         hds->hd_list[index] = NULL;
00117         hds->hd_count--;
00118 }
00119 
00120 struct hdhomerun_device_t *hdhomerun_device_selector_find_device(struct hdhomerun_device_selector_t *hds, uint32_t device_id, unsigned int tuner_index)
00121 {
00122         size_t index;
00123         for (index = 0; index < hds->hd_count; index++) {
00124                 struct hdhomerun_device_t *entry = hds->hd_list[index];
00125                 if (hdhomerun_device_get_device_id(entry) != device_id) {
00126                         continue;
00127                 }
00128                 if (hdhomerun_device_get_tuner(entry) != tuner_index) {
00129                         continue;
00130                 }
00131                 return entry;
00132         }
00133 
00134         return NULL;
00135 }
00136 
00137 int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename)
00138 {
00139         FILE *fp = fopen(filename, "r");
00140         if (!fp) {
00141                 return 0;
00142         }
00143 
00144         while(1) {
00145                 char device_name[32];
00146                 if (!fgets(device_name, sizeof(device_name), fp)) {
00147                         break;
00148                 }
00149 
00150                 struct hdhomerun_device_t *hd = hdhomerun_device_create_from_str(device_name, hds->dbg);
00151                 if (!hd) {
00152                         continue;
00153                 }
00154 
00155                 hdhomerun_device_selector_add_device(hds, hd);
00156         }
00157 
00158         fclose(fp);
00159         return (int)hds->hd_count;
00160 }
00161 
00162 #if defined(__WINDOWS__)
00163 int hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource)
00164 {
00165         HKEY tuners_key;
00166         LONG ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Silicondust\\HDHomeRun\\Tuners", 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &tuners_key);
00167         if (ret != ERROR_SUCCESS) {
00168                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open tuners registry key (%ld)\n", (long)ret);
00169                 return 0;
00170         }
00171 
00172         DWORD index = 0;
00173         while (1) {
00174                 /* Next tuner device. */
00175                 wchar_t wdevice_name[32];
00176                 DWORD size = sizeof(wdevice_name);
00177                 ret = RegEnumKeyEx(tuners_key, index++, wdevice_name, &size, NULL, NULL, NULL, NULL);
00178                 if (ret != ERROR_SUCCESS) {
00179                         break;
00180                 }
00181 
00182                 /* Check device configuation. */
00183                 HKEY device_key;
00184                 ret = RegOpenKeyEx(tuners_key, wdevice_name, 0, KEY_QUERY_VALUE, &device_key);
00185                 if (ret != ERROR_SUCCESS) {
00186                         hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open registry key for %S (%ld)\n", wdevice_name, (long)ret);
00187                         continue;
00188                 }
00189 
00190                 wchar_t wsource_test[32];
00191                 size = sizeof(wsource_test);
00192                 if (RegQueryValueEx(device_key, L"Source", NULL, NULL, (LPBYTE)&wsource_test, &size) != ERROR_SUCCESS) {
00193                         wsprintf(wsource_test, L"Unknown");
00194                 }
00195 
00196                 RegCloseKey(device_key);
00197 
00198                 if (_wcsicmp(wsource_test, wsource) != 0) {
00199                         continue;
00200                 }
00201 
00202                 /* Create and add device. */
00203                 char device_name[32];
00204                 sprintf(device_name, "%S", wdevice_name);
00205 
00206                 struct hdhomerun_device_t *hd = hdhomerun_device_create_from_str(device_name, hds->dbg);
00207                 if (!hd) {
00208                         hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: invalid device name '%s' / failed to create device object\n", device_name);
00209                         continue;
00210                 }
00211 
00212                 hdhomerun_device_selector_add_device(hds, hd);
00213         }
00214 
00215         RegCloseKey(tuners_key);
00216         return (int)hds->hd_count;
00217 }
00218 #endif
00219 
00220 static bool_t hdhomerun_device_selector_choose_test(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *test_hd)
00221 {
00222         const char *name = hdhomerun_device_get_name(test_hd);
00223 
00224         /*
00225          * Attempt to aquire lock.
00226          */
00227         char *error;
00228         int ret = hdhomerun_device_tuner_lockkey_request(test_hd, &error);
00229         if (ret > 0) {
00230                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s chosen\n", name);
00231                 return TRUE;
00232         }
00233         if (ret < 0) {
00234                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
00235                 return FALSE;
00236         }
00237 
00238         /*
00239          * In use - check target.
00240          */
00241         char *target;
00242         ret = hdhomerun_device_get_tuner_target(test_hd, &target);
00243         if (ret < 0) {
00244                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
00245                 return FALSE;
00246         }
00247         if (ret == 0) {
00248                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, failed to read target\n", name);
00249                 return FALSE;
00250         }
00251 
00252         char *ptr = strstr(target, "//");
00253         if (ptr) {
00254                 target = ptr + 2;
00255         }
00256         ptr = strchr(target, ' ');
00257         if (ptr) {
00258                 *ptr = 0;
00259         }
00260 
00261         unsigned long a[4];
00262         unsigned long target_port;
00263         if (sscanf(target, "%lu.%lu.%lu.%lu:%lu", &a[0], &a[1], &a[2], &a[3], &target_port) != 5) {
00264                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, no target set (%s)\n", name, target);
00265                 return FALSE;
00266         }
00267 
00268         uint32_t target_ip = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
00269         uint32_t local_ip = hdhomerun_device_get_local_machine_addr(test_hd);
00270         if (target_ip != local_ip) {
00271                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by %s\n", name, target);
00272                 return FALSE;
00273         }
00274 
00275         /*
00276          * Test local port.
00277          */
00278         hdhomerun_sock_t test_sock = hdhomerun_sock_create_udp();
00279         if (test_sock == HDHOMERUN_SOCK_INVALID) {
00280                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, failed to create test sock\n", name);
00281                 return FALSE;
00282         }
00283 
00284         bool_t inuse = (hdhomerun_sock_bind(test_sock, INADDR_ANY, (uint16_t)target_port, FALSE) == FALSE);
00285         hdhomerun_sock_destroy(test_sock);
00286 
00287         if (inuse) {
00288                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine\n", name);
00289                 return FALSE;
00290         }
00291 
00292         /*
00293          * Dead local target, force clear lock.
00294          */
00295         ret = hdhomerun_device_tuner_lockkey_force(test_hd);
00296         if (ret < 0) {
00297                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
00298                 return FALSE;
00299         }
00300         if (ret == 0) {
00301                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine, dead target, failed to force release lockkey\n", name);
00302                 return FALSE;
00303         }
00304 
00305         hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine, dead target, lockkey force successful\n", name);
00306 
00307         /*
00308          * Attempt to aquire lock.
00309          */
00310         ret = hdhomerun_device_tuner_lockkey_request(test_hd, &error);
00311         if (ret > 0) {
00312                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s chosen\n", name);
00313                 return TRUE;
00314         }
00315         if (ret < 0) {
00316                 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
00317                 return FALSE;
00318         }
00319 
00320         hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s still in use after lockkey force (%s)\n", name, error);
00321         return FALSE;
00322 }
00323 
00324 struct hdhomerun_device_t *hdhomerun_device_selector_choose_and_lock(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *prefered)
00325 {
00326         /* Test prefered device first. */
00327         if (prefered) {
00328                 if (hdhomerun_device_selector_choose_test(hds, prefered)) {
00329                         return prefered;
00330                 }
00331         }
00332 
00333         /* Test other tuners. */
00334         size_t index;
00335         for (index = 0; index < hds->hd_count; index++) {
00336                 struct hdhomerun_device_t *entry = hds->hd_list[index];
00337                 if (entry == prefered) {
00338                         continue;
00339                 }
00340 
00341                 if (hdhomerun_device_selector_choose_test(hds, entry)) {
00342                         return entry;
00343                 }
00344         }
00345 
00346         hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_and_lock: no devices available\n");
00347         return NULL;
00348 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends