|
MythTV
0.26-pre
|
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 }
1.7.6.1