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 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 void hdhomerun_device_selector_add_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd)
00072 {
00073 size_t index;
00074 for (index = 0; index < hds->hd_count; index++) {
00075 struct hdhomerun_device_t *entry = hds->hd_list[index];
00076 if (entry == hd) {
00077 return;
00078 }
00079 }
00080
00081 hds->hd_list = (struct hdhomerun_device_t **)realloc(hds->hd_list, (hds->hd_count + 1) * sizeof(struct hdhomerun_device_selector_t *));
00082 if (!hds->hd_list) {
00083 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_add_device: failed to allocate device list\n");
00084 return;
00085 }
00086
00087 hds->hd_list[hds->hd_count++] = hd;
00088 }
00089
00090 void hdhomerun_device_selector_remove_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd)
00091 {
00092 size_t index = 0;
00093 while (1) {
00094 if (index >= hds->hd_count) {
00095 return;
00096 }
00097
00098 struct hdhomerun_device_t *entry = hds->hd_list[index];
00099 if (entry == hd) {
00100 break;
00101 }
00102
00103 index++;
00104 }
00105
00106 while (index + 1 < hds->hd_count) {
00107 hds->hd_list[index] = hds->hd_list[index + 1];
00108 index++;
00109 }
00110
00111 hds->hd_list[index] = NULL;
00112 hds->hd_count--;
00113 }
00114
00115 struct hdhomerun_device_t *hdhomerun_device_selector_find_device(struct hdhomerun_device_selector_t *hds, uint32_t device_id, unsigned int tuner_index)
00116 {
00117 size_t index;
00118 for (index = 0; index < hds->hd_count; index++) {
00119 struct hdhomerun_device_t *entry = hds->hd_list[index];
00120 if (hdhomerun_device_get_device_id(entry) != device_id) {
00121 continue;
00122 }
00123 if (hdhomerun_device_get_tuner(entry) != tuner_index) {
00124 continue;
00125 }
00126 return entry;
00127 }
00128
00129 return NULL;
00130 }
00131
00132 void hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename)
00133 {
00134 FILE *fp = fopen(filename, "r");
00135 if (!fp) {
00136 return;
00137 }
00138
00139 while(1) {
00140 char device_name[32];
00141 if (!fgets(device_name, sizeof(device_name), fp)) {
00142 break;
00143 }
00144
00145 struct hdhomerun_device_t *hd = hdhomerun_device_create_from_str(device_name, hds->dbg);
00146 if (!hd) {
00147 continue;
00148 }
00149
00150 hdhomerun_device_selector_add_device(hds, hd);
00151 }
00152
00153 fclose(fp);
00154 }
00155
00156 #if defined(__WINDOWS__)
00157 void hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource)
00158 {
00159 HKEY tuners_key;
00160 LONG ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Silicondust\\HDHomeRun\\Tuners", 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &tuners_key);
00161 if (ret != ERROR_SUCCESS) {
00162 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open tuners registry key (%ld)\n", ret);
00163 return;
00164 }
00165
00166 DWORD index = 0;
00167 while (1) {
00168
00169 wchar_t wdevice_name[32];
00170 DWORD size = sizeof(wdevice_name);
00171 ret = RegEnumKeyEx(tuners_key, index++, wdevice_name, &size, NULL, NULL, NULL, NULL);
00172 if (ret != ERROR_SUCCESS) {
00173 break;
00174 }
00175
00176
00177 HKEY device_key;
00178 ret = RegOpenKeyEx(tuners_key, wdevice_name, 0, KEY_QUERY_VALUE, &device_key);
00179 if (ret != ERROR_SUCCESS) {
00180 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open registry key for %S (%ld)\n", wdevice_name, ret);
00181 continue;
00182 }
00183
00184 wchar_t wsource_test[32];
00185 size = sizeof(wsource_test);
00186 if (RegQueryValueEx(device_key, L"Source", NULL, NULL, (LPBYTE)&wsource_test, &size) != ERROR_SUCCESS) {
00187 wsprintf(wsource_test, L"Unknown");
00188 }
00189
00190 RegCloseKey(device_key);
00191
00192 if (_wcsicmp(wsource_test, wsource) != 0) {
00193 continue;
00194 }
00195
00196
00197 char device_name[32];
00198 sprintf(device_name, "%S", wdevice_name);
00199
00200 struct hdhomerun_device_t *hd = hdhomerun_device_create_from_str(device_name, hds->dbg);
00201 if (!hd) {
00202 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: invalid device name '%s' / failed to create device object\n", device_name);
00203 continue;
00204 }
00205
00206 hdhomerun_device_selector_add_device(hds, hd);
00207 }
00208
00209 RegCloseKey(tuners_key);
00210 }
00211 #endif
00212
00213 static bool_t hdhomerun_device_selector_choose_test(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *test_hd)
00214 {
00215 const char *name = hdhomerun_device_get_name(test_hd);
00216
00217
00218
00219
00220 char *error;
00221 int ret = hdhomerun_device_tuner_lockkey_request(test_hd, &error);
00222 if (ret > 0) {
00223 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s chosen\n", name);
00224 return true;
00225 }
00226 if (ret < 0) {
00227 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
00228 return false;
00229 }
00230
00231
00232
00233
00234 char *target;
00235 ret = hdhomerun_device_get_tuner_target(test_hd, &target);
00236 if (ret < 0) {
00237 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
00238 return false;
00239 }
00240 if (ret == 0) {
00241 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, failed to read target\n", name);
00242 return false;
00243 }
00244
00245 char *ptr = strstr(target, "//");
00246 if (ptr) {
00247 target = ptr + 2;
00248 }
00249 ptr = strchr(target, ' ');
00250 if (ptr) {
00251 *ptr = 0;
00252 }
00253
00254 unsigned long a[4];
00255 unsigned long target_port;
00256 if (sscanf(target, "%lu.%lu.%lu.%lu:%lu", &a[0], &a[1], &a[2], &a[3], &target_port) != 5) {
00257 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, no target set (%s)\n", name, target);
00258 return false;
00259 }
00260
00261 uint32_t target_ip = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
00262 uint32_t local_ip = hdhomerun_device_get_local_machine_addr(test_hd);
00263 if (target_ip != local_ip) {
00264 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by %s\n", name, target);
00265 return false;
00266 }
00267
00268
00269
00270
00271 int test_sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
00272 if (test_sock == -1) {
00273 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, failed to create test sock\n", name);
00274 return false;
00275 }
00276
00277 struct sockaddr_in sock_addr;
00278 memset(&sock_addr, 0, sizeof(sock_addr));
00279 sock_addr.sin_family = AF_INET;
00280 sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
00281 sock_addr.sin_port = htons((uint16_t)target_port);
00282 ret = bind(test_sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
00283 close(test_sock);
00284
00285 if (ret != 0) {
00286 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine\n", name);
00287 return false;
00288 }
00289
00290
00291
00292
00293 ret = hdhomerun_device_tuner_lockkey_force(test_hd);
00294 if (ret < 0) {
00295 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
00296 return false;
00297 }
00298 if (ret == 0) {
00299 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);
00300 return false;
00301 }
00302
00303 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine, dead target, lockkey force successful\n", name);
00304
00305
00306
00307
00308 ret = hdhomerun_device_tuner_lockkey_request(test_hd, &error);
00309 if (ret > 0) {
00310 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s chosen\n", name);
00311 return true;
00312 }
00313 if (ret < 0) {
00314 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
00315 return false;
00316 }
00317
00318 hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s still in use after lockkey force (%s)\n", name, error);
00319 return false;
00320 }
00321
00322 struct hdhomerun_device_t *hdhomerun_device_selector_choose_and_lock(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *prefered)
00323 {
00324
00325 if (prefered) {
00326 if (hdhomerun_device_selector_choose_test(hds, prefered)) {
00327 return prefered;
00328 }
00329 }
00330
00331
00332 size_t index;
00333 for (index = 0; index < hds->hd_count; index++) {
00334 struct hdhomerun_device_t *entry = hds->hd_list[index];
00335 if (entry == prefered) {
00336 continue;
00337 }
00338
00339 if (hdhomerun_device_selector_choose_test(hds, entry)) {
00340 return entry;
00341 }
00342 }
00343
00344 return NULL;
00345 }