|
MythTV
0.26-pre
|
00001 /* 00002 * hdhomerun_discover.c 00003 * 00004 * Copyright © 2006-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 #define HDHOMERUN_DISOCVER_MAX_SOCK_COUNT 16 00036 00037 struct hdhomerun_discover_sock_t { 00038 hdhomerun_sock_t sock; 00039 bool_t detected; 00040 uint32_t local_ip; 00041 uint32_t subnet_mask; 00042 }; 00043 00044 struct hdhomerun_discover_t { 00045 struct hdhomerun_discover_sock_t socks[HDHOMERUN_DISOCVER_MAX_SOCK_COUNT]; 00046 unsigned int sock_count; 00047 struct hdhomerun_pkt_t tx_pkt; 00048 struct hdhomerun_pkt_t rx_pkt; 00049 }; 00050 00051 static bool_t hdhomerun_discover_sock_add(struct hdhomerun_discover_t *ds, uint32_t local_ip, uint32_t subnet_mask) 00052 { 00053 unsigned int i; 00054 for (i = 1; i < ds->sock_count; i++) { 00055 struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; 00056 00057 if ((dss->local_ip == local_ip) && (dss->subnet_mask == subnet_mask)) { 00058 dss->detected = TRUE; 00059 return TRUE; 00060 } 00061 } 00062 00063 if (ds->sock_count >= HDHOMERUN_DISOCVER_MAX_SOCK_COUNT) { 00064 return FALSE; 00065 } 00066 00067 /* Create socket. */ 00068 hdhomerun_sock_t sock = hdhomerun_sock_create_udp(); 00069 if (sock == HDHOMERUN_SOCK_INVALID) { 00070 return FALSE; 00071 } 00072 00073 /* Bind socket. */ 00074 if (!hdhomerun_sock_bind(sock, local_ip, 0, FALSE)) { 00075 hdhomerun_sock_destroy(sock); 00076 return FALSE; 00077 } 00078 00079 /* Write sock entry. */ 00080 struct hdhomerun_discover_sock_t *dss = &ds->socks[ds->sock_count++]; 00081 dss->sock = sock; 00082 dss->detected = TRUE; 00083 dss->local_ip = local_ip; 00084 dss->subnet_mask = subnet_mask; 00085 00086 return TRUE; 00087 } 00088 00089 struct hdhomerun_discover_t *hdhomerun_discover_create(void) 00090 { 00091 struct hdhomerun_discover_t *ds = (struct hdhomerun_discover_t *)calloc(1, sizeof(struct hdhomerun_discover_t)); 00092 if (!ds) { 00093 return NULL; 00094 } 00095 00096 /* Create a routable socket (always first entry). */ 00097 if (!hdhomerun_discover_sock_add(ds, 0, 0)) { 00098 free(ds); 00099 return NULL; 00100 } 00101 00102 /* Success. */ 00103 return ds; 00104 } 00105 00106 void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds) 00107 { 00108 unsigned int i; 00109 for (i = 0; i < ds->sock_count; i++) { 00110 struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; 00111 hdhomerun_sock_destroy(dss->sock); 00112 } 00113 00114 free(ds); 00115 } 00116 00117 static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds) 00118 { 00119 unsigned int i; 00120 for (i = 1; i < ds->sock_count; i++) { 00121 struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; 00122 dss->detected = FALSE; 00123 } 00124 00125 struct hdhomerun_local_ip_info_t ip_info_list[HDHOMERUN_DISOCVER_MAX_SOCK_COUNT]; 00126 int count = hdhomerun_local_ip_info(ip_info_list, HDHOMERUN_DISOCVER_MAX_SOCK_COUNT); 00127 if (count < 0) { 00128 count = 0; 00129 } 00130 00131 int index; 00132 for (index = 0; index < count; index++) { 00133 struct hdhomerun_local_ip_info_t *ip_info = &ip_info_list[index]; 00134 hdhomerun_discover_sock_add(ds, ip_info->ip_addr, ip_info->subnet_mask); 00135 } 00136 00137 struct hdhomerun_discover_sock_t *src = &ds->socks[1]; 00138 struct hdhomerun_discover_sock_t *dst = &ds->socks[1]; 00139 count = 1; 00140 for (i = 1; i < ds->sock_count; i++) { 00141 if (!src->detected) { 00142 hdhomerun_sock_destroy(src->sock); 00143 src++; 00144 continue; 00145 } 00146 if (dst != src) { 00147 *dst = *src; 00148 } 00149 src++; 00150 dst++; 00151 count++; 00152 } 00153 00154 ds->sock_count = count; 00155 } 00156 00157 static bool_t hdhomerun_discover_send_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, uint32_t target_ip, uint32_t device_type, uint32_t device_id) 00158 { 00159 struct hdhomerun_pkt_t *tx_pkt = &ds->tx_pkt; 00160 hdhomerun_pkt_reset(tx_pkt); 00161 00162 hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_DEVICE_TYPE); 00163 hdhomerun_pkt_write_var_length(tx_pkt, 4); 00164 hdhomerun_pkt_write_u32(tx_pkt, device_type); 00165 hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_DEVICE_ID); 00166 hdhomerun_pkt_write_var_length(tx_pkt, 4); 00167 hdhomerun_pkt_write_u32(tx_pkt, device_id); 00168 hdhomerun_pkt_seal_frame(tx_pkt, HDHOMERUN_TYPE_DISCOVER_REQ); 00169 00170 return hdhomerun_sock_sendto(dss->sock, target_ip, HDHOMERUN_DISCOVER_UDP_PORT, tx_pkt->start, tx_pkt->end - tx_pkt->start, 0); 00171 } 00172 00173 static bool_t hdhomerun_discover_send_wildcard_ip(struct hdhomerun_discover_t *ds, uint32_t device_type, uint32_t device_id) 00174 { 00175 bool_t result = FALSE; 00176 00177 /* 00178 * Send subnet broadcast using each local ip socket. 00179 * This will work with multiple separate 169.254.x.x interfaces. 00180 */ 00181 unsigned int i; 00182 for (i = 1; i < ds->sock_count; i++) { 00183 struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; 00184 uint32_t target_ip = dss->local_ip | ~dss->subnet_mask; 00185 result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id); 00186 } 00187 00188 /* 00189 * If no local ip sockets then fall back to sending a global broadcast letting the OS choose the interface. 00190 */ 00191 if (!result) { 00192 struct hdhomerun_discover_sock_t *dss = &ds->socks[0]; 00193 result = hdhomerun_discover_send_internal(ds, dss, 0xFFFFFFFF, device_type, device_id); 00194 } 00195 00196 return result; 00197 } 00198 00199 static bool_t hdhomerun_discover_send_target_ip(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id) 00200 { 00201 bool_t result = FALSE; 00202 00203 /* 00204 * Send targeted packet from any local ip that is in the same subnet. 00205 * This will work with multiple separate 169.254.x.x interfaces. 00206 */ 00207 unsigned int i; 00208 for (i = 1; i < ds->sock_count; i++) { 00209 struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; 00210 if ((target_ip & dss->subnet_mask) != (dss->local_ip & dss->subnet_mask)) { 00211 continue; 00212 } 00213 00214 result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id); 00215 } 00216 00217 /* 00218 * If target IP does not match a local subnet then fall back to letting the OS choose the gateway interface. 00219 */ 00220 if (!result) { 00221 struct hdhomerun_discover_sock_t *dss = &ds->socks[0]; 00222 result = hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id); 00223 } 00224 00225 return result; 00226 } 00227 00228 static bool_t hdhomerun_discover_send(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id) 00229 { 00230 if (target_ip == 0) { 00231 return hdhomerun_discover_send_wildcard_ip(ds, device_type, device_id); 00232 } else { 00233 return hdhomerun_discover_send_target_ip(ds, target_ip, device_type, device_id); 00234 } 00235 } 00236 00237 static bool_t hdhomerun_discover_recv_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, struct hdhomerun_discover_device_t *result) 00238 { 00239 struct hdhomerun_pkt_t *rx_pkt = &ds->rx_pkt; 00240 hdhomerun_pkt_reset(rx_pkt); 00241 00242 uint32_t remote_addr; 00243 uint16_t remote_port; 00244 size_t length = rx_pkt->limit - rx_pkt->end; 00245 if (!hdhomerun_sock_recvfrom(dss->sock, &remote_addr, &remote_port, rx_pkt->end, &length, 0)) { 00246 return FALSE; 00247 } 00248 00249 rx_pkt->end += length; 00250 00251 uint16_t type; 00252 if (hdhomerun_pkt_open_frame(rx_pkt, &type) <= 0) { 00253 return FALSE; 00254 } 00255 if (type != HDHOMERUN_TYPE_DISCOVER_RPY) { 00256 return FALSE; 00257 } 00258 00259 result->ip_addr = remote_addr; 00260 result->device_type = 0; 00261 result->device_id = 0; 00262 result->tuner_count = 0; 00263 00264 while (1) { 00265 uint8_t tag; 00266 size_t len; 00267 uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len); 00268 if (!next) { 00269 break; 00270 } 00271 00272 switch (tag) { 00273 case HDHOMERUN_TAG_DEVICE_TYPE: 00274 if (len != 4) { 00275 break; 00276 } 00277 result->device_type = hdhomerun_pkt_read_u32(rx_pkt); 00278 break; 00279 00280 case HDHOMERUN_TAG_DEVICE_ID: 00281 if (len != 4) { 00282 break; 00283 } 00284 result->device_id = hdhomerun_pkt_read_u32(rx_pkt); 00285 break; 00286 00287 case HDHOMERUN_TAG_TUNER_COUNT: 00288 if (len != 1) { 00289 break; 00290 } 00291 result->tuner_count = hdhomerun_pkt_read_u8(rx_pkt); 00292 break; 00293 00294 default: 00295 break; 00296 } 00297 00298 rx_pkt->pos = next; 00299 } 00300 00301 /* Fixup for old firmware. */ 00302 if (result->tuner_count == 0) { 00303 switch (result->device_id >> 20) { 00304 case 0x102: 00305 result->tuner_count = 1; 00306 break; 00307 00308 case 0x100: 00309 case 0x101: 00310 case 0x121: 00311 result->tuner_count = 2; 00312 break; 00313 00314 default: 00315 break; 00316 } 00317 } 00318 00319 return TRUE; 00320 } 00321 00322 static bool_t hdhomerun_discover_recv(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_device_t *result) 00323 { 00324 unsigned int i; 00325 for (i = 0; i < ds->sock_count; i++) { 00326 struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; 00327 00328 if (hdhomerun_discover_recv_internal(ds, dss, result)) { 00329 return TRUE; 00330 } 00331 } 00332 00333 return FALSE; 00334 } 00335 00336 static struct hdhomerun_discover_device_t *hdhomerun_discover_find_in_list(struct hdhomerun_discover_device_t result_list[], int count, struct hdhomerun_discover_device_t *lookup) 00337 { 00338 int index; 00339 for (index = 0; index < count; index++) { 00340 struct hdhomerun_discover_device_t *entry = &result_list[index]; 00341 if (memcmp(lookup, entry, sizeof(struct hdhomerun_discover_device_t)) == 0) { 00342 return entry; 00343 } 00344 } 00345 00346 return NULL; 00347 } 00348 00349 int hdhomerun_discover_find_devices(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count) 00350 { 00351 hdhomerun_discover_sock_detect(ds); 00352 00353 int count = 0; 00354 int attempt; 00355 for (attempt = 0; attempt < 2; attempt++) { 00356 if (!hdhomerun_discover_send(ds, target_ip, device_type, device_id)) { 00357 return -1; 00358 } 00359 00360 uint64_t timeout = getcurrenttime() + 200; 00361 while (1) { 00362 struct hdhomerun_discover_device_t *result = &result_list[count]; 00363 memset(result, 0, sizeof(struct hdhomerun_discover_device_t)); 00364 00365 if (!hdhomerun_discover_recv(ds, result)) { 00366 if (getcurrenttime() >= timeout) { 00367 break; 00368 } 00369 msleep_approx(10); 00370 continue; 00371 } 00372 00373 /* Filter. */ 00374 if (device_type != HDHOMERUN_DEVICE_TYPE_WILDCARD) { 00375 if (device_type != result->device_type) { 00376 continue; 00377 } 00378 } 00379 if (device_id != HDHOMERUN_DEVICE_ID_WILDCARD) { 00380 if (device_id != result->device_id) { 00381 continue; 00382 } 00383 } 00384 00385 /* Ensure not already in list. */ 00386 if (hdhomerun_discover_find_in_list(result_list, count, result)) { 00387 continue; 00388 } 00389 00390 /* Add to list. */ 00391 count++; 00392 if (count >= max_count) { 00393 return count; 00394 } 00395 } 00396 } 00397 00398 return count; 00399 } 00400 00401 int hdhomerun_discover_find_devices_custom(uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count) 00402 { 00403 if (hdhomerun_discover_is_ip_multicast(target_ip)) { 00404 return 0; 00405 } 00406 00407 struct hdhomerun_discover_t *ds = hdhomerun_discover_create(); 00408 if (!ds) { 00409 return -1; 00410 } 00411 00412 int ret = hdhomerun_discover_find_devices(ds, target_ip, device_type, device_id, result_list, max_count); 00413 00414 hdhomerun_discover_destroy(ds); 00415 return ret; 00416 } 00417 00418 bool_t hdhomerun_discover_validate_device_id(uint32_t device_id) 00419 { 00420 static uint32_t lookup_table[16] = {0xA, 0x5, 0xF, 0x6, 0x7, 0xC, 0x1, 0xB, 0x9, 0x2, 0x8, 0xD, 0x4, 0x3, 0xE, 0x0}; 00421 00422 uint32_t checksum = 0; 00423 00424 checksum ^= lookup_table[(device_id >> 28) & 0x0F]; 00425 checksum ^= (device_id >> 24) & 0x0F; 00426 checksum ^= lookup_table[(device_id >> 20) & 0x0F]; 00427 checksum ^= (device_id >> 16) & 0x0F; 00428 checksum ^= lookup_table[(device_id >> 12) & 0x0F]; 00429 checksum ^= (device_id >> 8) & 0x0F; 00430 checksum ^= lookup_table[(device_id >> 4) & 0x0F]; 00431 checksum ^= (device_id >> 0) & 0x0F; 00432 00433 return (checksum == 0); 00434 } 00435 00436 bool_t hdhomerun_discover_is_ip_multicast(uint32_t ip_addr) 00437 { 00438 return (ip_addr >= 0xE0000000) && (ip_addr < 0xF0000000); 00439 }
1.7.6.1