libhdhomerun/hdhomerun.h0000664000175000017500000000226713013646353014675 0ustar buildbuild/* * hdhomerun.h * * Copyright © 2006-2010 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun_os.h" #include "hdhomerun_types.h" #include "hdhomerun_pkt.h" #include "hdhomerun_sock.h" #include "hdhomerun_debug.h" #include "hdhomerun_discover.h" #include "hdhomerun_control.h" #include "hdhomerun_video.h" #include "hdhomerun_channels.h" #include "hdhomerun_channelscan.h" #include "hdhomerun_device.h" #include "hdhomerun_device_selector.h" libhdhomerun/hdhomerun_channels.c0000664000175000017500000003070213737525222016542 0ustar buildbuild/* * hdhomerun_channels.c * * Copyright © 2007-2014 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" struct hdhomerun_channel_entry_t { struct hdhomerun_channel_entry_t *next; struct hdhomerun_channel_entry_t *prev; uint32_t frequency; uint16_t channel_number; char name[16]; }; struct hdhomerun_channel_list_t { struct hdhomerun_channel_entry_t *head; struct hdhomerun_channel_entry_t *tail; }; struct hdhomerun_channelmap_range_t { uint16_t channel_range_start; uint16_t channel_range_end; uint32_t frequency; uint32_t spacing; }; struct hdhomerun_channelmap_record_t { const char *channelmap; const struct hdhomerun_channelmap_range_t *range_list; const char *channelmap_scan_group; const char *countrycodes; }; /* AU antenna channels. Channels {6, 7, 8, 9, 9A} are numbered {5, 6, 7, 8, 9} by the HDHomeRun. */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_au_bcast[] = { { 5, 12, 177500000, 7000000}, { 21, 69, 480500000, 7000000}, { 0, 0, 0, 0} }; /* EU antenna channels. */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_eu_bcast[] = { { 5, 12, 177500000, 7000000}, { 21, 69, 474000000, 8000000}, { 0, 0, 0, 0} }; /* EU cable channels. No common standard - use frequency in MHz for channel number. */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_eu_cable[] = { {108, 862, 108000000, 1000000}, { 0, 0, 0, 0} }; /* KR cable channels. */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_kr_cable[] = { { 2, 4, 57000000, 6000000}, { 5, 6, 79000000, 6000000}, { 7, 13, 177000000, 6000000}, { 14, 22, 123000000, 6000000}, { 23, 153, 219000000, 6000000}, { 0, 0, 0, 0} }; /* JP antenna channels. */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_jp_bcast[] = { { 13, 62, 473000000, 6000000}, { 0, 0, 0, 0} }; /* TW antenna channels. */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_tw_bcast[] = { { 7, 13, 177000000, 6000000}, { 14, 69, 473000000, 6000000}, { 0, 0, 0, 0} }; /* US antenna channels. */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_us_bcast[] = { { 2, 4, 57000000, 6000000}, { 5, 6, 79000000, 6000000}, { 7, 13, 177000000, 6000000}, { 14, 36, 473000000, 6000000}, { 38, 51, 617000000, 6000000}, { 0, 0, 0, 0} }; /* US cable channels. */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_us_cable[] = { { 2, 4, 57000000, 6000000}, { 5, 6, 79000000, 6000000}, { 7, 13, 177000000, 6000000}, { 14, 22, 123000000, 6000000}, { 23, 94, 219000000, 6000000}, { 95, 99, 93000000, 6000000}, {100, 158, 651000000, 6000000}, { 0, 0, 0, 0} }; /* US cable channels (HRC). */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_us_hrc[] = { { 2, 4, 55752700, 6000300}, { 5, 6, 79753900, 6000300}, { 7, 13, 175758700, 6000300}, { 14, 22, 121756000, 6000300}, { 23, 94, 217760800, 6000300}, { 95, 99, 91754500, 6000300}, {100, 158, 649782400, 6000300}, { 0, 0, 0, 0} }; /* US cable channels (IRC). */ static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_us_irc[] = { { 2, 4, 57012500, 6000000}, { 5, 6, 81012500, 6000000}, { 7, 13, 177012500, 6000000}, { 14, 22, 123012500, 6000000}, { 23, 41, 219012500, 6000000}, { 42, 42, 333025000, 6000000}, { 43, 94, 339012500, 6000000}, { 95, 97, 93012500, 6000000}, { 98, 99, 111025000, 6000000}, {100, 158, 651012500, 6000000}, { 0, 0, 0, 0} }; static const struct hdhomerun_channelmap_record_t hdhomerun_channelmap_table[] = { {"au-bcast", hdhomerun_channelmap_range_au_bcast, "au-bcast", "AU"}, {"au-cable", hdhomerun_channelmap_range_eu_cable, "au-cable", "AU"}, {"eu-bcast", hdhomerun_channelmap_range_eu_bcast, "eu-bcast", NULL}, {"eu-cable", hdhomerun_channelmap_range_eu_cable, "eu-cable", NULL}, {"tw-bcast", hdhomerun_channelmap_range_tw_bcast, "tw-bcast", "TW"}, {"tw-cable", hdhomerun_channelmap_range_us_cable, "tw-cable", "TW"}, {"kr-bcast", hdhomerun_channelmap_range_us_bcast, "kr-bcast", "KR"}, {"kr-cable", hdhomerun_channelmap_range_kr_cable, "kr-cable", "KR"}, {"us-bcast", hdhomerun_channelmap_range_us_bcast, "us-bcast", NULL}, {"us-cable", hdhomerun_channelmap_range_us_cable, "us-cable us-hrc us-irc", NULL}, {"us-hrc", hdhomerun_channelmap_range_us_hrc , "us-cable us-hrc us-irc", NULL}, {"us-irc", hdhomerun_channelmap_range_us_irc, "us-cable us-hrc us-irc", NULL}, {"jp-bcast", hdhomerun_channelmap_range_jp_bcast, "jp-bcast", "JP" }, { NULL, NULL, NULL, NULL } }; const char *hdhomerun_channelmap_get_channelmap_from_country_source(const char *countrycode, const char *source, const char *supported) { const char *default_result = NULL; const struct hdhomerun_channelmap_record_t *record = hdhomerun_channelmap_table; while (record->channelmap) { /* Ignore records that do not match the requested source. */ if (!strstr(record->channelmap, source)) { record++; continue; } /* Ignore records that are not supported by the hardware. */ if (!strstr(supported, record->channelmap)) { record++; continue; } /* If this record is the default result then remember it and keep searching. */ if (!record->countrycodes) { default_result = record->channelmap; record++; continue; } /* Ignore records that have a countrycode filter and do not match. */ if (!strstr(record->countrycodes, countrycode)) { record++; continue; } /* Record found with exact match for source and countrycode. */ return record->channelmap; } return default_result; } const char *hdhomerun_channelmap_get_channelmap_scan_group(const char *channelmap) { const struct hdhomerun_channelmap_record_t *record = hdhomerun_channelmap_table; while (record->channelmap) { if (strstr(channelmap, record->channelmap)) { return record->channelmap_scan_group; } record++; } return NULL; } uint16_t hdhomerun_channel_entry_channel_number(struct hdhomerun_channel_entry_t *entry) { return entry->channel_number; } uint32_t hdhomerun_channel_entry_frequency(struct hdhomerun_channel_entry_t *entry) { return entry->frequency; } const char *hdhomerun_channel_entry_name(struct hdhomerun_channel_entry_t *entry) { return entry->name; } struct hdhomerun_channel_entry_t *hdhomerun_channel_list_first(struct hdhomerun_channel_list_t *channel_list) { return channel_list->head; } struct hdhomerun_channel_entry_t *hdhomerun_channel_list_last(struct hdhomerun_channel_list_t *channel_list) { return channel_list->tail; } struct hdhomerun_channel_entry_t *hdhomerun_channel_list_next(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry) { return entry->next; } struct hdhomerun_channel_entry_t *hdhomerun_channel_list_prev(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry) { return entry->prev; } uint32_t hdhomerun_channel_list_total_count(struct hdhomerun_channel_list_t *channel_list) { uint32_t count = 0; struct hdhomerun_channel_entry_t *entry = hdhomerun_channel_list_first(channel_list); while (entry) { count++; entry = hdhomerun_channel_list_next(channel_list, entry); } return count; } uint32_t hdhomerun_channel_list_frequency_count(struct hdhomerun_channel_list_t *channel_list) { uint32_t count = 0; uint32_t last_frequency = 0; struct hdhomerun_channel_entry_t *entry = hdhomerun_channel_list_first(channel_list); while (entry) { if (entry->frequency != last_frequency) { last_frequency = entry->frequency; count++; } entry = hdhomerun_channel_list_next(channel_list, entry); } return count; } uint32_t hdhomerun_channel_frequency_round(uint32_t frequency, uint32_t resolution) { frequency += resolution / 2; return (frequency / resolution) * resolution; } uint32_t hdhomerun_channel_frequency_round_normal(uint32_t frequency) { return hdhomerun_channel_frequency_round(frequency, 125000); } uint32_t hdhomerun_channel_number_to_frequency(struct hdhomerun_channel_list_t *channel_list, uint16_t channel_number) { struct hdhomerun_channel_entry_t *entry = hdhomerun_channel_list_first(channel_list); while (entry) { if (entry->channel_number == channel_number) { return entry->frequency; } entry = hdhomerun_channel_list_next(channel_list, entry); } return 0; } uint16_t hdhomerun_channel_frequency_to_number(struct hdhomerun_channel_list_t *channel_list, uint32_t frequency) { frequency = hdhomerun_channel_frequency_round_normal(frequency); struct hdhomerun_channel_entry_t *entry = hdhomerun_channel_list_first(channel_list); while (entry) { if (entry->frequency == frequency) { return entry->channel_number; } if (entry->frequency > frequency) { return 0; } entry = hdhomerun_channel_list_next(channel_list, entry); } return 0; } static void hdhomerun_channel_list_build_insert(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry) { struct hdhomerun_channel_entry_t *prev = NULL; struct hdhomerun_channel_entry_t *next = channel_list->head; while (next) { if (next->frequency > entry->frequency) { break; } prev = next; next = next->next; } entry->prev = prev; entry->next = next; if (prev) { prev->next = entry; } else { channel_list->head = entry; } if (next) { next->prev = entry; } else { channel_list->tail = entry; } } static void hdhomerun_channel_list_build_range(struct hdhomerun_channel_list_t *channel_list, const char *channelmap, const struct hdhomerun_channelmap_range_t *range) { uint16_t channel_number; for (channel_number = range->channel_range_start; channel_number <= range->channel_range_end; channel_number++) { struct hdhomerun_channel_entry_t *entry = (struct hdhomerun_channel_entry_t *)calloc(1, sizeof(struct hdhomerun_channel_entry_t)); if (!entry) { return; } entry->channel_number = channel_number; entry->frequency = range->frequency + ((uint32_t)(channel_number - range->channel_range_start) * range->spacing); entry->frequency = hdhomerun_channel_frequency_round_normal(entry->frequency); hdhomerun_sprintf(entry->name, entry->name + sizeof(entry->name), "%s:%u", channelmap, entry->channel_number); hdhomerun_channel_list_build_insert(channel_list, entry); } } static void hdhomerun_channel_list_build_ranges(struct hdhomerun_channel_list_t *channel_list, const struct hdhomerun_channelmap_record_t *record) { const struct hdhomerun_channelmap_range_t *range = record->range_list; while (range->frequency) { hdhomerun_channel_list_build_range(channel_list, record->channelmap, range); range++; } } void hdhomerun_channel_list_destroy(struct hdhomerun_channel_list_t *channel_list) { while (channel_list->head) { struct hdhomerun_channel_entry_t *entry = channel_list->head; channel_list->head = entry->next; free(entry); } free(channel_list); } struct hdhomerun_channel_list_t *hdhomerun_channel_list_create(const char *channelmap) { struct hdhomerun_channel_list_t *channel_list = (struct hdhomerun_channel_list_t *)calloc(1, sizeof(struct hdhomerun_channel_list_t)); if (!channel_list) { return NULL; } const struct hdhomerun_channelmap_record_t *record = hdhomerun_channelmap_table; while (record->channelmap) { if (!strstr(channelmap, record->channelmap)) { record++; continue; } hdhomerun_channel_list_build_ranges(channel_list, record); record++; } if (!channel_list->head) { free(channel_list); return NULL; } return channel_list; } libhdhomerun/hdhomerun_channels.h0000664000175000017500000000611713013646353016546 0ustar buildbuild/* * hdhomerun_channels.h * * Copyright © 2007-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif struct hdhomerun_channel_entry_t; struct hdhomerun_channel_list_t; extern LIBHDHOMERUN_API const char *hdhomerun_channelmap_get_channelmap_from_country_source(const char *countrycode, const char *source, const char *supported); extern LIBHDHOMERUN_API const char *hdhomerun_channelmap_get_channelmap_scan_group(const char *channelmap); extern LIBHDHOMERUN_API uint16_t hdhomerun_channel_entry_channel_number(struct hdhomerun_channel_entry_t *entry); extern LIBHDHOMERUN_API uint32_t hdhomerun_channel_entry_frequency(struct hdhomerun_channel_entry_t *entry); extern LIBHDHOMERUN_API const char *hdhomerun_channel_entry_name(struct hdhomerun_channel_entry_t *entry); extern LIBHDHOMERUN_API struct hdhomerun_channel_list_t *hdhomerun_channel_list_create(const char *channelmap); extern LIBHDHOMERUN_API void hdhomerun_channel_list_destroy(struct hdhomerun_channel_list_t *channel_list); extern LIBHDHOMERUN_API struct hdhomerun_channel_entry_t *hdhomerun_channel_list_first(struct hdhomerun_channel_list_t *channel_list); extern LIBHDHOMERUN_API struct hdhomerun_channel_entry_t *hdhomerun_channel_list_last(struct hdhomerun_channel_list_t *channel_list); extern LIBHDHOMERUN_API struct hdhomerun_channel_entry_t *hdhomerun_channel_list_next(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry); extern LIBHDHOMERUN_API struct hdhomerun_channel_entry_t *hdhomerun_channel_list_prev(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry); extern LIBHDHOMERUN_API uint32_t hdhomerun_channel_list_total_count(struct hdhomerun_channel_list_t *channel_list); extern LIBHDHOMERUN_API uint32_t hdhomerun_channel_list_frequency_count(struct hdhomerun_channel_list_t *channel_list); extern LIBHDHOMERUN_API uint32_t hdhomerun_channel_frequency_round(uint32_t frequency, uint32_t resolution); extern LIBHDHOMERUN_API uint32_t hdhomerun_channel_frequency_round_normal(uint32_t frequency); extern LIBHDHOMERUN_API uint32_t hdhomerun_channel_number_to_frequency(struct hdhomerun_channel_list_t *channel_list, uint16_t channel_number); extern LIBHDHOMERUN_API uint16_t hdhomerun_channel_frequency_to_number(struct hdhomerun_channel_list_t *channel_list, uint32_t frequency); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_channelscan.c0000664000175000017500000002111313063620312017204 0ustar buildbuild/* * hdhomerun_channelscan.c * * Copyright © 2007-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" struct hdhomerun_channelscan_t { struct hdhomerun_device_t *hd; uint32_t scanned_channels; struct hdhomerun_channel_list_t *channel_list; struct hdhomerun_channel_entry_t *next_channel; }; struct hdhomerun_channelscan_t *channelscan_create(struct hdhomerun_device_t *hd, const char *channelmap) { struct hdhomerun_channelscan_t *scan = (struct hdhomerun_channelscan_t *)calloc(1, sizeof(struct hdhomerun_channelscan_t)); if (!scan) { return NULL; } scan->hd = hd; scan->channel_list = hdhomerun_channel_list_create(channelmap); if (!scan->channel_list) { free(scan); return NULL; } scan->next_channel = hdhomerun_channel_list_last(scan->channel_list); return scan; } void channelscan_destroy(struct hdhomerun_channelscan_t *scan) { hdhomerun_channel_list_destroy(scan->channel_list); free(scan); } static int channelscan_find_lock(struct hdhomerun_channelscan_t *scan, uint32_t frequency, struct hdhomerun_channelscan_result_t *result) { /* Set channel. */ char channel_str[64]; hdhomerun_sprintf(channel_str, channel_str + sizeof(channel_str), "auto:%u", (unsigned int)frequency); int ret = hdhomerun_device_set_tuner_channel(scan->hd, channel_str); if (ret <= 0) { return ret; } /* Wait for lock. */ ret = hdhomerun_device_wait_for_lock(scan->hd, &result->status); if (ret <= 0) { return ret; } if (!result->status.lock_supported) { return 1; } /* Wait for symbol quality = 100%. */ uint64_t timeout = getcurrenttime() + 5000; while (1) { ret = hdhomerun_device_get_tuner_status(scan->hd, NULL, &result->status); if (ret <= 0) { return ret; } if (result->status.symbol_error_quality == 100) { return 1; } if (getcurrenttime() >= timeout) { return 1; } msleep_approx(250); } } static void channelscan_extract_name(struct hdhomerun_channelscan_program_t *program, const char *line) { /* Find start of name. */ const char *start = strchr(line, ' '); if (!start) { return; } start++; start = strchr(start, ' '); if (!start) { return; } start++; /* Find end of name. */ const char *end = strstr(start, " ("); if (!end) { end = strchr(line, 0); } if (end <= start) { return; } /* Extract name. */ size_t length = (size_t)(end - start); if (length > sizeof(program->name) - 1) { length = sizeof(program->name) - 1; } strncpy(program->name, start, length); program->name[length] = 0; } static int channelscan_detect_programs(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result, bool *pchanged, bool *pincomplete) { *pchanged = false; *pincomplete = false; char *streaminfo; int ret = hdhomerun_device_get_tuner_streaminfo(scan->hd, &streaminfo); if (ret <= 0) { return ret; } char *next_line = streaminfo; int program_count = 0; while (1) { char *line = next_line; next_line = strchr(line, '\n'); if (!next_line) { break; } *next_line++ = 0; unsigned int transport_stream_id; if (sscanf(line, "tsid=0x%x", &transport_stream_id) == 1) { result->transport_stream_id = (uint16_t)transport_stream_id; result->transport_stream_id_detected = true; continue; } unsigned int original_network_id; if (sscanf(line, "onid=0x%x", &original_network_id) == 1) { result->original_network_id = (uint16_t)original_network_id; result->original_network_id_detected = true; continue; } if (program_count >= HDHOMERUN_CHANNELSCAN_MAX_PROGRAM_COUNT) { continue; } struct hdhomerun_channelscan_program_t program; memset(&program, 0, sizeof(program)); hdhomerun_sprintf(program.program_str, program.program_str + sizeof(program.program_str), "%s", line); unsigned int program_number; unsigned int virtual_major, virtual_minor; if (sscanf(line, "%u: %u.%u", &program_number, &virtual_major, &virtual_minor) != 3) { if (sscanf(line, "%u: %u", &program_number, &virtual_major) != 2) { continue; } virtual_minor = 0; } program.program_number = (uint16_t)program_number; program.virtual_major = (uint16_t)virtual_major; program.virtual_minor = (uint16_t)virtual_minor; channelscan_extract_name(&program, line); if (strstr(line, "(control)")) { program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_CONTROL; } else if (strstr(line, "(encrypted)")) { program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_ENCRYPTED; } else if (strstr(line, "(no data)")) { program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_NODATA; *pincomplete = true; } else { program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_NORMAL; if ((program.virtual_major == 0) || (program.name[0] == 0)) { *pincomplete = true; } } if (memcmp(&result->programs[program_count], &program, sizeof(program)) != 0) { memcpy(&result->programs[program_count], &program, sizeof(program)); *pchanged = true; } program_count++; } if (program_count == 0) { *pincomplete = true; } if (result->program_count != program_count) { result->program_count = program_count; *pchanged = true; } return 1; } int channelscan_advance(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result) { memset(result, 0, sizeof(struct hdhomerun_channelscan_result_t)); struct hdhomerun_channel_entry_t *entry = scan->next_channel; if (!entry) { return 0; } /* Combine channels with same frequency. */ result->frequency = hdhomerun_channel_entry_frequency(entry); char *ptr = result->channel_str; char *end = result->channel_str + sizeof(result->channel_str); hdhomerun_sprintf(ptr, end, hdhomerun_channel_entry_name(entry)); while (1) { entry = hdhomerun_channel_list_prev(scan->channel_list, entry); if (!entry) { scan->next_channel = NULL; break; } if (hdhomerun_channel_entry_frequency(entry) != result->frequency) { scan->next_channel = entry; break; } ptr = strchr(ptr, 0); hdhomerun_sprintf(ptr, end, ", %s", hdhomerun_channel_entry_name(entry)); } return 1; } int channelscan_detect(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result) { scan->scanned_channels++; /* Find lock. */ int ret = channelscan_find_lock(scan, result->frequency, result); if (ret <= 0) { return ret; } if (!result->status.lock_supported) { return 1; } /* Detect programs. */ result->program_count = 0; uint64_t timeout; if (strstr(hdhomerun_device_get_model_str(scan->hd), "atsc")) { timeout = getcurrenttime() + 4000; } else { timeout = getcurrenttime() + 10000; } uint64_t complete_time = getcurrenttime() + 1000; while (1) { bool changed, incomplete; ret = channelscan_detect_programs(scan, result, &changed, &incomplete); if (ret <= 0) { return ret; } if (changed) { complete_time = getcurrenttime() + 1000; } if (!incomplete && (getcurrenttime() >= complete_time)) { break; } if (getcurrenttime() >= timeout) { break; } msleep_approx(250); } /* Lock => skip overlapping channels. */ uint32_t max_next_frequency = result->frequency - 5500000; while (1) { if (!scan->next_channel) { break; } if (hdhomerun_channel_entry_frequency(scan->next_channel) <= max_next_frequency) { break; } scan->next_channel = hdhomerun_channel_list_prev(scan->channel_list, scan->next_channel); } /* Success. */ return 1; } uint8_t channelscan_get_progress(struct hdhomerun_channelscan_t *scan) { struct hdhomerun_channel_entry_t *entry = scan->next_channel; if (!entry) { return 100; } uint32_t channels_remaining = 1; uint32_t frequency = hdhomerun_channel_entry_frequency(entry); while (1) { entry = hdhomerun_channel_list_prev(scan->channel_list, entry); if (!entry) { break; } if (hdhomerun_channel_entry_frequency(entry) != frequency) { channels_remaining++; frequency = hdhomerun_channel_entry_frequency(entry); } } return (uint8_t) (scan->scanned_channels * 100 / (scan->scanned_channels + channels_remaining)); } libhdhomerun/hdhomerun_channelscan.h0000664000175000017500000000330213013646353017221 0ustar buildbuild/* * hdhomerun_channelscan.h * * Copyright © 2007-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif #define HDHOMERUN_CHANNELSCAN_PROGRAM_NORMAL 0 #define HDHOMERUN_CHANNELSCAN_PROGRAM_NODATA 1 #define HDHOMERUN_CHANNELSCAN_PROGRAM_CONTROL 2 #define HDHOMERUN_CHANNELSCAN_PROGRAM_ENCRYPTED 3 struct hdhomerun_channelscan_t; extern LIBHDHOMERUN_API struct hdhomerun_channelscan_t *channelscan_create(struct hdhomerun_device_t *hd, const char *channelmap); extern LIBHDHOMERUN_API void channelscan_destroy(struct hdhomerun_channelscan_t *scan); extern LIBHDHOMERUN_API int channelscan_advance(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result); extern LIBHDHOMERUN_API int channelscan_detect(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result); extern LIBHDHOMERUN_API uint8_t channelscan_get_progress(struct hdhomerun_channelscan_t *scan); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_config.c0000664000175000017500000003525713737525222016226 0ustar buildbuild/* * hdhomerun_config.c * * Copyright © 2006-2017 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" static const char *appname; struct hdhomerun_device_t *hd; static int help(void) { printf("Usage:\n"); printf("\t%s discover\n", appname); printf("\t%s get help\n", appname); printf("\t%s get \n", appname); printf("\t%s set \n", appname); printf("\t%s scan []\n", appname); printf("\t%s save \n", appname); printf("\t%s upgrade \n", appname); return -1; } static void extract_appname(const char *argv0) { const char *ptr = strrchr(argv0, '/'); if (ptr) { argv0 = ptr + 1; } ptr = strrchr(argv0, '\\'); if (ptr) { argv0 = ptr + 1; } appname = argv0; } static bool contains(const char *arg, const char *cmpstr) { if (strcmp(arg, cmpstr) == 0) { return true; } if (*arg++ != '-') { return false; } if (*arg++ != '-') { return false; } if (strcmp(arg, cmpstr) == 0) { return true; } return false; } static uint32_t parse_ip_addr(const char *str) { unsigned int a[4]; if (sscanf(str, "%u.%u.%u.%u", &a[0], &a[1], &a[2], &a[3]) != 4) { return 0; } return (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0)); } static int discover_print(char *target_ip_str) { uint32_t target_ip = 0; if (target_ip_str) { target_ip = parse_ip_addr(target_ip_str); if (target_ip == 0) { fprintf(stderr, "invalid ip address: %s\n", target_ip_str); return -1; } } struct hdhomerun_discover_device_t result_list[64]; int result_count = hdhomerun_discover_find_devices_custom_v2(target_ip, HDHOMERUN_DEVICE_TYPE_WILDCARD, HDHOMERUN_DEVICE_ID_WILDCARD, result_list, 64); if (result_count < 0) { fprintf(stderr, "error sending discover request\n"); return -1; } struct hdhomerun_discover_device_t *result = result_list; struct hdhomerun_discover_device_t *result_end = result_list + result_count; int valid_count = 0; while (result < result_end) { if (result->device_id == 0) { result++; continue; } printf("hdhomerun device %08X found at %u.%u.%u.%u\n", (unsigned int)result->device_id, (unsigned int)(result->ip_addr >> 24) & 0x0FF, (unsigned int)(result->ip_addr >> 16) & 0x0FF, (unsigned int)(result->ip_addr >> 8) & 0x0FF, (unsigned int)(result->ip_addr >> 0) & 0x0FF ); valid_count++; result++; } if (valid_count == 0) { printf("no devices found\n"); } return valid_count; } static int cmd_get(const char *item) { char *ret_value; char *ret_error; if (hdhomerun_device_get_var(hd, item, &ret_value, &ret_error) < 0) { fprintf(stderr, "communication error sending request to hdhomerun device\n"); return -1; } if (ret_error) { printf("%s\n", ret_error); return 0; } printf("%s\n", ret_value); return 1; } static int cmd_set_internal(const char *item, const char *value) { char *ret_error; if (hdhomerun_device_set_var(hd, item, value, NULL, &ret_error) < 0) { fprintf(stderr, "communication error sending request to hdhomerun device\n"); return -1; } if (ret_error) { printf("%s\n", ret_error); return 0; } return 1; } static int cmd_set(const char *item, const char *value) { if (strcmp(value, "-") == 0) { char *buffer = NULL; size_t pos = 0; while (1) { char *new_buffer = (char *)realloc(buffer, pos + 1024); if (!new_buffer) { fprintf(stderr, "out of memory\n"); return -1; } buffer = new_buffer; size_t size = fread(buffer + pos, 1, 1024, stdin); pos += size; if (size < 1024) { break; } } buffer[pos] = 0; int ret = cmd_set_internal(item, buffer); free(buffer); return ret; } return cmd_set_internal(item, value); } static volatile sig_atomic_t sigabort_flag = false; static volatile sig_atomic_t siginfo_flag = false; static void sigabort_handler(int arg) { sigabort_flag = true; } static void siginfo_handler(int arg) { siginfo_flag = true; } static void register_signal_handlers(sig_t sigpipe_handler, sig_t sigint_handler, sig_t siginfo_handler) { #if defined(SIGPIPE) signal(SIGPIPE, sigpipe_handler); #endif #if defined(SIGINT) signal(SIGINT, sigint_handler); #endif #if defined(SIGINFO) signal(SIGINFO, siginfo_handler); #endif } static void cmd_scan_printf(FILE *fp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (fp) { va_list apc; va_copy(apc, ap); vfprintf(fp, fmt, apc); fflush(fp); va_end(apc); } vprintf(fmt, ap); fflush(stdout); va_end(ap); } static int cmd_scan(const char *tuner_str, const char *filename) { if (hdhomerun_device_set_tuner_from_str(hd, tuner_str) <= 0) { fprintf(stderr, "invalid tuner number\n"); return -1; } char *ret_error; if (hdhomerun_device_tuner_lockkey_request(hd, &ret_error) <= 0) { fprintf(stderr, "failed to lock tuner\n"); if (ret_error) { fprintf(stderr, "%s\n", ret_error); } return -1; } hdhomerun_device_set_tuner_target(hd, "none"); char *channelmap; if (hdhomerun_device_get_tuner_channelmap(hd, &channelmap) <= 0) { fprintf(stderr, "failed to query channelmap from device\n"); return -1; } const char *channelmap_scan_group = hdhomerun_channelmap_get_channelmap_scan_group(channelmap); if (!channelmap_scan_group) { fprintf(stderr, "unknown channelmap '%s'\n", channelmap); return -1; } if (hdhomerun_device_channelscan_init(hd, channelmap_scan_group) <= 0) { fprintf(stderr, "failed to initialize channel scan\n"); return -1; } FILE *fp = NULL; if (filename) { fp = fopen(filename, "w"); if (!fp) { fprintf(stderr, "unable to create file: %s\n", filename); return -1; } } register_signal_handlers(sigabort_handler, sigabort_handler, siginfo_handler); int ret = 0; while (!sigabort_flag) { struct hdhomerun_channelscan_result_t result; ret = hdhomerun_device_channelscan_advance(hd, &result); if (ret <= 0) { break; } cmd_scan_printf(fp, "SCANNING: %u (%s)\n", (unsigned int)result.frequency, result.channel_str ); ret = hdhomerun_device_channelscan_detect(hd, &result); if (ret < 0) { break; } if (ret == 0) { continue; } cmd_scan_printf(fp, "LOCK: %s (ss=%u snq=%u seq=%u)\n", result.status.lock_str, result.status.signal_strength, result.status.signal_to_noise_quality, result.status.symbol_error_quality ); if (result.transport_stream_id_detected) { cmd_scan_printf(fp, "TSID: 0x%04X\n", result.transport_stream_id); } if (result.original_network_id_detected) { cmd_scan_printf(fp, "ONID: 0x%04X\n", result.original_network_id); } int i; for (i = 0; i < result.program_count; i++) { struct hdhomerun_channelscan_program_t *program = &result.programs[i]; cmd_scan_printf(fp, "PROGRAM %s\n", program->program_str); } } hdhomerun_device_tuner_lockkey_release(hd); if (fp) { fclose(fp); } if (ret < 0) { fprintf(stderr, "communication error sending request to hdhomerun device\n"); } return ret; } static void cmd_save_print_stats(void) { struct hdhomerun_video_stats_t stats; hdhomerun_device_get_video_stats(hd, &stats); fprintf(stderr, "%u packets received, %u overflow errors, %u network errors, %u transport errors, %u sequence errors\n", (unsigned int)stats.packet_count, (unsigned int)stats.overflow_error_count, (unsigned int)stats.network_error_count, (unsigned int)stats.transport_error_count, (unsigned int)stats.sequence_error_count ); } static int cmd_save(const char *tuner_str, const char *filename) { if (hdhomerun_device_set_tuner_from_str(hd, tuner_str) <= 0) { fprintf(stderr, "invalid tuner number\n"); return -1; } FILE *fp; if (strcmp(filename, "null") == 0) { fp = NULL; } else if (strcmp(filename, "-") == 0) { fp = stdout; } else { fp = fopen(filename, "wb"); if (!fp) { fprintf(stderr, "unable to create file %s\n", filename); return -1; } } int ret = hdhomerun_device_stream_start(hd); if (ret <= 0) { fprintf(stderr, "unable to start stream\n"); if (fp && fp != stdout) { fclose(fp); } return ret; } register_signal_handlers(sigabort_handler, sigabort_handler, siginfo_handler); struct hdhomerun_video_stats_t stats_old, stats_cur; hdhomerun_device_get_video_stats(hd, &stats_old); uint64_t next_progress = getcurrenttime() + 1000; while (!sigabort_flag) { uint64_t loop_start_time = getcurrenttime(); if (siginfo_flag) { fprintf(stderr, "\n"); cmd_save_print_stats(); siginfo_flag = false; } size_t actual_size; uint8_t *ptr = hdhomerun_device_stream_recv(hd, VIDEO_DATA_BUFFER_SIZE_1S, &actual_size); if (!ptr) { msleep_approx(64); continue; } if (fp) { if (fwrite(ptr, 1, actual_size, fp) != actual_size) { fprintf(stderr, "error writing output\n"); return -1; } } if (loop_start_time >= next_progress) { next_progress += 1000; if (loop_start_time >= next_progress) { next_progress = loop_start_time + 1000; } /* Windows - indicate activity to suppress auto sleep mode. */ #if defined(_WIN32) SetThreadExecutionState(ES_SYSTEM_REQUIRED); #endif /* Video stats. */ hdhomerun_device_get_video_stats(hd, &stats_cur); if (stats_cur.overflow_error_count > stats_old.overflow_error_count) { fprintf(stderr, "o"); } else if (stats_cur.network_error_count > stats_old.network_error_count) { fprintf(stderr, "n"); } else if (stats_cur.transport_error_count > stats_old.transport_error_count) { fprintf(stderr, "t"); } else if (stats_cur.sequence_error_count > stats_old.sequence_error_count) { fprintf(stderr, "s"); } else { fprintf(stderr, "."); } stats_old = stats_cur; fflush(stderr); } int32_t delay = 64 - (int32_t)(getcurrenttime() - loop_start_time); if (delay <= 0) { continue; } msleep_approx(delay); } if (fp) { fclose(fp); } hdhomerun_device_stream_stop(hd); fprintf(stderr, "\n"); fprintf(stderr, "-- Video statistics --\n"); cmd_save_print_stats(); return 0; } static int cmd_upgrade(const char *filename) { FILE *fp = fopen(filename, "rb"); if (!fp) { fprintf(stderr, "unable to open file %s\n", filename); return -1; } printf("uploading firmware...\n"); if (hdhomerun_device_upgrade(hd, fp) <= 0) { fprintf(stderr, "error sending upgrade file to hdhomerun device\n"); fclose(fp); return -1; } fclose(fp); msleep_minimum(2000); printf("upgrading firmware...\n"); msleep_minimum(8000); printf("rebooting...\n"); int count = 0; char *version_str; while (1) { if (hdhomerun_device_get_version(hd, &version_str, NULL) >= 0) { break; } count++; if (count > 30) { fprintf(stderr, "error finding device after firmware upgrade\n"); return -1; } msleep_minimum(1000); } printf("upgrade complete - now running firmware %s\n", version_str); return 0; } static int cmd_execute(void) { char *ret_value; char *ret_error; if (hdhomerun_device_get_var(hd, "/sys/boot", &ret_value, &ret_error) < 0) { fprintf(stderr, "communication error sending request to hdhomerun device\n"); return -1; } if (ret_error) { printf("%s\n", ret_error); return 0; } char *end = ret_value + strlen(ret_value); char *pos = ret_value; while (1) { if (pos >= end) { break; } char *eol_r = strchr(pos, '\r'); if (!eol_r) { eol_r = end; } char *eol_n = strchr(pos, '\n'); if (!eol_n) { eol_n = end; } char *eol = eol_r; if (eol_n < eol) { eol = eol_n; } char *sep = strchr(pos, ' '); if (!sep || sep > eol) { pos = eol + 1; continue; } *sep = 0; *eol = 0; char *item = pos; char *value = sep + 1; printf("set %s \"%s\"\n", item, value); cmd_set_internal(item, value); pos = eol + 1; } return 1; } static int main_cmd(int argc, char *argv[]) { if (argc < 1) { return help(); } char *cmd = *argv++; argc--; if (contains(cmd, "key")) { if (argc < 2) { return help(); } uint32_t lockkey = (uint32_t)strtoul(argv[0], NULL, 0); hdhomerun_device_tuner_lockkey_use_value(hd, lockkey); cmd = argv[1]; argv+=2; argc-=2; } if (contains(cmd, "get")) { if (argc < 1) { return help(); } return cmd_get(argv[0]); } if (contains(cmd, "set")) { if (argc < 2) { return help(); } return cmd_set(argv[0], argv[1]); } if (contains(cmd, "scan")) { if (argc < 1) { return help(); } if (argc < 2) { return cmd_scan(argv[0], NULL); } else { return cmd_scan(argv[0], argv[1]); } } if (contains(cmd, "save")) { if (argc < 2) { return help(); } return cmd_save(argv[0], argv[1]); } if (contains(cmd, "upgrade")) { if (argc < 1) { return help(); } return cmd_upgrade(argv[0]); } if (contains(cmd, "execute")) { return cmd_execute(); } return help(); } static int main_internal(int argc, char *argv[]) { #if defined(_WIN32) /* Configure console for UTF-8. */ SetConsoleOutputCP(CP_UTF8); /* Initialize network socket support. */ WORD wVersionRequested = MAKEWORD(2, 0); WSADATA wsaData; WSAStartup(wVersionRequested, &wsaData); #endif extract_appname(argv[0]); argv++; argc--; if (argc == 0) { return help(); } char *id_str = *argv++; argc--; if (contains(id_str, "help")) { return help(); } if (contains(id_str, "discover")) { if (argc < 1) { return discover_print(NULL); } else { return discover_print(argv[0]); } } /* Device object. */ hd = hdhomerun_device_create_from_str(id_str, NULL); if (!hd) { fprintf(stderr, "invalid device id: %s\n", id_str); return -1; } /* Device ID check. */ uint32_t device_id_requested = hdhomerun_device_get_device_id_requested(hd); if (!hdhomerun_discover_validate_device_id(device_id_requested)) { fprintf(stderr, "invalid device id: %08X\n", (unsigned int)device_id_requested); } /* Connect to device and check model. */ const char *model = hdhomerun_device_get_model_str(hd); if (!model) { fprintf(stderr, "unable to connect to device\n"); hdhomerun_device_destroy(hd); return -1; } /* Command. */ int ret = main_cmd(argc, argv); /* Cleanup. */ hdhomerun_device_destroy(hd); /* Complete. */ return ret; } int main(int argc, char *argv[]) { int ret = main_internal(argc, argv); if (ret <= 0) { return 1; } return 0; } libhdhomerun/hdhomerun_control.c0000664000175000017500000003070614056010534016421 0ustar buildbuild/* * hdhomerun_control.c * * Copyright © 2006-2016 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" #define HDHOMERUN_CONTROL_CONNECT_TIMEOUT 2500 #define HDHOMERUN_CONTROL_SEND_TIMEOUT 2500 #define HDHOMERUN_CONTROL_RECV_TIMEOUT 2500 #define HDHOMERUN_CONTROL_UPGRADE_TIMEOUT 40000 struct hdhomerun_control_sock_t { uint32_t desired_device_id; uint32_t desired_device_ip; uint32_t actual_device_id; uint32_t actual_device_ip; struct hdhomerun_sock_t *sock; struct hdhomerun_debug_t *dbg; struct hdhomerun_pkt_t tx_pkt; struct hdhomerun_pkt_t rx_pkt; }; static void hdhomerun_control_close_sock(struct hdhomerun_control_sock_t *cs) { if (!cs->sock) { return; } hdhomerun_sock_destroy(cs->sock); cs->sock = NULL; } void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, uint32_t device_id, uint32_t device_ip) { hdhomerun_control_close_sock(cs); cs->desired_device_id = device_id; cs->desired_device_ip = device_ip; cs->actual_device_id = 0; cs->actual_device_ip = 0; } struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip, struct hdhomerun_debug_t *dbg) { struct hdhomerun_control_sock_t *cs = (struct hdhomerun_control_sock_t *)calloc(1, sizeof(struct hdhomerun_control_sock_t)); if (!cs) { hdhomerun_debug_printf(dbg, "hdhomerun_control_create: failed to allocate control object\n"); return NULL; } cs->dbg = dbg; hdhomerun_control_set_device(cs, device_id, device_ip); return cs; } void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs) { hdhomerun_control_close_sock(cs); free(cs); } static bool hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs) { if (cs->sock) { return true; } if ((cs->desired_device_id == 0) && (cs->desired_device_ip == 0)) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: no device specified\n"); return false; } if (hdhomerun_discover_is_ip_multicast(cs->desired_device_ip)) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: cannot use multicast ip address for device operations\n"); return false; } /* Find device. */ struct hdhomerun_discover_device_t result; if (hdhomerun_discover_find_devices_custom_v2(cs->desired_device_ip, HDHOMERUN_DEVICE_TYPE_WILDCARD, cs->desired_device_id, &result, 1) <= 0) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: device not found\n"); return false; } cs->actual_device_ip = result.ip_addr; cs->actual_device_id = result.device_id; /* Create socket. */ cs->sock = hdhomerun_sock_create_tcp(); if (!cs->sock) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to create socket (%d)\n", hdhomerun_sock_getlasterror()); return false; } /* Initiate connection. */ if (!hdhomerun_sock_connect(cs->sock, cs->actual_device_ip, HDHOMERUN_CONTROL_TCP_PORT, HDHOMERUN_CONTROL_CONNECT_TIMEOUT)) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to connect (%d)\n", hdhomerun_sock_getlasterror()); hdhomerun_control_close_sock(cs); return false; } /* Success. */ return true; } uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs) { if (!hdhomerun_control_connect_sock(cs)) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_device_id: connect failed\n"); return 0; } return cs->actual_device_id; } uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs) { if (!hdhomerun_control_connect_sock(cs)) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_device_ip: connect failed\n"); return 0; } return cs->actual_device_ip; } uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs) { return cs->desired_device_id; } uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs) { return cs->desired_device_ip; } uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs) { if (!hdhomerun_control_connect_sock(cs)) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: connect failed\n"); return 0; } uint32_t addr = hdhomerun_sock_getsockname_addr(cs->sock); if (addr == 0) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: getsockname failed (%d)\n", hdhomerun_sock_getlasterror()); return 0; } return addr; } static bool hdhomerun_control_send_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt) { if (!hdhomerun_sock_send(cs->sock, tx_pkt->start, tx_pkt->end - tx_pkt->start, HDHOMERUN_CONTROL_SEND_TIMEOUT)) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_sock: send failed (%d)\n", hdhomerun_sock_getlasterror()); hdhomerun_control_close_sock(cs); return false; } return true; } static bool hdhomerun_control_recv_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *rx_pkt, uint16_t *ptype, uint64_t recv_timeout) { uint64_t stop_time = getcurrenttime() + recv_timeout; hdhomerun_pkt_reset(rx_pkt); while (1) { uint64_t current_time = getcurrenttime(); if (current_time >= stop_time) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: timeout\n"); hdhomerun_control_close_sock(cs); return false; } size_t length = rx_pkt->limit - rx_pkt->end; if (!hdhomerun_sock_recv(cs->sock, rx_pkt->end, &length, stop_time - current_time)) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: recv failed (%d)\n", hdhomerun_sock_getlasterror()); hdhomerun_control_close_sock(cs); return false; } rx_pkt->end += length; int ret = hdhomerun_pkt_open_frame(rx_pkt, ptype); if (ret < 0) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: frame error\n"); hdhomerun_control_close_sock(cs); return false; } if (ret > 0) { return true; } } } static int hdhomerun_control_send_recv_internal(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt, struct hdhomerun_pkt_t *rx_pkt, uint16_t type, uint64_t recv_timeout) { hdhomerun_pkt_seal_frame(tx_pkt, type); int i; for (i = 0; i < 2; i++) { if (!cs->sock) { if (!hdhomerun_control_connect_sock(cs)) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: connect failed\n"); return -1; } } if (!hdhomerun_control_send_sock(cs, tx_pkt)) { continue; } if (!rx_pkt) { return 1; } uint16_t rsp_type; if (!hdhomerun_control_recv_sock(cs, rx_pkt, &rsp_type, recv_timeout)) { continue; } if (rsp_type != type + 1) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: unexpected frame type\n"); hdhomerun_control_close_sock(cs); continue; } return 1; } hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: failed\n"); return -1; } int hdhomerun_control_send_recv(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt, struct hdhomerun_pkt_t *rx_pkt, uint16_t type) { return hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, type, HDHOMERUN_CONTROL_RECV_TIMEOUT); } static int hdhomerun_control_get_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, uint32_t lockkey, char **pvalue, char **perror) { struct hdhomerun_pkt_t *tx_pkt = &cs->tx_pkt; struct hdhomerun_pkt_t *rx_pkt = &cs->rx_pkt; /* Request. */ hdhomerun_pkt_reset(tx_pkt); size_t name_len = strlen(name) + 1; if (tx_pkt->end + 3 + name_len > tx_pkt->limit) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: request too long\n"); return -1; } hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_GETSET_NAME); hdhomerun_pkt_write_var_length(tx_pkt, name_len); hdhomerun_pkt_write_mem(tx_pkt, (const void *)name, name_len); if (value) { size_t value_len = strlen(value) + 1; if (tx_pkt->end + 3 + value_len > tx_pkt->limit) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: request too long\n"); return -1; } hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_GETSET_VALUE); hdhomerun_pkt_write_var_length(tx_pkt, value_len); hdhomerun_pkt_write_mem(tx_pkt, (const void *)value, value_len); } if (lockkey != 0) { if (tx_pkt->end + 6 > tx_pkt->limit) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: request too long\n"); return -1; } hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_GETSET_LOCKKEY); hdhomerun_pkt_write_var_length(tx_pkt, 4); hdhomerun_pkt_write_u32(tx_pkt, lockkey); } /* Send/Recv. */ if (hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, HDHOMERUN_TYPE_GETSET_REQ, HDHOMERUN_CONTROL_RECV_TIMEOUT) < 0) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: send/recv error\n"); return -1; } /* Response. */ while (1) { uint8_t tag; size_t len; uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len); if (!next) { break; } switch (tag) { case HDHOMERUN_TAG_GETSET_VALUE: if (pvalue) { *pvalue = (char *)rx_pkt->pos; rx_pkt->pos[len] = 0; } if (perror) { *perror = NULL; } return 1; case HDHOMERUN_TAG_ERROR_MESSAGE: rx_pkt->pos[len] = 0; hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: %s\n", rx_pkt->pos); if (pvalue) { *pvalue = NULL; } if (perror) { *perror = (char *)rx_pkt->pos; } return 0; default: break; } rx_pkt->pos = next; } hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: missing response tags\n"); return -1; } int hdhomerun_control_get(struct hdhomerun_control_sock_t *cs, const char *name, char **pvalue, char **perror) { return hdhomerun_control_get_set(cs, name, NULL, 0, pvalue, perror); } int hdhomerun_control_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror) { return hdhomerun_control_get_set(cs, name, value, 0, pvalue, perror); } int hdhomerun_control_set_with_lockkey(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, uint32_t lockkey, char **pvalue, char **perror) { return hdhomerun_control_get_set(cs, name, value, lockkey, pvalue, perror); } int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade_file) { struct hdhomerun_pkt_t *tx_pkt = &cs->tx_pkt; struct hdhomerun_pkt_t *rx_pkt = &cs->rx_pkt; bool upload_delay = false; uint32_t sequence = 0; /* Special case detection. */ char *version_str; int ret = hdhomerun_control_get(cs, "/sys/version", &version_str, NULL); if (ret > 0) { upload_delay = strcmp(version_str, "20120704beta1") == 0; } /* Upload. */ while (1) { uint8_t data[1024]; size_t length = fread(data, 1, 1024, upgrade_file); if (length == 0) { break; } hdhomerun_pkt_reset(tx_pkt); hdhomerun_pkt_write_u32(tx_pkt, sequence); hdhomerun_pkt_write_mem(tx_pkt, data, length); if (hdhomerun_control_send_recv_internal(cs, tx_pkt, NULL, HDHOMERUN_TYPE_UPGRADE_REQ, 0) < 0) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: send/recv failed\n"); return -1; } sequence += (uint32_t)length; if (upload_delay) { msleep_approx(25); } } if (sequence == 0) { /* No data in file. Error, but no need to close connection. */ hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: zero length file\n"); return 0; } /* Execute upgrade. */ hdhomerun_pkt_reset(tx_pkt); hdhomerun_pkt_write_u32(tx_pkt, 0xFFFFFFFF); if (hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, HDHOMERUN_TYPE_UPGRADE_REQ, HDHOMERUN_CONTROL_UPGRADE_TIMEOUT) < 0) { hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: send/recv failed\n"); return -1; } /* Check response. */ while (1) { uint8_t tag; size_t len; uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len); if (!next) { break; } switch (tag) { case HDHOMERUN_TAG_ERROR_MESSAGE: rx_pkt->pos[len] = 0; hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: %s\n", (char *)rx_pkt->pos); return 0; default: break; } rx_pkt->pos = next; } return 1; } libhdhomerun/hdhomerun_control.h0000664000175000017500000001142013013646353016424 0ustar buildbuild/* * hdhomerun_control.h * * Copyright © 2006-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif struct hdhomerun_control_sock_t; /* * Create a control socket. * * This function will not attempt to connect to the device. * The connection will be established when first used. * * uint32_t device_id = 32-bit device id of device. Set to HDHOMERUN_DEVICE_ID_WILDCARD to match any device ID. * uint32_t device_ip = IP address of device. Set to 0 to auto-detect. * struct hdhomerun_debug_t *dbg: Pointer to debug logging object. May be NULL. * * Returns a pointer to the newly created control socket. * * When no longer needed, the socket should be destroyed by calling hdhomerun_control_destroy. */ extern LIBHDHOMERUN_API struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip, struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs); /* * Get the actual device id or ip of the device. * * Returns 0 if the device id cannot be determined. */ extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs); extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs); extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs); extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs); extern LIBHDHOMERUN_API void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, uint32_t device_id, uint32_t device_ip); /* * Get the local machine IP address used when communicating with the device. * * This function is useful for determining the IP address to use with set target commands. * * Returns 32-bit IP address with native endianness, or 0 on error. */ extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs); /* * Low-level communication. */ extern LIBHDHOMERUN_API int hdhomerun_control_send_recv(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt, struct hdhomerun_pkt_t *rx_pkt, uint16_t type); /* * Get/set a control variable on the device. * * const char *name: The name of var to get/set (c-string). The supported vars is device/firmware dependant. * const char *value: The value to set (c-string). The format is device/firmware dependant. * char **pvalue: If provided, the caller-supplied char pointer will be populated with a pointer to the value * string returned by the device, or NULL if the device returned an error string. The string will remain * valid until the next call to a control sock function. * char **perror: If provided, the caller-supplied char pointer will be populated with a pointer to the error * string returned by the device, or NULL if the device returned an value string. The string will remain * valid until the next call to a control sock function. * * Returns 1 if the operation was successful (pvalue set, perror NULL). * Returns 0 if the operation was rejected (pvalue NULL, perror set). * Returns -1 if a communication error occurs. */ extern LIBHDHOMERUN_API int hdhomerun_control_get(struct hdhomerun_control_sock_t *cs, const char *name, char **pvalue, char **perror); extern LIBHDHOMERUN_API int hdhomerun_control_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror); extern LIBHDHOMERUN_API int hdhomerun_control_set_with_lockkey(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, uint32_t lockkey, char **pvalue, char **perror); /* * Upload new firmware to the device. * * FILE *upgrade_file: File pointer to read from. The file must have been opened in binary mode for reading. * * Returns 1 if the upload succeeded. * Returns 0 if the upload was rejected. * Returns -1 if an error occurs. */ extern LIBHDHOMERUN_API int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade_file); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_debug.c0000664000175000017500000002334613117566021016035 0ustar buildbuild/* * hdhomerun_debug.c * * Copyright © 2007-2016 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * The debug logging includes optional support for connecting to the * Silicondust support server. This option should not be used without * being explicitly enabled by the user. Debug information should be * limited to information useful to diagnosing a problem. * - Silicondust. */ #include "hdhomerun.h" #if !defined(HDHOMERUN_DEBUG_HOST) #define HDHOMERUN_DEBUG_HOST "debug.silicondust.com" #endif #if !defined(HDHOMERUN_DEBUG_PORT) #define HDHOMERUN_DEBUG_PORT 8002 #endif #define HDHOMERUN_DEBUG_CONNECT_RETRY_TIME 30000 #define HDHOMERUN_DEBUG_CONNECT_TIMEOUT 10000 #define HDHOMERUN_DEBUG_SEND_TIMEOUT 10000 struct hdhomerun_debug_message_t { struct hdhomerun_debug_message_t *next; char buffer[2048]; }; struct hdhomerun_debug_t { thread_task_t thread; volatile bool enabled; volatile bool terminate; char *prefix; thread_mutex_t print_lock; thread_mutex_t queue_lock; thread_mutex_t send_lock; thread_cond_t queue_cond; struct hdhomerun_debug_message_t *queue_head; struct hdhomerun_debug_message_t *queue_tail; uint32_t queue_depth; uint64_t connect_delay; char *file_name; FILE *file_fp; struct hdhomerun_sock_t *sock; }; static void hdhomerun_debug_thread_execute(void *arg); struct hdhomerun_debug_t *hdhomerun_debug_create(void) { struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)calloc(1, sizeof(struct hdhomerun_debug_t)); if (!dbg) { return NULL; } thread_mutex_init(&dbg->print_lock); thread_mutex_init(&dbg->queue_lock); thread_mutex_init(&dbg->send_lock); thread_cond_init(&dbg->queue_cond); if (!thread_task_create(&dbg->thread, &hdhomerun_debug_thread_execute, dbg)) { free(dbg); return NULL; } return dbg; } void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg) { if (!dbg) { return; } dbg->terminate = true; thread_cond_signal(&dbg->queue_cond); thread_task_join(dbg->thread); if (dbg->prefix) { free(dbg->prefix); } if (dbg->file_name) { free(dbg->file_name); } if (dbg->file_fp) { fclose(dbg->file_fp); } if (dbg->sock) { hdhomerun_sock_destroy(dbg->sock); } thread_cond_dispose(&dbg->queue_cond); thread_mutex_dispose(&dbg->print_lock); thread_mutex_dispose(&dbg->queue_lock); thread_mutex_dispose(&dbg->send_lock); free(dbg); } /* Send lock held by caller */ static void hdhomerun_debug_close_internal(struct hdhomerun_debug_t *dbg) { if (dbg->file_fp) { fclose(dbg->file_fp); dbg->file_fp = NULL; } if (dbg->sock) { hdhomerun_sock_destroy(dbg->sock); dbg->sock = NULL; } } void hdhomerun_debug_close(struct hdhomerun_debug_t *dbg, uint64_t timeout) { if (!dbg) { return; } if (timeout > 0) { hdhomerun_debug_flush(dbg, timeout); } thread_mutex_lock(&dbg->send_lock); hdhomerun_debug_close_internal(dbg); dbg->connect_delay = 0; thread_mutex_unlock(&dbg->send_lock); } void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename) { if (!dbg) { return; } thread_mutex_lock(&dbg->send_lock); if (!filename && !dbg->file_name) { thread_mutex_unlock(&dbg->send_lock); return; } if (filename && dbg->file_name) { if (strcmp(filename, dbg->file_name) == 0) { thread_mutex_unlock(&dbg->send_lock); return; } } hdhomerun_debug_close_internal(dbg); dbg->connect_delay = 0; if (dbg->file_name) { free(dbg->file_name); dbg->file_name = NULL; } if (filename) { dbg->file_name = strdup(filename); } thread_mutex_unlock(&dbg->send_lock); } void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix) { if (!dbg) { return; } thread_mutex_lock(&dbg->print_lock); if (dbg->prefix) { free(dbg->prefix); dbg->prefix = NULL; } if (prefix) { dbg->prefix = strdup(prefix); } thread_mutex_unlock(&dbg->print_lock); } void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg) { if (!dbg) { return; } if (dbg->enabled) { return; } dbg->enabled = true; thread_cond_signal(&dbg->queue_cond); } void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg) { if (!dbg) { return; } dbg->enabled = false; } bool hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg) { if (!dbg) { return false; } return dbg->enabled; } void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout) { if (!dbg) { return; } timeout = getcurrenttime() + timeout; while (getcurrenttime() < timeout) { thread_mutex_lock(&dbg->queue_lock); struct hdhomerun_debug_message_t *message = dbg->queue_head; thread_mutex_unlock(&dbg->queue_lock); if (!message) { return; } msleep_approx(16); } } void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...) { va_list args; va_start(args, fmt); hdhomerun_debug_vprintf(dbg, fmt, args); va_end(args); } void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_list args) { if (!dbg) { return; } struct hdhomerun_debug_message_t *message = (struct hdhomerun_debug_message_t *)malloc(sizeof(struct hdhomerun_debug_message_t)); if (!message) { return; } message->next = NULL; char *ptr = message->buffer; char *end = message->buffer + sizeof(message->buffer) - 2; *end = 0; /* * Timestamp. */ time_t current_time = time(NULL); ptr += strftime(ptr, end - ptr, "%Y%m%d-%H:%M:%S ", localtime(¤t_time)); if (ptr > end) { ptr = end; } /* * Debug prefix. */ thread_mutex_lock(&dbg->print_lock); if (dbg->prefix) { hdhomerun_sprintf(ptr, end, "%s ", dbg->prefix); ptr = strchr(ptr, 0); } thread_mutex_unlock(&dbg->print_lock); /* * Message text. */ hdhomerun_vsprintf(ptr, end, fmt, args); ptr = strchr(ptr, 0); /* * Force newline. */ if (ptr[-1] != '\n') { hdhomerun_sprintf(ptr, end, "\n"); } /* * Enqueue. */ thread_mutex_lock(&dbg->queue_lock); if (dbg->queue_tail) { dbg->queue_tail->next = message; } else { dbg->queue_head = message; } dbg->queue_tail = message; dbg->queue_depth++; bool signal_thread = dbg->enabled || (dbg->queue_depth > 1024 + 100); thread_mutex_unlock(&dbg->queue_lock); if (signal_thread) { thread_cond_signal(&dbg->queue_cond); } } /* Send lock held by caller */ static bool hdhomerun_debug_output_message_file(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message) { if (!dbg->file_fp) { uint64_t current_time = getcurrenttime(); if (current_time < dbg->connect_delay) { return false; } dbg->connect_delay = current_time + 30*1000; dbg->file_fp = fopen(dbg->file_name, "a"); if (!dbg->file_fp) { return false; } } fprintf(dbg->file_fp, "%s", message->buffer); fflush(dbg->file_fp); return true; } /* Send lock held by caller */ static bool hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message) { if (!dbg->sock) { uint64_t current_time = getcurrenttime(); if (current_time < dbg->connect_delay) { return false; } dbg->connect_delay = current_time + HDHOMERUN_DEBUG_CONNECT_RETRY_TIME; dbg->sock = hdhomerun_sock_create_tcp(); if (!dbg->sock) { return false; } uint32_t remote_addr = hdhomerun_sock_getaddrinfo_addr(dbg->sock, HDHOMERUN_DEBUG_HOST); if (remote_addr == 0) { hdhomerun_debug_close_internal(dbg); return false; } if (!hdhomerun_sock_connect(dbg->sock, remote_addr, HDHOMERUN_DEBUG_PORT, HDHOMERUN_DEBUG_CONNECT_TIMEOUT)) { hdhomerun_debug_close_internal(dbg); return false; } } size_t length = strlen(message->buffer); if (!hdhomerun_sock_send(dbg->sock, message->buffer, length, HDHOMERUN_DEBUG_SEND_TIMEOUT)) { hdhomerun_debug_close_internal(dbg); return false; } return true; } static bool hdhomerun_debug_output_message(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message) { thread_mutex_lock(&dbg->send_lock); bool ret; if (dbg->file_name) { ret = hdhomerun_debug_output_message_file(dbg, message); } else { ret = hdhomerun_debug_output_message_sock(dbg, message); } thread_mutex_unlock(&dbg->send_lock); return ret; } static void hdhomerun_debug_pop_and_free_message(struct hdhomerun_debug_t *dbg) { thread_mutex_lock(&dbg->queue_lock); struct hdhomerun_debug_message_t *message = dbg->queue_head; dbg->queue_head = message->next; if (!dbg->queue_head) { dbg->queue_tail = NULL; } dbg->queue_depth--; thread_mutex_unlock(&dbg->queue_lock); free(message); } static void hdhomerun_debug_thread_execute(void *arg) { struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)arg; while (!dbg->terminate) { thread_mutex_lock(&dbg->queue_lock); struct hdhomerun_debug_message_t *message = dbg->queue_head; uint32_t queue_depth = dbg->queue_depth; thread_mutex_unlock(&dbg->queue_lock); if (!message) { thread_cond_wait(&dbg->queue_cond); continue; } if (queue_depth > 1024) { hdhomerun_debug_pop_and_free_message(dbg); continue; } if (!dbg->enabled) { thread_cond_wait(&dbg->queue_cond); continue; } if (!hdhomerun_debug_output_message(dbg, message)) { msleep_approx(1000); continue; } hdhomerun_debug_pop_and_free_message(dbg); } } libhdhomerun/hdhomerun_debug.h0000664000175000017500000000437513063620312016035 0ustar buildbuild/* * hdhomerun_debug.h * * Copyright © 2007-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * The debug logging includes optional support for connecting to the * Silicondust support server. This option should not be used without * being explicitly enabled by the user. Debug information should be * limited to information useful to diagnosing a problem. * - Silicondust. */ #ifdef __cplusplus extern "C" { #endif struct hdhomerun_debug_t; extern LIBHDHOMERUN_API struct hdhomerun_debug_t *hdhomerun_debug_create(void); extern LIBHDHOMERUN_API void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix); extern LIBHDHOMERUN_API void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename); extern LIBHDHOMERUN_API void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API bool hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout); extern LIBHDHOMERUN_API void hdhomerun_debug_close(struct hdhomerun_debug_t *dbg, uint64_t timeout); extern LIBHDHOMERUN_API void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...); extern LIBHDHOMERUN_API void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_list args); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_device.c0000664000175000017500000010675613737525222016223 0ustar buildbuild/* * hdhomerun_device.c * * Copyright © 2006-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" struct hdhomerun_device_t { struct hdhomerun_control_sock_t *cs; struct hdhomerun_video_sock_t *vs; struct hdhomerun_debug_t *dbg; struct hdhomerun_channelscan_t *scan; uint32_t multicast_ip; uint16_t multicast_port; uint32_t device_id; unsigned int tuner; uint32_t lockkey; char name[32]; char model[32]; }; int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip) { if ((device_id == 0) && (device_ip == 0)) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device: device not specified\n"); return -1; } if (hdhomerun_discover_is_ip_multicast(device_ip)) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device: invalid address %08X\n", (unsigned int)device_ip); return -1; } if (!hd->cs) { hd->cs = hdhomerun_control_create(0, 0, hd->dbg); if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device: failed to create control object\n"); return -1; } } hdhomerun_control_set_device(hd->cs, device_id, device_ip); if ((device_id == 0) || (device_id == HDHOMERUN_DEVICE_ID_WILDCARD)) { device_id = hdhomerun_control_get_device_id(hd->cs); } hd->multicast_ip = 0; hd->multicast_port = 0; hd->device_id = device_id; hd->tuner = 0; hd->lockkey = 0; hdhomerun_sprintf(hd->name, hd->name + sizeof(hd->name), "%08X-%u", (unsigned int)hd->device_id, hd->tuner); hdhomerun_sprintf(hd->model, hd->model + sizeof(hd->model), ""); /* clear cached model string */ return 1; } int hdhomerun_device_set_multicast(struct hdhomerun_device_t *hd, uint32_t multicast_ip, uint16_t multicast_port) { if (!hdhomerun_discover_is_ip_multicast(multicast_ip)) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device_multicast: invalid address %08X\n", (unsigned int)multicast_ip); return -1; } if (multicast_port == 0) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device_multicast: invalid port %u\n", (unsigned int)multicast_port); return -1; } if (hd->cs) { hdhomerun_control_destroy(hd->cs); hd->cs = NULL; } hd->multicast_ip = multicast_ip; hd->multicast_port = multicast_port; hd->device_id = 0; hd->tuner = 0; hd->lockkey = 0; unsigned int ip = multicast_ip; hdhomerun_sprintf(hd->name, hd->name + sizeof(hd->name), "%u.%u.%u.%u:%u", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, (ip >> 0) & 0xFF, (unsigned int)multicast_port); hdhomerun_sprintf(hd->model, hd->model + sizeof(hd->model), "multicast"); return 1; } int hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner) { if (hd->multicast_ip != 0) { if (tuner != 0) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner: tuner cannot be specified in multicast mode\n"); return -1; } return 1; } hd->tuner = tuner; hdhomerun_sprintf(hd->name, hd->name + sizeof(hd->name), "%08X-%u", (unsigned int)hd->device_id, hd->tuner); return 1; } int hdhomerun_device_set_tuner_from_str(struct hdhomerun_device_t *hd, const char *tuner_str) { unsigned int tuner; if (sscanf(tuner_str, "%u", &tuner) == 1) { hdhomerun_device_set_tuner(hd, tuner); return 1; } if (sscanf(tuner_str, "/tuner%u", &tuner) == 1) { hdhomerun_device_set_tuner(hd, tuner); return 1; } return -1; } static struct hdhomerun_device_t *hdhomerun_device_create_internal(struct hdhomerun_debug_t *dbg) { struct hdhomerun_device_t *hd = (struct hdhomerun_device_t *)calloc(1, sizeof(struct hdhomerun_device_t)); if (!hd) { hdhomerun_debug_printf(dbg, "hdhomerun_device_create: failed to allocate device object\n"); return NULL; } hd->dbg = dbg; return hd; } struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t device_ip, unsigned int tuner, struct hdhomerun_debug_t *dbg) { struct hdhomerun_device_t *hd = hdhomerun_device_create_internal(dbg); if (!hd) { return NULL; } if ((device_id == 0) && (device_ip == 0) && (tuner == 0)) { return hd; } if (hdhomerun_device_set_device(hd, device_id, device_ip) <= 0) { free(hd); return NULL; } if (hdhomerun_device_set_tuner(hd, tuner) <= 0) { free(hd); return NULL; } return hd; } struct hdhomerun_device_t *hdhomerun_device_create_multicast(uint32_t multicast_ip, uint16_t multicast_port, struct hdhomerun_debug_t *dbg) { struct hdhomerun_device_t *hd = hdhomerun_device_create_internal(dbg); if (!hd) { return NULL; } if (hdhomerun_device_set_multicast(hd, multicast_ip, multicast_port) <= 0) { free(hd); return NULL; } return hd; } void hdhomerun_device_destroy(struct hdhomerun_device_t *hd) { if (hd->scan) { channelscan_destroy(hd->scan); } if (hd->vs) { hdhomerun_video_destroy(hd->vs); } if (hd->cs) { hdhomerun_control_destroy(hd->cs); } free(hd); } struct hdhomerun_device_t *hdhomerun_device_create_from_str(const char *device_str, struct hdhomerun_debug_t *dbg) { /* * IP address based device_str. */ unsigned int a[4]; if (sscanf(device_str, "%u.%u.%u.%u", &a[0], &a[1], &a[2], &a[3]) == 4) { uint32_t ip_addr = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0)); /* * Multicast IP address. */ unsigned int port; if (sscanf(device_str, "%u.%u.%u.%u:%u", &a[0], &a[1], &a[2], &a[3], &port) == 5) { return hdhomerun_device_create_multicast(ip_addr, (uint16_t)port, dbg); } /* * IP address + tuner number. */ unsigned int tuner; if (sscanf(device_str, "%u.%u.%u.%u-%u", &a[0], &a[1], &a[2], &a[3], &tuner) == 5) { return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, ip_addr, tuner, dbg); } /* * IP address only. */ return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, ip_addr, 0, dbg); } /* * Device ID based device_str. */ char *end; uint32_t device_id = (uint32_t)strtoul(device_str, &end, 16); if ((end == device_str + 8) && hdhomerun_discover_validate_device_id(device_id)) { /* * IP address + tuner number. */ if (*end == '-') { unsigned int tuner = (unsigned int)strtoul(end + 1, NULL, 10); return hdhomerun_device_create(device_id, 0, tuner, dbg); } /* * Device ID only. */ return hdhomerun_device_create(device_id, 0, 0, dbg); } /* * DNS based device_str. */ struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; struct addrinfo *sock_info; if (getaddrinfo(device_str, "65001", &hints, &sock_info) != 0) { return NULL; } struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr; uint32_t ip_addr = (uint32_t)ntohl(sock_addr->sin_addr.s_addr); freeaddrinfo(sock_info); if (ip_addr == 0) { return NULL; } return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, ip_addr, 0, dbg); } const char *hdhomerun_device_get_name(struct hdhomerun_device_t *hd) { return hd->name; } uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd) { return hd->device_id; } uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd) { if (hd->multicast_ip != 0) { return hd->multicast_ip; } if (hd->cs) { return hdhomerun_control_get_device_ip(hd->cs); } return 0; } uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd) { if (hd->multicast_ip != 0) { return 0; } if (hd->cs) { return hdhomerun_control_get_device_id_requested(hd->cs); } return 0; } uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd) { if (hd->multicast_ip != 0) { return hd->multicast_ip; } if (hd->cs) { return hdhomerun_control_get_device_ip_requested(hd->cs); } return 0; } unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd) { return hd->tuner; } struct hdhomerun_control_sock_t *hdhomerun_device_get_control_sock(struct hdhomerun_device_t *hd) { return hd->cs; } struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdhomerun_device_t *hd) { if (hd->vs) { return hd->vs; } bool allow_port_reuse = (hd->multicast_port != 0); hd->vs = hdhomerun_video_create(hd->multicast_port, allow_port_reuse, VIDEO_DATA_BUFFER_SIZE_1S * 2, hd->dbg); if (!hd->vs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_video_sock: failed to create video object\n"); return NULL; } return hd->vs; } uint32_t hdhomerun_device_get_local_machine_addr(struct hdhomerun_device_t *hd) { if (hd->cs) { return hdhomerun_control_get_local_addr(hd->cs); } return 0; } static uint32_t hdhomerun_device_get_status_parse(const char *status_str, const char *tag) { const char *ptr = strstr(status_str, tag); if (!ptr) { return 0; } unsigned int value = 0; sscanf(ptr + strlen(tag), "%u", &value); return (uint32_t)value; } static bool hdhomerun_device_get_tuner_status_lock_is_bcast(struct hdhomerun_tuner_status_t *status) { if (strcmp(status->lock_str, "8vsb") == 0) { return true; } if (strcmp(status->lock_str, "atsc3") == 0) { return true; } if (strncmp(status->lock_str, "t8", 2) == 0) { return true; } if (strncmp(status->lock_str, "t7", 2) == 0) { return true; } if (strncmp(status->lock_str, "t6", 2) == 0) { return true; } return false; } uint32_t hdhomerun_device_get_tuner_status_ss_color(struct hdhomerun_tuner_status_t *status) { unsigned int ss_yellow_min; unsigned int ss_green_min; if (!status->lock_supported) { return HDHOMERUN_STATUS_COLOR_NEUTRAL; } if (hdhomerun_device_get_tuner_status_lock_is_bcast(status)) { ss_yellow_min = 50; /* -30dBmV */ ss_green_min = 75; /* -15dBmV */ } else { ss_yellow_min = 80; /* -12dBmV */ ss_green_min = 90; /* -6dBmV */ } if (status->signal_strength >= ss_green_min) { return HDHOMERUN_STATUS_COLOR_GREEN; } if (status->signal_strength >= ss_yellow_min) { return HDHOMERUN_STATUS_COLOR_YELLOW; } return HDHOMERUN_STATUS_COLOR_RED; } uint32_t hdhomerun_device_get_tuner_status_snq_color(struct hdhomerun_tuner_status_t *status) { if (status->signal_to_noise_quality >= 70) { return HDHOMERUN_STATUS_COLOR_GREEN; } if (status->signal_to_noise_quality >= 50) { return HDHOMERUN_STATUS_COLOR_YELLOW; } return HDHOMERUN_STATUS_COLOR_RED; } uint32_t hdhomerun_device_get_tuner_status_seq_color(struct hdhomerun_tuner_status_t *status) { if (status->symbol_error_quality >= 100) { return HDHOMERUN_STATUS_COLOR_GREEN; } return HDHOMERUN_STATUS_COLOR_RED; } int hdhomerun_device_get_tuner_status(struct hdhomerun_device_t *hd, char **pstatus_str, struct hdhomerun_tuner_status_t *status) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_status: device not set\n"); return -1; } memset(status, 0, sizeof(struct hdhomerun_tuner_status_t)); char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/status", hd->tuner); char *status_str; int ret = hdhomerun_control_get(hd->cs, name, &status_str, NULL); if (ret <= 0) { return ret; } if (pstatus_str) { *pstatus_str = status_str; } if (status) { char *channel = strstr(status_str, "ch="); if (channel) { sscanf(channel + 3, "%31s", status->channel); } char *lock = strstr(status_str, "lock="); if (lock) { sscanf(lock + 5, "%31s", status->lock_str); } status->signal_strength = (unsigned int)hdhomerun_device_get_status_parse(status_str, "ss="); status->signal_to_noise_quality = (unsigned int)hdhomerun_device_get_status_parse(status_str, "snq="); status->symbol_error_quality = (unsigned int)hdhomerun_device_get_status_parse(status_str, "seq="); status->raw_bits_per_second = hdhomerun_device_get_status_parse(status_str, "bps="); status->packets_per_second = hdhomerun_device_get_status_parse(status_str, "pps="); status->signal_present = status->signal_strength >= 35; if (strcmp(status->lock_str, "none") != 0) { if (status->lock_str[0] == '(') { status->lock_unsupported = true; } else { status->lock_supported = true; } } } return 1; } int hdhomerun_device_get_oob_status(struct hdhomerun_device_t *hd, char **pstatus_str, struct hdhomerun_tuner_status_t *status) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_oob_status: device not set\n"); return -1; } memset(status, 0, sizeof(struct hdhomerun_tuner_status_t)); char *status_str; int ret = hdhomerun_control_get(hd->cs, "/oob/status", &status_str, NULL); if (ret <= 0) { return ret; } if (pstatus_str) { *pstatus_str = status_str; } if (status) { char *channel = strstr(status_str, "ch="); if (channel) { sscanf(channel + 3, "%31s", status->channel); } char *lock = strstr(status_str, "lock="); if (lock) { sscanf(lock + 5, "%31s", status->lock_str); } status->signal_strength = (unsigned int)hdhomerun_device_get_status_parse(status_str, "ss="); status->signal_to_noise_quality = (unsigned int)hdhomerun_device_get_status_parse(status_str, "snq="); status->signal_present = status->signal_strength >= 35; status->lock_supported = (strcmp(status->lock_str, "none") != 0); } return 1; } int hdhomerun_device_get_tuner_vstatus(struct hdhomerun_device_t *hd, char **pvstatus_str, struct hdhomerun_tuner_vstatus_t *vstatus) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_vstatus: device not set\n"); return -1; } memset(vstatus, 0, sizeof(struct hdhomerun_tuner_vstatus_t)); char var_name[32]; hdhomerun_sprintf(var_name, var_name + sizeof(var_name), "/tuner%u/vstatus", hd->tuner); char *vstatus_str; int ret = hdhomerun_control_get(hd->cs, var_name, &vstatus_str, NULL); if (ret <= 0) { return ret; } if (pvstatus_str) { *pvstatus_str = vstatus_str; } if (vstatus) { char *vch = strstr(vstatus_str, "vch="); if (vch) { sscanf(vch + 4, "%31s", vstatus->vchannel); } char *name = strstr(vstatus_str, "name="); if (name) { sscanf(name + 5, "%31s", vstatus->name); } char *auth = strstr(vstatus_str, "auth="); if (auth) { sscanf(auth + 5, "%31s", vstatus->auth); } char *cci = strstr(vstatus_str, "cci="); if (cci) { sscanf(cci + 4, "%31s", vstatus->cci); } char *cgms = strstr(vstatus_str, "cgms="); if (cgms) { sscanf(cgms + 5, "%31s", vstatus->cgms); } if (strncmp(vstatus->auth, "not-subscribed", 14) == 0) { vstatus->not_subscribed = true; } if (strncmp(vstatus->auth, "error", 5) == 0) { vstatus->not_available = true; } if (strncmp(vstatus->auth, "dialog", 6) == 0) { vstatus->not_available = true; } if (strncmp(vstatus->cci, "protected", 9) == 0) { vstatus->copy_protected = true; } if (strncmp(vstatus->cgms, "protected", 9) == 0) { vstatus->copy_protected = true; } } return 1; } int hdhomerun_device_get_tuner_plpinfo(struct hdhomerun_device_t *hd, char **pplpinfo) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_plpinfo: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/plpinfo", hd->tuner); return hdhomerun_control_get(hd->cs, name, pplpinfo, NULL); } int hdhomerun_device_get_tuner_streaminfo(struct hdhomerun_device_t *hd, char **pstreaminfo) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_streaminfo: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/streaminfo", hd->tuner); return hdhomerun_control_get(hd->cs, name, pstreaminfo, NULL); } int hdhomerun_device_get_tuner_channel(struct hdhomerun_device_t *hd, char **pchannel) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_channel: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/channel", hd->tuner); return hdhomerun_control_get(hd->cs, name, pchannel, NULL); } int hdhomerun_device_get_tuner_vchannel(struct hdhomerun_device_t *hd, char **pvchannel) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_vchannel: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/vchannel", hd->tuner); return hdhomerun_control_get(hd->cs, name, pvchannel, NULL); } int hdhomerun_device_get_tuner_channelmap(struct hdhomerun_device_t *hd, char **pchannelmap) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_channelmap: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/channelmap", hd->tuner); return hdhomerun_control_get(hd->cs, name, pchannelmap, NULL); } int hdhomerun_device_get_tuner_filter(struct hdhomerun_device_t *hd, char **pfilter) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_filter: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/filter", hd->tuner); return hdhomerun_control_get(hd->cs, name, pfilter, NULL); } int hdhomerun_device_get_tuner_program(struct hdhomerun_device_t *hd, char **pprogram) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_program: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/program", hd->tuner); return hdhomerun_control_get(hd->cs, name, pprogram, NULL); } int hdhomerun_device_get_tuner_target(struct hdhomerun_device_t *hd, char **ptarget) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_target: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/target", hd->tuner); return hdhomerun_control_get(hd->cs, name, ptarget, NULL); } static int hdhomerun_device_get_tuner_plotsample_internal(struct hdhomerun_device_t *hd, const char *name, struct hdhomerun_plotsample_t **psamples, size_t *pcount) { char *result; int ret = hdhomerun_control_get(hd->cs, name, &result, NULL); if (ret <= 0) { return ret; } struct hdhomerun_plotsample_t *samples = (struct hdhomerun_plotsample_t *)result; *psamples = samples; size_t count = 0; while (1) { char *ptr = strchr(result, ' '); if (!ptr) { break; } *ptr++ = 0; unsigned int raw; if (sscanf(result, "%x", &raw) != 1) { break; } uint16_t real = (raw >> 12) & 0x0FFF; if (real & 0x0800) { real |= 0xF000; } uint16_t imag = (raw >> 0) & 0x0FFF; if (imag & 0x0800) { imag |= 0xF000; } samples->real = (int16_t)real; samples->imag = (int16_t)imag; samples++; count++; result = ptr; } *pcount = count; return 1; } int hdhomerun_device_get_tuner_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_plotsample: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/plotsample", hd->tuner); return hdhomerun_device_get_tuner_plotsample_internal(hd, name, psamples, pcount); } int hdhomerun_device_get_oob_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_oob_plotsample: device not set\n"); return -1; } return hdhomerun_device_get_tuner_plotsample_internal(hd, "/oob/plotsample", psamples, pcount); } int hdhomerun_device_get_tuner_lockkey_owner(struct hdhomerun_device_t *hd, char **powner) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_lockkey_owner: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/lockkey", hd->tuner); return hdhomerun_control_get(hd->cs, name, powner, NULL); } int hdhomerun_device_get_ir_target(struct hdhomerun_device_t *hd, char **ptarget) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_ir_target: device not set\n"); return -1; } return hdhomerun_control_get(hd->cs, "/ir/target", ptarget, NULL); } int hdhomerun_device_get_version(struct hdhomerun_device_t *hd, char **pversion_str, uint32_t *pversion_num) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_version: device not set\n"); return -1; } char *version_str; int ret = hdhomerun_control_get(hd->cs, "/sys/version", &version_str, NULL); if (ret <= 0) { return ret; } if (pversion_str) { *pversion_str = version_str; } if (pversion_num) { unsigned int version_num; if (sscanf(version_str, "%u", &version_num) != 1) { *pversion_num = 0; } else { *pversion_num = (uint32_t)version_num; } } return 1; } int hdhomerun_device_get_supported(struct hdhomerun_device_t *hd, char *prefix, char **pstr) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_channel: device not set\n"); return -1; } char *features; int ret = hdhomerun_control_get(hd->cs, "/sys/features", &features, NULL); if (ret <= 0) { return ret; } if (!prefix) { *pstr = features; return 1; } char *ptr = strstr(features, prefix); if (!ptr) { return 0; } ptr += strlen(prefix); *pstr = ptr; ptr = strchr(ptr, '\n'); if (ptr) { *ptr = 0; } return 1; } int hdhomerun_device_set_tuner_channel(struct hdhomerun_device_t *hd, const char *channel) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_channel: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/channel", hd->tuner); return hdhomerun_control_set_with_lockkey(hd->cs, name, channel, hd->lockkey, NULL, NULL); } int hdhomerun_device_set_tuner_vchannel(struct hdhomerun_device_t *hd, const char *vchannel) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_vchannel: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/vchannel", hd->tuner); return hdhomerun_control_set_with_lockkey(hd->cs, name, vchannel, hd->lockkey, NULL, NULL); } int hdhomerun_device_set_tuner_channelmap(struct hdhomerun_device_t *hd, const char *channelmap) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_channelmap: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/channelmap", hd->tuner); return hdhomerun_control_set_with_lockkey(hd->cs, name, channelmap, hd->lockkey, NULL, NULL); } int hdhomerun_device_set_tuner_filter(struct hdhomerun_device_t *hd, const char *filter) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_filter: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/filter", hd->tuner); return hdhomerun_control_set_with_lockkey(hd->cs, name, filter, hd->lockkey, NULL, NULL); } static bool hdhomerun_device_set_tuner_filter_by_array_append(char *ptr, char *end, uint16_t range_begin, uint16_t range_end) { if (range_begin == range_end) { return hdhomerun_sprintf(ptr, end, "0x%04x ", (unsigned int)range_begin); } else { return hdhomerun_sprintf(ptr, end, "0x%04x-0x%04x ", (unsigned int)range_begin, (unsigned int)range_end); } } int hdhomerun_device_set_tuner_filter_by_array(struct hdhomerun_device_t *hd, unsigned char filter_array[0x2000]) { char filter[1024]; char *ptr = filter; char *end = filter + sizeof(filter); uint16_t range_begin = 0xFFFF; uint16_t range_end = 0xFFFF; uint16_t i; for (i = 0; i <= 0x1FFF; i++) { if (!filter_array[i]) { if (range_begin == 0xFFFF) { continue; } if (!hdhomerun_device_set_tuner_filter_by_array_append(ptr, end, range_begin, range_end)) { return 0; } ptr = strchr(ptr, 0); range_begin = 0xFFFF; range_end = 0xFFFF; continue; } if (range_begin == 0xFFFF) { range_begin = i; range_end = i; continue; } range_end = i; } if (range_begin != 0xFFFF) { if (!hdhomerun_device_set_tuner_filter_by_array_append(ptr, end, range_begin, range_end)) { return 0; } ptr = strchr(ptr, 0); } /* Remove trailing space. */ if (ptr > filter) { ptr--; *ptr = 0; } return hdhomerun_device_set_tuner_filter(hd, filter); } int hdhomerun_device_set_tuner_program(struct hdhomerun_device_t *hd, const char *program) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_program: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/program", hd->tuner); return hdhomerun_control_set_with_lockkey(hd->cs, name, program, hd->lockkey, NULL, NULL); } int hdhomerun_device_set_tuner_target(struct hdhomerun_device_t *hd, const char *target) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_target: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/target", hd->tuner); return hdhomerun_control_set_with_lockkey(hd->cs, name, target, hd->lockkey, NULL, NULL); } static int hdhomerun_device_set_tuner_target_to_local(struct hdhomerun_device_t *hd, const char *protocol) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_target_to_local: device not set\n"); return -1; } if (!hd->vs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_target_to_local: video not initialized\n"); return -1; } /* Set target. */ char target[64]; uint32_t local_ip = hdhomerun_control_get_local_addr(hd->cs); uint16_t local_port = hdhomerun_video_get_local_port(hd->vs); hdhomerun_sprintf(target, target + sizeof(target), "%s://%u.%u.%u.%u:%u", protocol, (unsigned int)(local_ip >> 24) & 0xFF, (unsigned int)(local_ip >> 16) & 0xFF, (unsigned int)(local_ip >> 8) & 0xFF, (unsigned int)(local_ip >> 0) & 0xFF, (unsigned int)local_port ); return hdhomerun_device_set_tuner_target(hd, target); } int hdhomerun_device_set_ir_target(struct hdhomerun_device_t *hd, const char *target) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_ir_target: device not set\n"); return -1; } return hdhomerun_control_set(hd->cs, "/ir/target", target, NULL, NULL); } int hdhomerun_device_set_sys_dvbc_modulation(struct hdhomerun_device_t *hd, const char *modulation_list) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_sys_dvbc_modulation: device not set\n"); return -1; } return hdhomerun_control_set(hd->cs, "/sys/dvbc_modulation", modulation_list, NULL, NULL); } int hdhomerun_device_get_var(struct hdhomerun_device_t *hd, const char *name, char **pvalue, char **perror) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_var: device not set\n"); return -1; } return hdhomerun_control_get(hd->cs, name, pvalue, perror); } int hdhomerun_device_set_var(struct hdhomerun_device_t *hd, const char *name, const char *value, char **pvalue, char **perror) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_var: device not set\n"); return -1; } return hdhomerun_control_set_with_lockkey(hd->cs, name, value, hd->lockkey, pvalue, perror); } int hdhomerun_device_tuner_lockkey_request(struct hdhomerun_device_t *hd, char **perror) { if (hd->multicast_ip != 0) { return 1; } if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_tuner_lockkey_request: device not set\n"); return -1; } uint32_t new_lockkey = random_get32(); char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/lockkey", hd->tuner); char new_lockkey_str[64]; hdhomerun_sprintf(new_lockkey_str, new_lockkey_str + sizeof(new_lockkey_str), "%u", (unsigned int)new_lockkey); int ret = hdhomerun_control_set_with_lockkey(hd->cs, name, new_lockkey_str, hd->lockkey, NULL, perror); if (ret <= 0) { hd->lockkey = 0; return ret; } hd->lockkey = new_lockkey; return ret; } int hdhomerun_device_tuner_lockkey_release(struct hdhomerun_device_t *hd) { if (hd->multicast_ip != 0) { return 1; } if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_tuner_lockkey_release: device not set\n"); return -1; } if (hd->lockkey == 0) { return 1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/lockkey", hd->tuner); int ret = hdhomerun_control_set_with_lockkey(hd->cs, name, "none", hd->lockkey, NULL, NULL); hd->lockkey = 0; return ret; } int hdhomerun_device_tuner_lockkey_force(struct hdhomerun_device_t *hd) { if (hd->multicast_ip != 0) { return 1; } if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_tuner_lockkey_force: device not set\n"); return -1; } char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/lockkey", hd->tuner); int ret = hdhomerun_control_set(hd->cs, name, "force", NULL, NULL); hd->lockkey = 0; return ret; } void hdhomerun_device_tuner_lockkey_use_value(struct hdhomerun_device_t *hd, uint32_t lockkey) { if (hd->multicast_ip != 0) { return; } hd->lockkey = lockkey; } int hdhomerun_device_wait_for_lock(struct hdhomerun_device_t *hd, struct hdhomerun_tuner_status_t *status) { /* Delay for SS reading to be valid (signal present). */ msleep_minimum(250); /* Wait for up to 2.5 seconds for lock. */ uint64_t timeout = getcurrenttime() + 2500; while (1) { /* Get status to check for lock. Quality numbers will not be valid yet. */ int ret = hdhomerun_device_get_tuner_status(hd, NULL, status); if (ret <= 0) { return ret; } if (!status->signal_present) { return 1; } if (status->lock_supported || status->lock_unsupported) { return 1; } if (getcurrenttime() >= timeout) { return 1; } msleep_approx(250); } } int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd) { hdhomerun_device_get_video_sock(hd); if (!hd->vs) { return -1; } hdhomerun_video_set_keepalive(hd->vs, 0, 0, 0); /* Set target. */ if (hd->multicast_ip != 0) { int ret = hdhomerun_video_join_multicast_group(hd->vs, hd->multicast_ip, 0); if (ret <= 0) { return ret; } } else { int ret = hdhomerun_device_set_tuner_target_to_local(hd, HDHOMERUN_TARGET_PROTOCOL_RTP); if (ret == 0) { ret = hdhomerun_device_set_tuner_target_to_local(hd, HDHOMERUN_TARGET_PROTOCOL_UDP); } if (ret <= 0) { return ret; } uint32_t remote_ip = hdhomerun_control_get_device_ip(hd->cs); hdhomerun_video_set_keepalive(hd->vs, remote_ip, 5004, hd->lockkey); } /* Flush video buffer. */ msleep_minimum(64); hdhomerun_video_flush(hd->vs); /* Success. */ return 1; } uint8_t *hdhomerun_device_stream_recv(struct hdhomerun_device_t *hd, size_t max_size, size_t *pactual_size) { if (!hd->vs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_stream_recv: video not initialized\n"); return NULL; } return hdhomerun_video_recv(hd->vs, max_size, pactual_size); } void hdhomerun_device_stream_flush(struct hdhomerun_device_t *hd) { if (!hd->vs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_stream_flush: video not initialized\n"); return; } hdhomerun_video_flush(hd->vs); } void hdhomerun_device_stream_stop(struct hdhomerun_device_t *hd) { if (!hd->vs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_stream_stop: video not initialized\n"); return; } if (hd->multicast_ip != 0) { hdhomerun_video_leave_multicast_group(hd->vs, hd->multicast_ip, 0); } else { hdhomerun_device_set_tuner_target(hd, "none"); } } int hdhomerun_device_channelscan_init(struct hdhomerun_device_t *hd, const char *channelmap) { if (hd->scan) { channelscan_destroy(hd->scan); } hd->scan = channelscan_create(hd, channelmap); if (!hd->scan) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_channelscan_init: failed to create scan object\n"); return -1; } return 1; } int hdhomerun_device_channelscan_advance(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result) { if (!hd->scan) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_channelscan_advance: scan not initialized\n"); return 0; } int ret = channelscan_advance(hd->scan, result); if (ret <= 0) { /* Free scan if normal finish or fatal error */ channelscan_destroy(hd->scan); hd->scan = NULL; } return ret; } int hdhomerun_device_channelscan_detect(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result) { if (!hd->scan) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_channelscan_detect: scan not initialized\n"); return 0; } int ret = channelscan_detect(hd->scan, result); if (ret < 0) { /* Free scan if fatal error */ channelscan_destroy(hd->scan); hd->scan = NULL; } return ret; } uint8_t hdhomerun_device_channelscan_get_progress(struct hdhomerun_device_t *hd) { if (!hd->scan) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_channelscan_get_progress: scan not initialized\n"); return 0; } return channelscan_get_progress(hd->scan); } const char *hdhomerun_device_get_hw_model_str(struct hdhomerun_device_t *hd) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_hw_model_str: device not set\n"); return NULL; } char *model_str; int ret = hdhomerun_control_get(hd->cs, "/sys/hwmodel", &model_str, NULL); if (ret < 0) { return NULL; } return model_str; } const char *hdhomerun_device_get_model_str(struct hdhomerun_device_t *hd) { if (*hd->model) { return hd->model; } if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_model_str: device not set\n"); return NULL; } char *model_str; int ret = hdhomerun_control_get(hd->cs, "/sys/model", &model_str, NULL); if (ret < 0) { return NULL; } if (ret == 0) { hdhomerun_sprintf(hd->model, hd->model + sizeof(hd->model), "hdhomerun_atsc"); return hd->model; } hdhomerun_sprintf(hd->model, hd->model + sizeof(hd->model), "%s", model_str); return hd->model; } int hdhomerun_device_upgrade(struct hdhomerun_device_t *hd, FILE *upgrade_file) { if (!hd->cs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_upgrade: device not set\n"); return -1; } hdhomerun_control_set(hd->cs, "/tuner0/lockkey", "force", NULL, NULL); hdhomerun_control_set(hd->cs, "/tuner0/channel", "none", NULL, NULL); hdhomerun_control_set(hd->cs, "/tuner1/lockkey", "force", NULL, NULL); hdhomerun_control_set(hd->cs, "/tuner1/channel", "none", NULL, NULL); return hdhomerun_control_upgrade(hd->cs, upgrade_file); } void hdhomerun_device_debug_print_video_stats(struct hdhomerun_device_t *hd) { if (!hdhomerun_debug_enabled(hd->dbg)) { return; } if (hd->cs) { char name[32]; hdhomerun_sprintf(name, name + sizeof(name), "/tuner%u/debug", hd->tuner); char *debug_str; char *error_str; int ret = hdhomerun_control_get(hd->cs, name, &debug_str, &error_str); if (ret < 0) { hdhomerun_debug_printf(hd->dbg, "video dev: communication error getting debug stats\n"); return; } if (error_str) { hdhomerun_debug_printf(hd->dbg, "video dev: %s\n", error_str); } else { hdhomerun_debug_printf(hd->dbg, "video dev: %s\n", debug_str); } } if (hd->vs) { hdhomerun_video_debug_print_stats(hd->vs); } } void hdhomerun_device_get_video_stats(struct hdhomerun_device_t *hd, struct hdhomerun_video_stats_t *stats) { if (!hd->vs) { hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_stream_flush: video not initialized\n"); memset(stats, 0, sizeof(struct hdhomerun_video_stats_t)); return; } hdhomerun_video_get_stats(hd->vs, stats); } libhdhomerun/hdhomerun_device.h0000664000175000017500000003520113737525222016212 0ustar buildbuild/* * hdhomerun_device.h * * Copyright © 2006-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif #define HDHOMERUN_DEVICE_MAX_TUNE_TO_LOCK_TIME 1500 #define HDHOMERUN_DEVICE_MAX_LOCK_TO_DATA_TIME 2000 #define HDHOMERUN_DEVICE_MAX_TUNE_TO_DATA_TIME (HDHOMERUN_DEVICE_MAX_TUNE_TO_LOCK_TIME + HDHOMERUN_DEVICE_MAX_LOCK_TO_DATA_TIME) #define HDHOMERUN_TARGET_PROTOCOL_UDP "udp" #define HDHOMERUN_TARGET_PROTOCOL_RTP "rtp" /* * Create a device object. * * Typically a device object will be created for each tuner. * It is valid to have multiple device objects communicating with a single HDHomeRun. * * For example, a threaded application that streams video from 4 tuners (2 HDHomeRun devices) and has * GUI feedback to the user of the selected tuner might use 5 device objects: 4 for streaming video * (one per thread) and one for the GUI display that can switch between tuners. * * This function will not attempt to connect to the device. The connection will be established when first used. * * uint32_t device_id = 32-bit device id of device. Set to HDHOMERUN_DEVICE_ID_WILDCARD to match any device ID. * uint32_t device_ip = IP address of device. Set to 0 to auto-detect. * unsigned int tuner = tuner index (0 or 1). Can be changed later by calling hdhomerun_device_set_tuner. * struct hdhomerun_debug_t *dbg: Pointer to debug logging object. May be NULL. * * Returns a pointer to the newly created device object. * * When no longer needed, the socket should be destroyed by calling hdhomerun_device_destroy. * * The hdhomerun_device_create_from_str function creates a device object from the given device_str. * The device_str parameter can be any of the following forms: * * - * * If the tuner index is not included in the device_str then it is set to zero. Use hdhomerun_device_set_tuner * or hdhomerun_device_set_tuner_from_str to set the tuner. * * The hdhomerun_device_set_tuner_from_str function sets the tuner from the given tuner_str. * The tuner_str parameter can be any of the following forms: * * /tuner */ extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t device_ip, unsigned int tuner, struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_create_multicast(uint32_t multicast_ip, uint16_t multicast_port, struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_create_from_str(const char *device_str, struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API void hdhomerun_device_destroy(struct hdhomerun_device_t *hd); /* * Get the device id, ip, or tuner of the device instance. */ extern LIBHDHOMERUN_API const char *hdhomerun_device_get_name(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip); extern LIBHDHOMERUN_API int hdhomerun_device_set_multicast(struct hdhomerun_device_t *hd, uint32_t multicast_ip, uint16_t multicast_port); extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner); extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_from_str(struct hdhomerun_device_t *hd, const char *tuner_str); /* * Get the local machine IP address used when communicating with the device. * * This function is useful for determining the IP address to use with set target commands. * * Returns 32-bit IP address with native endianness, or 0 on error. */ extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_local_machine_addr(struct hdhomerun_device_t *hd); /* * Get operations. * * struct hdhomerun_tuner_status_t *status = Pointer to caller supplied status struct to be populated with result. * const char **p = Caller supplied char * to be updated to point to the result string. The string will remain * valid until another call to a device function. * * Returns 1 if the operation was successful. * Returns 0 if the operation was rejected. * Returns -1 if a communication error occurred. */ extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_status(struct hdhomerun_device_t *hd, char **pstatus_str, struct hdhomerun_tuner_status_t *status); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_vstatus(struct hdhomerun_device_t *hd, char **pvstatus_str, struct hdhomerun_tuner_vstatus_t *vstatus); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_plpinfo(struct hdhomerun_device_t *hd, char **pplpinfo); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_streaminfo(struct hdhomerun_device_t *hd, char **pstreaminfo); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_channel(struct hdhomerun_device_t *hd, char **pchannel); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_vchannel(struct hdhomerun_device_t *hd, char **pvchannel); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_channelmap(struct hdhomerun_device_t *hd, char **pchannelmap); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_filter(struct hdhomerun_device_t *hd, char **pfilter); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_program(struct hdhomerun_device_t *hd, char **pprogram); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_target(struct hdhomerun_device_t *hd, char **ptarget); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount); extern LIBHDHOMERUN_API int hdhomerun_device_get_tuner_lockkey_owner(struct hdhomerun_device_t *hd, char **powner); extern LIBHDHOMERUN_API int hdhomerun_device_get_oob_status(struct hdhomerun_device_t *hd, char **pstatus_str, struct hdhomerun_tuner_status_t *status); extern LIBHDHOMERUN_API int hdhomerun_device_get_oob_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount); extern LIBHDHOMERUN_API int hdhomerun_device_get_ir_target(struct hdhomerun_device_t *hd, char **ptarget); extern LIBHDHOMERUN_API int hdhomerun_device_get_version(struct hdhomerun_device_t *hd, char **pversion_str, uint32_t *pversion_num); extern LIBHDHOMERUN_API int hdhomerun_device_get_supported(struct hdhomerun_device_t *hd, char *prefix, char **pstr); extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_tuner_status_ss_color(struct hdhomerun_tuner_status_t *status); extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_tuner_status_snq_color(struct hdhomerun_tuner_status_t *status); extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_tuner_status_seq_color(struct hdhomerun_tuner_status_t *status); extern LIBHDHOMERUN_API const char *hdhomerun_device_get_hw_model_str(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API const char *hdhomerun_device_get_model_str(struct hdhomerun_device_t *hd); /* * Set operations. * * const char * = String to send to device. * * Returns 1 if the operation was successful. * Returns 0 if the operation was rejected. * Returns -1 if a communication error occurred. */ extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_channel(struct hdhomerun_device_t *hd, const char *channel); extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_vchannel(struct hdhomerun_device_t *hd, const char *vchannel); extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_channelmap(struct hdhomerun_device_t *hd, const char *channelmap); extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_filter(struct hdhomerun_device_t *hd, const char *filter); extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_filter_by_array(struct hdhomerun_device_t *hd, unsigned char filter_array[0x2000]); extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_program(struct hdhomerun_device_t *hd, const char *program); extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_target(struct hdhomerun_device_t *hd, const char *target); extern LIBHDHOMERUN_API int hdhomerun_device_set_ir_target(struct hdhomerun_device_t *hd, const char *target); extern LIBHDHOMERUN_API int hdhomerun_device_set_sys_dvbc_modulation(struct hdhomerun_device_t *hd, const char *modulation_list); /* * Get/set a named control variable on the device. * * const char *name: The name of var to get/set (c-string). The supported vars is device/firmware dependant. * const char *value: The value to set (c-string). The format is device/firmware dependant. * char **pvalue: If provided, the caller-supplied char pointer will be populated with a pointer to the value * string returned by the device, or NULL if the device returned an error string. The string will remain * valid until the next call to a control sock function. * char **perror: If provided, the caller-supplied char pointer will be populated with a pointer to the error * string returned by the device, or NULL if the device returned an value string. The string will remain * valid until the next call to a control sock function. * * Returns 1 if the operation was successful (pvalue set, perror NULL). * Returns 0 if the operation was rejected (pvalue NULL, perror set). * Returns -1 if a communication error occurs. */ extern LIBHDHOMERUN_API int hdhomerun_device_get_var(struct hdhomerun_device_t *hd, const char *name, char **pvalue, char **perror); extern LIBHDHOMERUN_API int hdhomerun_device_set_var(struct hdhomerun_device_t *hd, const char *name, const char *value, char **pvalue, char **perror); /* * Tuner locking. * * The hdhomerun_device_tuner_lockkey_request function is used to obtain a lock * or to verify that the hdhomerun_device object still holds the lock. * Returns 1 if the lock request was successful and the lock was obtained. * Returns 0 if the lock request was rejected. * Returns -1 if a communication error occurs. * * The hdhomerun_device_tuner_lockkey_release function is used to release a * previously held lock. If locking is used then this function must be called * before destroying the hdhomerun_device object. */ extern LIBHDHOMERUN_API int hdhomerun_device_tuner_lockkey_request(struct hdhomerun_device_t *hd, char **perror); extern LIBHDHOMERUN_API int hdhomerun_device_tuner_lockkey_release(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API int hdhomerun_device_tuner_lockkey_force(struct hdhomerun_device_t *hd); /* * Intended only for non persistent connections; eg, hdhomerun_config. */ extern LIBHDHOMERUN_API void hdhomerun_device_tuner_lockkey_use_value(struct hdhomerun_device_t *hd, uint32_t lockkey); /* * Wait for tuner lock after channel change. * * The hdhomerun_device_wait_for_lock function is used to detect/wait for a lock vs no lock indication * after a channel change. * * It will return quickly if a lock is aquired. * It will return quickly if there is no signal detected. * Worst case it will time out after 1.5 seconds - the case where there is signal but no lock. */ extern LIBHDHOMERUN_API int hdhomerun_device_wait_for_lock(struct hdhomerun_device_t *hd, struct hdhomerun_tuner_status_t *status); /* * Stream a filtered program or the unfiltered stream. * * The hdhomerun_device_stream_start function initializes the process and tells the device to start streamin data. * * uint16_t program_number = The program number to filer, or 0 for unfiltered. * * Returns 1 if the oprtation started successfully. * Returns 0 if the operation was rejected. * Returns -1 if a communication error occurs. * * The hdhomerun_device_stream_recv function should be called periodically to receive the stream data. * The buffer can losslessly store 1 second of data, however a more typical call rate would be every 15ms. * * The hdhomerun_device_stream_stop function tells the device to stop streaming data. */ extern LIBHDHOMERUN_API int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API uint8_t *hdhomerun_device_stream_recv(struct hdhomerun_device_t *hd, size_t max_size, size_t *pactual_size); extern LIBHDHOMERUN_API void hdhomerun_device_stream_flush(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API void hdhomerun_device_stream_stop(struct hdhomerun_device_t *hd); /* * Channel scan API. */ extern LIBHDHOMERUN_API int hdhomerun_device_channelscan_init(struct hdhomerun_device_t *hd, const char *channelmap); extern LIBHDHOMERUN_API int hdhomerun_device_channelscan_advance(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result); extern LIBHDHOMERUN_API int hdhomerun_device_channelscan_detect(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result); extern LIBHDHOMERUN_API uint8_t hdhomerun_device_channelscan_get_progress(struct hdhomerun_device_t *hd); /* * Upload new firmware to the device. * * FILE *upgrade_file: File pointer to read from. The file must have been opened in binary mode for reading. * * Returns 1 if the upload succeeded. * Returns 0 if the upload was rejected. * Returns -1 if an error occurs. */ extern LIBHDHOMERUN_API int hdhomerun_device_upgrade(struct hdhomerun_device_t *hd, FILE *upgrade_file); /* * Low level accessor functions. */ extern LIBHDHOMERUN_API struct hdhomerun_control_sock_t *hdhomerun_device_get_control_sock(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdhomerun_device_t *hd); /* * Debug print internal stats. */ extern LIBHDHOMERUN_API void hdhomerun_device_debug_print_video_stats(struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API void hdhomerun_device_get_video_stats(struct hdhomerun_device_t *hd, struct hdhomerun_video_stats_t *stats); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_device_selector.c0000664000175000017500000003162213063620312020074 0ustar buildbuild/* * hdhomerun_device_selector.c * * Copyright © 2009-2016 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" struct hdhomerun_device_selector_t { struct hdhomerun_device_t **hd_list; size_t hd_count; struct hdhomerun_debug_t *dbg; }; struct hdhomerun_device_selector_t *hdhomerun_device_selector_create(struct hdhomerun_debug_t *dbg) { struct hdhomerun_device_selector_t *hds = (struct hdhomerun_device_selector_t *)calloc(1, sizeof(struct hdhomerun_device_selector_t)); if (!hds) { hdhomerun_debug_printf(dbg, "hdhomerun_device_selector_create: failed to allocate selector object\n"); return NULL; } hds->dbg = dbg; return hds; } void hdhomerun_device_selector_destroy(struct hdhomerun_device_selector_t *hds, bool destroy_devices) { if (destroy_devices) { size_t index; for (index = 0; index < hds->hd_count; index++) { struct hdhomerun_device_t *entry = hds->hd_list[index]; hdhomerun_device_destroy(entry); } } if (hds->hd_list) { free(hds->hd_list); } free(hds); } int hdhomerun_device_selector_get_device_count(struct hdhomerun_device_selector_t *hds) { return (int)hds->hd_count; } void hdhomerun_device_selector_add_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd) { size_t index; for (index = 0; index < hds->hd_count; index++) { struct hdhomerun_device_t *entry = hds->hd_list[index]; if (entry == hd) { return; } } struct hdhomerun_device_t **hd_list = (struct hdhomerun_device_t **)realloc(hds->hd_list, (hds->hd_count + 1) * sizeof(struct hdhomerun_device_t *)); if (!hd_list) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_add_device: failed to allocate device list\n"); return; } hds->hd_list = hd_list; hds->hd_list[hds->hd_count++] = hd; } void hdhomerun_device_selector_remove_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd) { size_t index = 0; while (1) { if (index >= hds->hd_count) { return; } struct hdhomerun_device_t *entry = hds->hd_list[index]; if (entry == hd) { break; } index++; } while (index + 1 < hds->hd_count) { hds->hd_list[index] = hds->hd_list[index + 1]; index++; } hds->hd_list[index] = NULL; hds->hd_count--; } struct hdhomerun_device_t *hdhomerun_device_selector_find_device(struct hdhomerun_device_selector_t *hds, uint32_t device_id, unsigned int tuner_index) { size_t index; for (index = 0; index < hds->hd_count; index++) { struct hdhomerun_device_t *entry = hds->hd_list[index]; if (hdhomerun_device_get_device_id(entry) != device_id) { continue; } if (hdhomerun_device_get_tuner(entry) != tuner_index) { continue; } return entry; } return NULL; } static int hdhomerun_device_selector_load_from_str_discover(struct hdhomerun_device_selector_t *hds, uint32_t target_ip, uint32_t device_id) { struct hdhomerun_discover_device_t result; int discover_count = hdhomerun_discover_find_devices_custom_v2(target_ip, HDHOMERUN_DEVICE_TYPE_TUNER, device_id, &result, 1); if (discover_count != 1) { return 0; } int count = 0; unsigned int tuner_index; for (tuner_index = 0; tuner_index < result.tuner_count; tuner_index++) { struct hdhomerun_device_t *hd = hdhomerun_device_create(result.device_id, result.ip_addr, tuner_index, hds->dbg); if (!hd) { continue; } hdhomerun_device_selector_add_device(hds, hd); count++; } return count; } int hdhomerun_device_selector_load_from_str(struct hdhomerun_device_selector_t *hds, char *device_str) { /* * IP address based device_str. */ unsigned int a[4]; if (sscanf(device_str, "%u.%u.%u.%u", &a[0], &a[1], &a[2], &a[3]) == 4) { uint32_t ip_addr = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0)); /* * Multicast IP address. */ unsigned int port; if (sscanf(device_str, "%u.%u.%u.%u:%u", &a[0], &a[1], &a[2], &a[3], &port) == 5) { struct hdhomerun_device_t *hd = hdhomerun_device_create_multicast(ip_addr, (uint16_t)port, hds->dbg); if (!hd) { return 0; } hdhomerun_device_selector_add_device(hds, hd); return 1; } /* * IP address + tuner number. */ unsigned int tuner; if (sscanf(device_str, "%u.%u.%u.%u-%u", &a[0], &a[1], &a[2], &a[3], &tuner) == 5) { struct hdhomerun_device_t *hd = hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, ip_addr, tuner, hds->dbg); if (!hd) { return 0; } hdhomerun_device_selector_add_device(hds, hd); return 1; } /* * IP address only - discover and add tuners. */ return hdhomerun_device_selector_load_from_str_discover(hds, ip_addr, HDHOMERUN_DEVICE_ID_WILDCARD); } /* * Device ID based device_str. */ char *end; uint32_t device_id = (uint32_t)strtoul(device_str, &end, 16); if ((end == device_str + 8) && hdhomerun_discover_validate_device_id(device_id)) { /* * IP address + tuner number. */ if (*end == '-') { unsigned int tuner = (unsigned int)strtoul(end + 1, NULL, 10); struct hdhomerun_device_t *hd = hdhomerun_device_create(device_id, 0, tuner, hds->dbg); if (!hd) { return 0; } hdhomerun_device_selector_add_device(hds, hd); return 1; } /* * Device ID only - discover and add tuners. */ return hdhomerun_device_selector_load_from_str_discover(hds, 0, device_id); } /* * DNS based device_str. */ struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; struct addrinfo *sock_info; if (getaddrinfo(device_str, "65001", &hints, &sock_info) != 0) { return 0; } struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr; uint32_t ip_addr = (uint32_t)ntohl(sock_addr->sin_addr.s_addr); freeaddrinfo(sock_info); if (ip_addr == 0) { return 0; } return hdhomerun_device_selector_load_from_str_discover(hds, ip_addr, HDHOMERUN_DEVICE_ID_WILDCARD); } int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename) { FILE *fp = fopen(filename, "r"); if (!fp) { return 0; } int count = 0; while(1) { char device_str[32]; if (!fgets(device_str, sizeof(device_str), fp)) { break; } count += hdhomerun_device_selector_load_from_str(hds, device_str); } fclose(fp); return count; } #if defined(_WIN32) && !defined(_WINRT) int hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource) { HKEY tuners_key; LONG ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Silicondust\\HDHomeRun\\Tuners", 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &tuners_key); if (ret != ERROR_SUCCESS) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open tuners registry key (%ld)\n", (long)ret); return 0; } int count = 0; DWORD index = 0; while (1) { /* Next tuner device. */ wchar_t wdevice_str[32]; DWORD size = sizeof(wdevice_str); ret = RegEnumKeyEx(tuners_key, index++, wdevice_str, &size, NULL, NULL, NULL, NULL); if (ret != ERROR_SUCCESS) { break; } /* Check device configuation. */ HKEY device_key; ret = RegOpenKeyEx(tuners_key, wdevice_str, 0, KEY_QUERY_VALUE, &device_key); if (ret != ERROR_SUCCESS) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open registry key for %S (%ld)\n", wdevice_str, (long)ret); continue; } wchar_t wsource_test[32]; size = sizeof(wsource_test); if (RegQueryValueEx(device_key, L"Source", NULL, NULL, (LPBYTE)&wsource_test, &size) != ERROR_SUCCESS) { wsprintf(wsource_test, L"Unknown"); } RegCloseKey(device_key); if (_wcsicmp(wsource_test, wsource) != 0) { continue; } /* Create and add device. */ char device_str[32]; hdhomerun_sprintf(device_str, device_str + sizeof(device_str), "%S", wdevice_str); count += hdhomerun_device_selector_load_from_str(hds, device_str); } RegCloseKey(tuners_key); return count; } #endif static bool hdhomerun_device_selector_choose_test(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *test_hd) { const char *name = hdhomerun_device_get_name(test_hd); /* * Attempt to aquire lock. */ char *error = NULL; int ret = hdhomerun_device_tuner_lockkey_request(test_hd, &error); if (ret > 0) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s chosen\n", name); return true; } if (ret < 0) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name); return false; } /* * In use - check target. */ char *target; ret = hdhomerun_device_get_tuner_target(test_hd, &target); if (ret < 0) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name); return false; } if (ret == 0) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, failed to read target\n", name); return false; } if (strcmp(target, "none") == 0) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, no target set\n", name); return false; } if ((strncmp(target, "udp://", 6) != 0) && (strncmp(target, "rtp://", 6) != 0)) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by %s\n", name, target); return false; } unsigned int a[4]; unsigned int target_port; if (sscanf(target + 6, "%u.%u.%u.%u:%u", &a[0], &a[1], &a[2], &a[3], &target_port) != 5) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, unexpected target set (%s)\n", name, target); return false; } uint32_t target_ip = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0)); uint32_t local_ip = hdhomerun_device_get_local_machine_addr(test_hd); if (target_ip != local_ip) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by %s\n", name, target); return false; } /* * Test local port. */ struct hdhomerun_sock_t *test_sock = hdhomerun_sock_create_udp(); if (!test_sock) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, failed to create test sock\n", name); return false; } bool inuse = (hdhomerun_sock_bind(test_sock, INADDR_ANY, (uint16_t)target_port, false) == false); hdhomerun_sock_destroy(test_sock); if (inuse) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine\n", name); return false; } /* * Dead local target, force clear lock. */ ret = hdhomerun_device_tuner_lockkey_force(test_hd); if (ret < 0) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name); return false; } if (ret == 0) { 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); return false; } hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine, dead target, lockkey force successful\n", name); /* * Attempt to aquire lock. */ ret = hdhomerun_device_tuner_lockkey_request(test_hd, &error); if (ret > 0) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s chosen\n", name); return true; } if (ret < 0) { hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name); return false; } hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s still in use after lockkey force (%s)\n", name, error); return false; } struct hdhomerun_device_t *hdhomerun_device_selector_choose_and_lock(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *prefered) { /* Test prefered device first. */ if (prefered) { if (hdhomerun_device_selector_choose_test(hds, prefered)) { return prefered; } } /* Test other tuners. */ size_t index; for (index = 0; index < hds->hd_count; index++) { struct hdhomerun_device_t *entry = hds->hd_list[index]; if (entry == prefered) { continue; } if (hdhomerun_device_selector_choose_test(hds, entry)) { return entry; } } hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_and_lock: no devices available\n"); return NULL; } libhdhomerun/hdhomerun_device_selector.h0000664000175000017500000000775713063620312020115 0ustar buildbuild/* * hdhomerun_device_selector.h * * Copyright © 2009-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif /* * Create a device selector object for use with dynamic tuner allocation support. * All tuners registered with a specific device selector instance must have the same signal source. * The dbg parameter may be null. */ extern LIBHDHOMERUN_API struct hdhomerun_device_selector_t *hdhomerun_device_selector_create(struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API void hdhomerun_device_selector_destroy(struct hdhomerun_device_selector_t *hds, bool destroy_devices); /* * Get the number of devices in the list. */ extern LIBHDHOMERUN_API int hdhomerun_device_selector_get_device_count(struct hdhomerun_device_selector_t *hds); /* * Populate device selector with devices from given source. * Returns the number of devices populated. */ extern LIBHDHOMERUN_API int hdhomerun_device_selector_load_from_str(struct hdhomerun_device_selector_t *hds, char *device_str); extern LIBHDHOMERUN_API int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename); #if defined(_WIN32) && !defined(_WINRT) extern LIBHDHOMERUN_API int hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource); #endif /* * Add/remove a device from the selector list. */ extern LIBHDHOMERUN_API void hdhomerun_device_selector_add_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd); extern LIBHDHOMERUN_API void hdhomerun_device_selector_remove_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd); /* * Find a device in the selector list. */ extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_selector_find_device(struct hdhomerun_device_selector_t *hds, uint32_t device_id, unsigned int tuner_index); /* * Select and lock an available device. * If not null, preference will be given to the prefered device specified. * The device resource lock must be released by the application when no longer needed by * calling hdhomerun_device_tuner_lockkey_release(). * * Recommended channel change logic: * * Start (inactive -> active): * - Call hdhomerun_device_selector_choose_and_lock() to choose and lock an available tuner. * * Stop (active -> inactive): * - Call hdhomerun_device_tuner_lockkey_release() to release the resource lock and allow the tuner * to be allocated by other computers. * * Channel change (active -> active): * - If the new channel has a different signal source then call hdhomerun_device_tuner_lockkey_release() * to release the lock on the tuner playing the previous channel, then call * hdhomerun_device_selector_choose_and_lock() to choose and lock an available tuner. * - If the new channel has the same signal source then call hdhomerun_device_tuner_lockkey_request() * to refresh the lock. If this function succeeds then the same device can be used. If this fucntion fails * then call hdhomerun_device_selector_choose_and_lock() to choose and lock an available tuner. */ extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_selector_choose_and_lock(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *prefered); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_discover.c0000664000175000017500000004212714053107271016561 0ustar buildbuild/* * hdhomerun_discover.c * * Copyright © 2006-2017 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" #define HDHOMERUN_DISCOVER_MAX_SOCK_COUNT 16 struct hdhomerun_discover_sock_t { struct hdhomerun_sock_t *sock; bool detected; uint32_t local_ip; uint32_t subnet_mask; }; struct hdhomerun_discover_t { struct hdhomerun_discover_sock_t socks[HDHOMERUN_DISCOVER_MAX_SOCK_COUNT]; unsigned int sock_count; struct hdhomerun_pkt_t tx_pkt; struct hdhomerun_pkt_t rx_pkt; struct hdhomerun_debug_t *dbg; }; static bool hdhomerun_discover_sock_add(struct hdhomerun_discover_t *ds, uint32_t local_ip, uint32_t subnet_mask) { unsigned int i; for (i = 1; i < ds->sock_count; i++) { struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; if ((dss->local_ip == local_ip) && (dss->subnet_mask == subnet_mask)) { dss->detected = true; return true; } } if (ds->sock_count >= HDHOMERUN_DISCOVER_MAX_SOCK_COUNT) { return false; } /* Create socket. */ struct hdhomerun_sock_t *sock = hdhomerun_sock_create_udp(); if (!sock) { hdhomerun_debug_printf(ds->dbg, "discover: failed to allocate socket (%d)\n", hdhomerun_sock_getlasterror()); return false; } /* Bind socket. */ if (!hdhomerun_sock_bind(sock, local_ip, 0, false)) { hdhomerun_debug_printf(ds->dbg, "discover: failed to bind to %u.%u.%u.%u:0\n", (unsigned int)(local_ip >> 24) & 0xFF, (unsigned int)(local_ip >> 16) & 0xFF, (unsigned int)(local_ip >> 8) & 0xFF, (unsigned int)(local_ip >> 0) & 0xFF); hdhomerun_sock_destroy(sock); return false; } /* Write sock entry. */ struct hdhomerun_discover_sock_t *dss = &ds->socks[ds->sock_count++]; dss->sock = sock; dss->detected = true; dss->local_ip = local_ip; dss->subnet_mask = subnet_mask; return true; } struct hdhomerun_discover_t *hdhomerun_discover_create(struct hdhomerun_debug_t *dbg) { struct hdhomerun_discover_t *ds = (struct hdhomerun_discover_t *)calloc(1, sizeof(struct hdhomerun_discover_t)); if (!ds) { return NULL; } ds->dbg = dbg; /* Create a routable socket (always first entry). */ if (!hdhomerun_discover_sock_add(ds, 0, 0)) { free(ds); return NULL; } /* Success. */ return ds; } void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds) { unsigned int i; for (i = 0; i < ds->sock_count; i++) { struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; hdhomerun_sock_destroy(dss->sock); } free(ds); } static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds) { unsigned int i; for (i = 1; i < ds->sock_count; i++) { struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; dss->detected = false; } struct hdhomerun_local_ip_info_t ip_info_list[HDHOMERUN_DISCOVER_MAX_SOCK_COUNT]; int count = hdhomerun_local_ip_info(ip_info_list, HDHOMERUN_DISCOVER_MAX_SOCK_COUNT); if (count < 0) { hdhomerun_debug_printf(ds->dbg, "discover: hdhomerun_local_ip_info returned error\n"); count = 0; } if (count > HDHOMERUN_DISCOVER_MAX_SOCK_COUNT) { hdhomerun_debug_printf(ds->dbg, "discover: too many local IP addresses\n"); count = HDHOMERUN_DISCOVER_MAX_SOCK_COUNT; } int index; for (index = 0; index < count; index++) { struct hdhomerun_local_ip_info_t *ip_info = &ip_info_list[index]; hdhomerun_discover_sock_add(ds, ip_info->ip_addr, ip_info->subnet_mask); } struct hdhomerun_discover_sock_t *src = &ds->socks[1]; struct hdhomerun_discover_sock_t *dst = &ds->socks[1]; count = 1; for (i = 1; i < ds->sock_count; i++) { if (!src->detected) { hdhomerun_sock_destroy(src->sock); src++; continue; } if (dst != src) { *dst = *src; } src++; dst++; count++; } ds->sock_count = (unsigned int)count; } static bool 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) { struct hdhomerun_pkt_t *tx_pkt = &ds->tx_pkt; hdhomerun_pkt_reset(tx_pkt); hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_DEVICE_TYPE); hdhomerun_pkt_write_var_length(tx_pkt, 4); hdhomerun_pkt_write_u32(tx_pkt, device_type); hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_DEVICE_ID); hdhomerun_pkt_write_var_length(tx_pkt, 4); hdhomerun_pkt_write_u32(tx_pkt, device_id); hdhomerun_pkt_seal_frame(tx_pkt, HDHOMERUN_TYPE_DISCOVER_REQ); return hdhomerun_sock_sendto(dss->sock, target_ip, HDHOMERUN_DISCOVER_UDP_PORT, tx_pkt->start, tx_pkt->end - tx_pkt->start, 0); } static bool hdhomerun_discover_send_targeted(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id) { /* * Send targeted packet from any local ip that is in the same subnet. * This is needed to support multiple separate 169.254.x.x interfaces. */ bool result = false; unsigned int i; for (i = 1; i < ds->sock_count; i++) { struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; if (dss->subnet_mask == 0) { continue; } if ((target_ip & dss->subnet_mask) != (dss->local_ip & dss->subnet_mask)) { continue; } result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id); } if (result) { return true; } /* * Fall back to letting the OS choose the interface. */ struct hdhomerun_discover_sock_t *dss = &ds->socks[0]; return hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id); } static bool hdhomerun_discover_send_broadcast(struct hdhomerun_discover_t *ds, uint32_t device_type, uint32_t device_id) { struct hdhomerun_discover_sock_t *dss; bool result = false; unsigned int i; #if !defined(IP_ONESBCAST) /* * Send global broadcast from every local ip. */ for (i = 1; i < ds->sock_count; i++) { dss = &ds->socks[i]; result |= hdhomerun_discover_send_internal(ds, dss, 0xFFFFFFFF, device_type, device_id); } if (result) { return true; } /* * Fall back to letting the OS choose the interface. */ dss = &ds->socks[0]; if (hdhomerun_discover_send_internal(ds, dss, 0xFFFFFFFF, device_type, device_id)) { return true; } #endif /* * Fall back to sending subnet broadcasts. */ for (i = 1; i < ds->sock_count; i++) { dss = &ds->socks[i]; if (dss->subnet_mask == 0) { continue; } uint32_t send_ip = dss->local_ip | ~dss->subnet_mask; if (send_ip >= 0xE0000000) { continue; } result |= hdhomerun_discover_send_internal(ds, dss, send_ip, device_type, device_id); } return result; } static bool hdhomerun_discover_send(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id) { if (target_ip == 0xFFFFFFFF) { return hdhomerun_discover_send_broadcast(ds, device_type, device_id); } else { return hdhomerun_discover_send_targeted(ds, target_ip, device_type, device_id); } } static bool hdhomerun_discover_is_legacy(uint32_t device_id) { switch (device_id >> 20) { case 0x100: /* TECH-US/TECH3-US */ return (device_id < 0x10040000); case 0x120: /* TECH3-EU */ return (device_id < 0x12030000); case 0x101: /* HDHR-US */ case 0x102: /* HDHR-T1-US */ case 0x103: /* HDHR3-US */ case 0x111: /* HDHR3-DT */ case 0x121: /* HDHR-EU */ case 0x122: /* HDHR3-EU */ return true; default: return false; } } static bool hdhomerun_discover_recv_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, size_t result_struct_size, struct hdhomerun_discover_device_t *result) { static char hdhomerun_discover_recv_base64_encode_table[64 + 1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; struct hdhomerun_pkt_t *rx_pkt = &ds->rx_pkt; hdhomerun_pkt_reset(rx_pkt); uint32_t remote_addr; uint16_t remote_port; size_t length = rx_pkt->limit - rx_pkt->end; if (!hdhomerun_sock_recvfrom(dss->sock, &remote_addr, &remote_port, rx_pkt->end, &length, 0)) { return false; } rx_pkt->end += length; uint16_t type; if (hdhomerun_pkt_open_frame(rx_pkt, &type) <= 0) { return false; } if (type != HDHOMERUN_TYPE_DISCOVER_RPY) { return false; } struct hdhomerun_discover_device_v3_t *result_v3 = (result_struct_size >= sizeof(struct hdhomerun_discover_device_v3_t)) ? (struct hdhomerun_discover_device_v3_t *)(void *)result : NULL; memset(result, 0, result_struct_size); result->ip_addr = remote_addr; while (1) { uint8_t tag; size_t len; uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len); if (!next) { break; } int i; switch (tag) { case HDHOMERUN_TAG_DEVICE_TYPE: if (len != 4) { break; } result->device_type = hdhomerun_pkt_read_u32(rx_pkt); break; case HDHOMERUN_TAG_DEVICE_ID: if (len != 4) { break; } result->device_id = hdhomerun_pkt_read_u32(rx_pkt); result->is_legacy = hdhomerun_discover_is_legacy(result->device_id); break; case HDHOMERUN_TAG_TUNER_COUNT: if (len != 1) { break; } result->tuner_count = hdhomerun_pkt_read_u8(rx_pkt); break; case HDHOMERUN_TAG_DEVICE_AUTH_STR: if (len >= sizeof(result->device_auth)) { break; } hdhomerun_pkt_read_mem(rx_pkt, result->device_auth, len); result->device_auth[len] = 0; break; case HDHOMERUN_TAG_DEVICE_AUTH_BIN_DEPRECATED: if (len != 18) { break; } for (i = 0; i < 24; i += 4) { uint32_t raw24; raw24 = (uint32_t)hdhomerun_pkt_read_u8(rx_pkt) << 16; raw24 |= (uint32_t)hdhomerun_pkt_read_u8(rx_pkt) << 8; raw24 |= (uint32_t)hdhomerun_pkt_read_u8(rx_pkt) << 0; result->device_auth[i + 0] = hdhomerun_discover_recv_base64_encode_table[(raw24 >> 18) & 0x3F]; result->device_auth[i + 1] = hdhomerun_discover_recv_base64_encode_table[(raw24 >> 12) & 0x3F]; result->device_auth[i + 2] = hdhomerun_discover_recv_base64_encode_table[(raw24 >> 6) & 0x3F]; result->device_auth[i + 3] = hdhomerun_discover_recv_base64_encode_table[(raw24 >> 0) & 0x3F]; } result->device_auth[24] = 0; break; case HDHOMERUN_TAG_BASE_URL: if (len >= sizeof(result->base_url)) { break; } hdhomerun_pkt_read_mem(rx_pkt, result->base_url, len); result->base_url[len] = 0; break; case HDHOMERUN_TAG_STORAGE_ID: if (!result_v3) { break; } if (len >= sizeof(result_v3->storage_id)) { break; } hdhomerun_pkt_read_mem(rx_pkt, result_v3->storage_id, len); result_v3->storage_id[len] = 0; break; case HDHOMERUN_TAG_LINEUP_URL: if (!result_v3) { break; } if (len >= sizeof(result_v3->lineup_url)) { break; } hdhomerun_pkt_read_mem(rx_pkt, result_v3->lineup_url, len); result_v3->lineup_url[len] = 0; break; case HDHOMERUN_TAG_STORAGE_URL: if (!result_v3) { break; } if (len >= sizeof(result_v3->storage_url)) { break; } hdhomerun_pkt_read_mem(rx_pkt, result_v3->storage_url, len); result_v3->storage_url[len] = 0; break; default: break; } rx_pkt->pos = next; } /* * Fixup for old firmware. */ if (result->device_type == HDHOMERUN_DEVICE_TYPE_TUNER) { if (result->tuner_count == 0) { switch (result->device_id >> 20) { case 0x102: result->tuner_count = 1; break; case 0x100: case 0x101: case 0x121: result->tuner_count = 2; break; default: break; } } if (!result->base_url[0]) { hdhomerun_sprintf(result->base_url, result->base_url + sizeof(result->base_url), "http://%u.%u.%u.%u:80", (remote_addr >> 24) & 0xFF, (remote_addr >> 16) & 0xFF, (remote_addr >> 8) & 0xFF, (remote_addr >> 0) & 0xFF ); } } return true; } static bool hdhomerun_discover_recv(struct hdhomerun_discover_t *ds, size_t result_struct_size, struct hdhomerun_discover_device_t *result) { unsigned int i; for (i = 0; i < ds->sock_count; i++) { struct hdhomerun_discover_sock_t *dss = &ds->socks[i]; if (hdhomerun_discover_recv_internal(ds, dss, result_struct_size, result)) { return true; } } return false; } static struct hdhomerun_discover_device_t *hdhomerun_discover_result_by_index(size_t result_struct_size, struct hdhomerun_discover_device_t result_list[], int index) { uint8_t *ptr = (uint8_t *)(void *)result_list; ptr += result_struct_size * index; return (struct hdhomerun_discover_device_t *)(void *)ptr; } static struct hdhomerun_discover_device_t *hdhomerun_discover_find_in_list(size_t result_struct_size, struct hdhomerun_discover_device_t result_list[], int count, struct hdhomerun_discover_device_t *lookup) { int index; for (index = 0; index < count; index++) { struct hdhomerun_discover_device_t *entry = hdhomerun_discover_result_by_index(result_struct_size, result_list, index); if (lookup->ip_addr != entry->ip_addr) { continue; } if (strcmp(lookup->base_url, entry->base_url) != 0) { continue; } return entry; } return NULL; } static int hdhomerun_discover_find_devices(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, size_t result_struct_size, struct hdhomerun_discover_device_t result_list[], int max_count) { if (target_ip == 0x00000000) { target_ip = 0xFFFFFFFF; } hdhomerun_discover_sock_detect(ds); int count = 0; int attempt; for (attempt = 0; attempt < 2; attempt++) { if (!hdhomerun_discover_send(ds, target_ip, device_type_match, device_id_match)) { return -1; } uint64_t timeout = getcurrenttime() + 200; while (1) { struct hdhomerun_discover_device_t *result = hdhomerun_discover_result_by_index(result_struct_size, result_list, count); if (!hdhomerun_discover_recv(ds, result_struct_size, result)) { if (getcurrenttime() >= timeout) { break; } msleep_approx(16); continue; } /* Filter */ if ((device_type_match != HDHOMERUN_DEVICE_TYPE_WILDCARD) && (result->device_type != device_type_match)) { continue; } if ((device_id_match != HDHOMERUN_DEVICE_ID_WILDCARD) && (result->device_id != device_id_match)) { continue; } /* Ensure not already in list. */ if (hdhomerun_discover_find_in_list(result_struct_size, result_list, count, result)) { continue; } /* Add to list. */ count++; if (count >= max_count) { return count; } } } return count; } int hdhomerun_discover_find_devices_v3(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_v3_t result_list[], int max_count) { return hdhomerun_discover_find_devices(ds, target_ip, device_type_match, device_id_match, sizeof(struct hdhomerun_discover_device_v3_t), (struct hdhomerun_discover_device_t *)(void *)result_list, max_count); } int hdhomerun_discover_find_devices_v2(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_t result_list[], int max_count) { return hdhomerun_discover_find_devices(ds, target_ip, device_type_match, device_id_match, sizeof(struct hdhomerun_discover_device_t), (struct hdhomerun_discover_device_t *)(void *)result_list, max_count); } int hdhomerun_discover_find_devices_custom_v3(uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_v3_t result_list[], int max_count) { struct hdhomerun_discover_t *ds = hdhomerun_discover_create(NULL); if (!ds) { return -1; } int ret = hdhomerun_discover_find_devices(ds, target_ip, device_type_match, device_id_match, sizeof(struct hdhomerun_discover_device_v3_t), (struct hdhomerun_discover_device_t *)(void *)result_list, max_count); hdhomerun_discover_destroy(ds); return ret; } int hdhomerun_discover_find_devices_custom_v2(uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_t result_list[], int max_count) { struct hdhomerun_discover_t *ds = hdhomerun_discover_create(NULL); if (!ds) { return -1; } int ret = hdhomerun_discover_find_devices(ds, target_ip, device_type_match, device_id_match, sizeof(struct hdhomerun_discover_device_t), (struct hdhomerun_discover_device_t *)(void *)result_list, max_count); hdhomerun_discover_destroy(ds); return ret; } bool hdhomerun_discover_validate_device_id(uint32_t device_id) { static uint8_t lookup_table[16] = {0xA, 0x5, 0xF, 0x6, 0x7, 0xC, 0x1, 0xB, 0x9, 0x2, 0x8, 0xD, 0x4, 0x3, 0xE, 0x0}; uint8_t checksum = 0; checksum ^= lookup_table[(device_id >> 28) & 0x0F]; checksum ^= (device_id >> 24) & 0x0F; checksum ^= lookup_table[(device_id >> 20) & 0x0F]; checksum ^= (device_id >> 16) & 0x0F; checksum ^= lookup_table[(device_id >> 12) & 0x0F]; checksum ^= (device_id >> 8) & 0x0F; checksum ^= lookup_table[(device_id >> 4) & 0x0F]; checksum ^= (device_id >> 0) & 0x0F; return (checksum == 0); } bool hdhomerun_discover_is_ip_multicast(uint32_t ip_addr) { return (ip_addr >= 0xE0000000) && (ip_addr < 0xF0000000); } libhdhomerun/hdhomerun_discover.h0000664000175000017500000000711113502777040016565 0ustar buildbuild/* * hdhomerun_discover.h * * Copyright © 2006-2019 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif struct hdhomerun_discover_device_t { uint32_t ip_addr; uint32_t device_type; uint32_t device_id; uint8_t tuner_count; bool is_legacy; char device_auth[25]; char base_url[29]; }; struct hdhomerun_discover_device_v3_t { uint32_t ip_addr; uint32_t device_type; uint32_t device_id; uint8_t tuner_count; bool is_legacy; char device_auth[25]; char base_url[29]; char storage_id[37]; char lineup_url[128]; char storage_url[128]; }; /* * Find devices. * * The device information is stored in caller-supplied array of hdhomerun_discover_device_t vars. * Multiple attempts are made to find devices. * Execution time is typically 400ms unless max_count is reached. * * Set target_ip to zero to auto-detect the IP address. * Set device_type to HDHOMERUN_DEVICE_TYPE_TUNER to detect HDHomeRun tuner devices. * Set device_id to HDHOMERUN_DEVICE_ID_WILDCARD to detect all device ids. * * Returns the number of devices found. * Retruns -1 on error. */ extern LIBHDHOMERUN_API int hdhomerun_discover_find_devices_custom_v2(uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_t result_list[], int max_count); extern LIBHDHOMERUN_API int hdhomerun_discover_find_devices_custom_v3(uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_v3_t result_list[], int max_count); /* * Optional: persistent discover instance available for discover polling use. */ extern LIBHDHOMERUN_API struct hdhomerun_discover_t *hdhomerun_discover_create(struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds); extern LIBHDHOMERUN_API int hdhomerun_discover_find_devices_v2(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_t result_list[], int max_count); extern LIBHDHOMERUN_API int hdhomerun_discover_find_devices_v3(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_v3_t result_list[], int max_count); /* * Verify that the device ID given is valid. * * The device ID contains a self-check sequence that detects common user input errors including * single-digit errors and two digit transposition errors. * * Returns true if valid. * Returns false if not valid. */ extern LIBHDHOMERUN_API bool hdhomerun_discover_validate_device_id(uint32_t device_id); /* * Detect if an IP address is multicast. * * Returns true if multicast. * Returns false if zero, unicast, expermental, or broadcast. */ extern LIBHDHOMERUN_API bool hdhomerun_discover_is_ip_multicast(uint32_t ip_addr); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_os.h0000664000175000017500000000165613063620312015367 0ustar buildbuild/* * hdhomerun_os.h * * Copyright © 2006-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if defined(_WIN32) #include "hdhomerun_os_windows.h" #else #include "hdhomerun_os_posix.h" #endif libhdhomerun/hdhomerun_os_posix.c0000664000175000017500000001304713117566021016607 0ustar buildbuild/* * hdhomerun_os_posix.c * * Copyright © 2006-2017 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun_os.h" #if defined(__APPLE__) #include #include static pthread_once_t clock_monotonic_once = PTHREAD_ONCE_INIT; static clock_serv_t clock_monotonic_clock_serv; static void clock_monotonic_init(void) { host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clock_monotonic_clock_serv); } static inline void clock_monotonic_timespec(struct timespec *ts) { pthread_once(&clock_monotonic_once, clock_monotonic_init); struct mach_timespec mt; clock_get_time(clock_monotonic_clock_serv, &mt); ts->tv_nsec = mt.tv_nsec; ts->tv_sec = mt.tv_sec; } static inline void clock_realtime_timespec(struct timespec *ts) { struct timeval tv; gettimeofday(&tv, NULL); ts->tv_nsec = tv.tv_usec * 1000; ts->tv_sec = tv.tv_sec; } #else static inline void clock_monotonic_timespec(struct timespec *ts) { clock_gettime(CLOCK_MONOTONIC, ts); } static inline void clock_realtime_timespec(struct timespec *ts) { clock_gettime(CLOCK_REALTIME, ts); } #endif static pthread_once_t random_get32_once = PTHREAD_ONCE_INIT; static FILE *random_get32_fp = NULL; static void random_get32_init(void) { random_get32_fp = fopen("/dev/urandom", "rb"); } uint32_t random_get32(void) { pthread_once(&random_get32_once, random_get32_init); if (!random_get32_fp) { return (uint32_t)getcurrenttime(); } uint32_t Result; if (fread(&Result, 4, 1, random_get32_fp) != 1) { return (uint32_t)getcurrenttime(); } return Result; } uint64_t getcurrenttime(void) { struct timespec ts; clock_monotonic_timespec(&ts); return ((uint64_t)ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); } void msleep_approx(uint64_t ms) { uint64_t delay_s = ms / 1000; if (delay_s > 0) { sleep((unsigned int)delay_s); ms -= delay_s * 1000; } uint64_t delay_us = ms * 1000; if (delay_us > 0) { usleep((useconds_t)delay_us); } } void msleep_minimum(uint64_t ms) { uint64_t stop_time = getcurrenttime() + ms; while (1) { uint64_t current_time = getcurrenttime(); if (current_time >= stop_time) { return; } msleep_approx(stop_time - current_time); } } struct thread_task_execute_args_t { thread_task_func_t func; void *arg; }; static void *thread_task_execute(void *arg) { struct thread_task_execute_args_t *execute_args = (struct thread_task_execute_args_t *)arg; execute_args->func(execute_args->arg); free(execute_args); return NULL; } bool thread_task_create(thread_task_t *tid, thread_task_func_t func, void *arg) { struct thread_task_execute_args_t *execute_args = (struct thread_task_execute_args_t *)malloc(sizeof(struct thread_task_execute_args_t)); if (!execute_args) { return false; } execute_args->func = func; execute_args->arg = arg; if (pthread_create(tid, NULL, thread_task_execute, execute_args) != 0) { free(execute_args); return false; } return true; } void thread_task_join(thread_task_t tid) { pthread_join(tid, NULL); } void thread_mutex_init(thread_mutex_t *mutex) { pthread_mutex_init(mutex, NULL); } void thread_mutex_dispose(pthread_mutex_t *mutex) { } void thread_mutex_lock(thread_mutex_t *mutex) { pthread_mutex_lock(mutex); } void thread_mutex_unlock(thread_mutex_t *mutex) { pthread_mutex_unlock(mutex); } void thread_cond_init(thread_cond_t *cond) { cond->signaled = false; pthread_mutex_init(&cond->lock, NULL); pthread_cond_init(&cond->cond, NULL); } void thread_cond_dispose(thread_cond_t *cond) { } void thread_cond_signal(thread_cond_t *cond) { pthread_mutex_lock(&cond->lock); cond->signaled = true; pthread_cond_signal(&cond->cond); pthread_mutex_unlock(&cond->lock); } void thread_cond_wait(thread_cond_t *cond) { pthread_mutex_lock(&cond->lock); if (!cond->signaled) { pthread_cond_wait(&cond->cond, &cond->lock); } cond->signaled = false; pthread_mutex_unlock(&cond->lock); } void thread_cond_wait_with_timeout(thread_cond_t *cond, uint64_t max_wait_time) { pthread_mutex_lock(&cond->lock); if (!cond->signaled) { struct timespec ts; clock_realtime_timespec(&ts); uint64_t tv_nsec = (uint64_t)ts.tv_nsec + (max_wait_time * 1000000); ts.tv_nsec = (long)(tv_nsec % 1000000000); ts.tv_sec += (time_t)(tv_nsec / 1000000000); pthread_cond_timedwait(&cond->cond, &cond->lock, &ts); } cond->signaled = false; pthread_mutex_unlock(&cond->lock); } bool hdhomerun_vsprintf(char *buffer, char *end, const char *fmt, va_list ap) { if (buffer >= end) { return false; } int length = vsnprintf(buffer, end - buffer - 1, fmt, ap); if (length < 0) { *buffer = 0; return false; } if (buffer + length + 1 > end) { *(end - 1) = 0; return false; } return true; } bool hdhomerun_sprintf(char *buffer, char *end, const char *fmt, ...) { va_list ap; va_start(ap, fmt); bool result = hdhomerun_vsprintf(buffer, end, fmt, ap); va_end(ap); return result; } libhdhomerun/hdhomerun_os_posix.h0000664000175000017500000000577713660540251016627 0ustar buildbuild/* * hdhomerun_os_posix.h * * Copyright © 2006-2017 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef void (*sig_t)(int); typedef void (*thread_task_func_t)(void *arg); typedef pthread_t thread_task_t; typedef pthread_mutex_t thread_mutex_t; typedef struct { volatile bool signaled; pthread_mutex_t lock; pthread_cond_t cond; } thread_cond_t; #define LIBHDHOMERUN_API #define LIBHDHOMERUN_PACKED(x) x __attribute__((packed)) #if !defined(alignas) && !defined(__cplusplus) #define alignas(n) __attribute__((aligned(n))) #endif #ifdef __cplusplus extern "C" { #endif extern LIBHDHOMERUN_API uint32_t random_get32(void); extern LIBHDHOMERUN_API uint64_t getcurrenttime(void); extern LIBHDHOMERUN_API void msleep_approx(uint64_t ms); extern LIBHDHOMERUN_API void msleep_minimum(uint64_t ms); extern LIBHDHOMERUN_API bool thread_task_create(thread_task_t *tid, thread_task_func_t func, void *arg); extern LIBHDHOMERUN_API void thread_task_join(thread_task_t tid); extern LIBHDHOMERUN_API void thread_mutex_init(thread_mutex_t *mutex); extern LIBHDHOMERUN_API void thread_mutex_dispose(thread_mutex_t *mutex); extern LIBHDHOMERUN_API void thread_mutex_lock(thread_mutex_t *mutex); extern LIBHDHOMERUN_API void thread_mutex_unlock(thread_mutex_t *mutex); extern LIBHDHOMERUN_API void thread_cond_init(thread_cond_t *cond); extern LIBHDHOMERUN_API void thread_cond_dispose(thread_cond_t *cond); extern LIBHDHOMERUN_API void thread_cond_signal(thread_cond_t *cond); extern LIBHDHOMERUN_API void thread_cond_wait(thread_cond_t *cond); extern LIBHDHOMERUN_API void thread_cond_wait_with_timeout(thread_cond_t *cond, uint64_t max_wait_time); extern LIBHDHOMERUN_API bool hdhomerun_vsprintf(char *buffer, char *end, const char *fmt, va_list ap); extern LIBHDHOMERUN_API bool hdhomerun_sprintf(char *buffer, char *end, const char *fmt, ...); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_os_windows.c0000664000175000017500000001017013577170733017144 0ustar buildbuild/* * hdhomerun_os_windows.c * * Copyright © 2006-2017 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" #if defined(_WINRT) uint32_t random_get32(void) { return (uint32_t)getcurrenttime(); } #else uint32_t random_get32(void) { static DWORD random_get32_context_tls = 0xFFFFFFFF; if (random_get32_context_tls == 0xFFFFFFFF) { random_get32_context_tls = TlsAlloc(); } HCRYPTPROV *phProv = (HCRYPTPROV *)TlsGetValue(random_get32_context_tls); if (!phProv) { phProv = (HCRYPTPROV *)calloc(1, sizeof(HCRYPTPROV)); CryptAcquireContext(phProv, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); TlsSetValue(random_get32_context_tls, phProv); } uint32_t Result; if (!CryptGenRandom(*phProv, sizeof(Result), (BYTE *)&Result)) { return (uint32_t)getcurrenttime(); } return Result; } #endif uint64_t getcurrenttime(void) { return GetTickCount64(); } void msleep_approx(uint64_t ms) { Sleep((DWORD)ms); } void msleep_minimum(uint64_t ms) { uint64_t stop_time = getcurrenttime() + ms; while (1) { uint64_t current_time = getcurrenttime(); if (current_time >= stop_time) { return; } msleep_approx(stop_time - current_time); } } struct thread_task_execute_args_t { thread_task_func_t func; void *arg; }; static DWORD WINAPI thread_task_execute(void *arg) { struct thread_task_execute_args_t *execute_args = (struct thread_task_execute_args_t *)arg; execute_args->func(execute_args->arg); free(execute_args); return 0; } bool thread_task_create(thread_task_t *tid, thread_task_func_t func, void *arg) { struct thread_task_execute_args_t *execute_args = (struct thread_task_execute_args_t *)malloc(sizeof(struct thread_task_execute_args_t)); if (!execute_args) { return false; } execute_args->func = func; execute_args->arg = arg; *tid = CreateThread(NULL, 0, thread_task_execute, execute_args, 0, NULL); if (!*tid) { free(execute_args); return false; } return true; } void thread_task_join(thread_task_t tid) { while (1) { DWORD ExitCode = 0; if (!GetExitCodeThread(tid, &ExitCode)) { return; } if (ExitCode != STILL_ACTIVE) { CloseHandle(tid); return; } } } void thread_mutex_init(thread_mutex_t *mutex) { *mutex = CreateMutex(NULL, false, NULL); } void thread_mutex_dispose(thread_mutex_t *mutex) { CloseHandle(*mutex); } void thread_mutex_lock(thread_mutex_t *mutex) { WaitForSingleObject(*mutex, INFINITE); } void thread_mutex_unlock(thread_mutex_t *mutex) { ReleaseMutex(*mutex); } void thread_cond_init(thread_cond_t *cond) { *cond = CreateEvent(NULL, false, false, NULL); } void thread_cond_dispose(thread_cond_t *cond) { CloseHandle(*cond); } void thread_cond_signal(thread_cond_t *cond) { SetEvent(*cond); } void thread_cond_wait(thread_cond_t *cond) { WaitForSingleObject(*cond, INFINITE); } void thread_cond_wait_with_timeout(thread_cond_t *cond, uint64_t max_wait_time) { WaitForSingleObject(*cond, (DWORD)max_wait_time); } bool hdhomerun_vsprintf(char *buffer, char *end, const char *fmt, va_list ap) { if (buffer >= end) { return false; } int length = _vsnprintf(buffer, end - buffer - 1, fmt, ap); if (length < 0) { *buffer = 0; return false; } if (buffer + length + 1 > end) { *(end - 1) = 0; return false; } return true; } bool hdhomerun_sprintf(char *buffer, char *end, const char *fmt, ...) { va_list ap; va_start(ap, fmt); bool result = hdhomerun_vsprintf(buffer, end, fmt, ap); va_end(ap); return result; } libhdhomerun/hdhomerun_os_windows.h0000664000175000017500000000701113660540251017136 0ustar buildbuild/* * hdhomerun_os_windows.h * * Copyright © 2006-2017 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef _WINRT #include #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT _WIN32_WINNT_VISTA #endif #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS #define _WINSOCK_DEPRECATED_NO_WARNINGS #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LIBHDHOMERUN_DLLEXPORT #define LIBHDHOMERUN_API __declspec(dllexport) #endif #ifdef LIBHDHOMERUN_DLLIMPORT #define LIBHDHOMERUN_API __declspec(dllimport) #endif #ifndef LIBHDHOMERUN_API #define LIBHDHOMERUN_API #endif #define LIBHDHOMERUN_PACKED(x) __pragma(pack(push, 1)) x __pragma( pack(pop)) #if !defined(__unused) #define __unused __pragma(warning(suppress: 4100 4101)) #endif #if !defined(alignas) && !defined(__cplusplus) #define alignas(n) __declspec(align(n)) #endif typedef void (*sig_t)(int); typedef void (*thread_task_func_t)(void *arg); typedef HANDLE thread_task_t; typedef HANDLE thread_mutex_t; typedef HANDLE thread_cond_t; #if !defined(va_copy) #define va_copy(x, y) x = y #endif #define atoll _atoi64 #define strdup _strdup #define strcasecmp _stricmp #define strncasecmp _strnicmp #define fseeko _fseeki64 #define ftello _ftelli64 #ifdef __cplusplus extern "C" { #endif extern LIBHDHOMERUN_API uint32_t random_get32(void); extern LIBHDHOMERUN_API uint64_t getcurrenttime(void); extern LIBHDHOMERUN_API void msleep_approx(uint64_t ms); extern LIBHDHOMERUN_API void msleep_minimum(uint64_t ms); extern LIBHDHOMERUN_API bool thread_task_create(thread_task_t *tid, thread_task_func_t func, void *arg); extern LIBHDHOMERUN_API void thread_task_join(thread_task_t tid); extern LIBHDHOMERUN_API void thread_mutex_init(thread_mutex_t *mutex); extern LIBHDHOMERUN_API void thread_mutex_dispose(thread_mutex_t *mutex); extern LIBHDHOMERUN_API void thread_mutex_lock(thread_mutex_t *mutex); extern LIBHDHOMERUN_API void thread_mutex_unlock(thread_mutex_t *mutex); extern LIBHDHOMERUN_API void thread_cond_init(thread_cond_t *cond); extern LIBHDHOMERUN_API void thread_cond_dispose(thread_cond_t *cond); extern LIBHDHOMERUN_API void thread_cond_signal(thread_cond_t *cond); extern LIBHDHOMERUN_API void thread_cond_wait(thread_cond_t *cond); extern LIBHDHOMERUN_API void thread_cond_wait_with_timeout(thread_cond_t *cond, uint64_t max_wait_time); extern LIBHDHOMERUN_API bool hdhomerun_vsprintf(char *buffer, char *end, const char *fmt, va_list ap); extern LIBHDHOMERUN_API bool hdhomerun_sprintf(char *buffer, char *end, const char *fmt, ...); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_pkt.c0000664000175000017500000001253413013646353015544 0ustar buildbuild/* * hdhomerun_pkt.c * * Copyright © 2006-2014 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" struct hdhomerun_pkt_t *hdhomerun_pkt_create(void) { struct hdhomerun_pkt_t *pkt = (struct hdhomerun_pkt_t *)calloc(1, sizeof(struct hdhomerun_pkt_t)); if (!pkt) { return NULL; } hdhomerun_pkt_reset(pkt); return pkt; } void hdhomerun_pkt_destroy(struct hdhomerun_pkt_t *pkt) { free(pkt); } void hdhomerun_pkt_reset(struct hdhomerun_pkt_t *pkt) { pkt->limit = pkt->buffer + sizeof(pkt->buffer) - 4; pkt->start = pkt->buffer + 1024; pkt->end = pkt->start; pkt->pos = pkt->start; } static uint32_t hdhomerun_pkt_calc_crc(uint8_t *start, uint8_t *end) { uint8_t *pos = start; uint32_t crc = 0xFFFFFFFF; while (pos < end) { uint8_t x = (uint8_t)(crc) ^ *pos++; crc >>= 8; if (x & 0x01) crc ^= 0x77073096; if (x & 0x02) crc ^= 0xEE0E612C; if (x & 0x04) crc ^= 0x076DC419; if (x & 0x08) crc ^= 0x0EDB8832; if (x & 0x10) crc ^= 0x1DB71064; if (x & 0x20) crc ^= 0x3B6E20C8; if (x & 0x40) crc ^= 0x76DC4190; if (x & 0x80) crc ^= 0xEDB88320; } return crc ^ 0xFFFFFFFF; } uint8_t hdhomerun_pkt_read_u8(struct hdhomerun_pkt_t *pkt) { uint8_t v = *pkt->pos++; return v; } uint16_t hdhomerun_pkt_read_u16(struct hdhomerun_pkt_t *pkt) { uint16_t v; v = (uint16_t)*pkt->pos++ << 8; v |= (uint16_t)*pkt->pos++ << 0; return v; } uint32_t hdhomerun_pkt_read_u32(struct hdhomerun_pkt_t *pkt) { uint32_t v; v = (uint32_t)*pkt->pos++ << 24; v |= (uint32_t)*pkt->pos++ << 16; v |= (uint32_t)*pkt->pos++ << 8; v |= (uint32_t)*pkt->pos++ << 0; return v; } size_t hdhomerun_pkt_read_var_length(struct hdhomerun_pkt_t *pkt) { size_t length; if (pkt->pos + 1 > pkt->end) { return (size_t)-1; } length = (size_t)*pkt->pos++; if (length & 0x0080) { if (pkt->pos + 1 > pkt->end) { return (size_t)-1; } length &= 0x007F; length |= (size_t)*pkt->pos++ << 7; } return length; } uint8_t *hdhomerun_pkt_read_tlv(struct hdhomerun_pkt_t *pkt, uint8_t *ptag, size_t *plength) { if (pkt->pos + 2 > pkt->end) { return NULL; } *ptag = hdhomerun_pkt_read_u8(pkt); *plength = hdhomerun_pkt_read_var_length(pkt); if (pkt->pos + *plength > pkt->end) { return NULL; } return pkt->pos + *plength; } void hdhomerun_pkt_read_mem(struct hdhomerun_pkt_t *pkt, void *mem, size_t length) { memcpy(mem, pkt->pos, length); pkt->pos += length; } void hdhomerun_pkt_write_u8(struct hdhomerun_pkt_t *pkt, uint8_t v) { *pkt->pos++ = v; if (pkt->pos > pkt->end) { pkt->end = pkt->pos; } } void hdhomerun_pkt_write_u16(struct hdhomerun_pkt_t *pkt, uint16_t v) { *pkt->pos++ = (uint8_t)(v >> 8); *pkt->pos++ = (uint8_t)(v >> 0); if (pkt->pos > pkt->end) { pkt->end = pkt->pos; } } void hdhomerun_pkt_write_u32(struct hdhomerun_pkt_t *pkt, uint32_t v) { *pkt->pos++ = (uint8_t)(v >> 24); *pkt->pos++ = (uint8_t)(v >> 16); *pkt->pos++ = (uint8_t)(v >> 8); *pkt->pos++ = (uint8_t)(v >> 0); if (pkt->pos > pkt->end) { pkt->end = pkt->pos; } } void hdhomerun_pkt_write_var_length(struct hdhomerun_pkt_t *pkt, size_t v) { if (v <= 127) { *pkt->pos++ = (uint8_t)v; } else { *pkt->pos++ = (uint8_t)(v | 0x80); *pkt->pos++ = (uint8_t)(v >> 7); } if (pkt->pos > pkt->end) { pkt->end = pkt->pos; } } void hdhomerun_pkt_write_mem(struct hdhomerun_pkt_t *pkt, const void *mem, size_t length) { memcpy(pkt->pos, mem, length); pkt->pos += length; if (pkt->pos > pkt->end) { pkt->end = pkt->pos; } } int hdhomerun_pkt_open_frame(struct hdhomerun_pkt_t *pkt, uint16_t *ptype) { pkt->pos = pkt->start; if (pkt->pos + 4 > pkt->end) { return 0; } *ptype = hdhomerun_pkt_read_u16(pkt); size_t length = hdhomerun_pkt_read_u16(pkt); pkt->pos += length; if (pkt->pos + 4 > pkt->end) { pkt->pos = pkt->start; return 0; } uint32_t calc_crc = hdhomerun_pkt_calc_crc(pkt->start, pkt->pos); uint32_t packet_crc; packet_crc = (uint32_t)*pkt->pos++ << 0; packet_crc |= (uint32_t)*pkt->pos++ << 8; packet_crc |= (uint32_t)*pkt->pos++ << 16; packet_crc |= (uint32_t)*pkt->pos++ << 24; if (calc_crc != packet_crc) { return -1; } pkt->start += 4; pkt->end = pkt->start + length; pkt->pos = pkt->start; return 1; } void hdhomerun_pkt_seal_frame(struct hdhomerun_pkt_t *pkt, uint16_t frame_type) { size_t length = pkt->end - pkt->start; pkt->start -= 4; pkt->pos = pkt->start; hdhomerun_pkt_write_u16(pkt, frame_type); hdhomerun_pkt_write_u16(pkt, (uint16_t)length); uint32_t crc = hdhomerun_pkt_calc_crc(pkt->start, pkt->end); *pkt->end++ = (uint8_t)(crc >> 0); *pkt->end++ = (uint8_t)(crc >> 8); *pkt->end++ = (uint8_t)(crc >> 16); *pkt->end++ = (uint8_t)(crc >> 24); pkt->pos = pkt->start; } libhdhomerun/hdhomerun_pkt.h0000664000175000017500000001756613502777040015564 0ustar buildbuild/* * hdhomerun_pkt.h * * Copyright © 2006-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif /* * The discover protocol (UDP port 65001) and control protocol (TCP port 65001) * both use the same packet based format: * uint16_t Packet type * uint16_t Payload length (bytes) * uint8_t[] Payload data (0-n bytes). * uint32_t CRC (Ethernet style 32-bit CRC) * * All variables are big-endian except for the crc which is little-endian. * * Valid values for the packet type are listed below as defines prefixed * with "HDHOMERUN_TYPE_" * * Discovery: * * The payload for a discovery request or reply is a simple sequence of * tag-length-value data: * uint8_t Tag * varlen Length * uint8_t[] Value (0-n bytes) * * The length field can be one or two bytes long. * For a length <= 127 bytes the length is expressed as a single byte. The * most-significant-bit is clear indicating a single-byte length. * For a length >= 128 bytes the length is expressed as a sequence of two bytes as follows: * The first byte is contains the least-significant 7-bits of the length. The * most-significant bit is then set (add 0x80) to indicate that it is a two byte length. * The second byte contains the length shifted down 7 bits. * * A discovery request packet has a packet type of HDHOMERUN_TYPE_DISCOVER_REQ and should * contain two tags: HDHOMERUN_TAG_DEVICE_TYPE and HDHOMERUN_TAG_DEVICE_ID. * The HDHOMERUN_TAG_DEVICE_TYPE value should be set to HDHOMERUN_DEVICE_TYPE_TUNER. * The HDHOMERUN_TAG_DEVICE_ID value should be set to HDHOMERUN_DEVICE_ID_WILDCARD to match * all devices, or to the 32-bit device id number to match a single device. * * The discovery response packet has a packet type of HDHOMERUN_TYPE_DISCOVER_RPY and has the * same format as the discovery request packet with the two tags: HDHOMERUN_TAG_DEVICE_TYPE and * HDHOMERUN_TAG_DEVICE_ID. In the future additional tags may also be returned - unknown tags * should be skipped and not treated as an error. * * Control get/set: * * The payload for a control get/set request is a simple sequence of tag-length-value data * following the same format as for discover packets. * * A get request packet has a packet type of HDHOMERUN_TYPE_GETSET_REQ and should contain * the tag: HDHOMERUN_TAG_GETSET_NAME. The HDHOMERUN_TAG_GETSET_NAME value should be a sequence * of bytes forming a null-terminated string, including the NULL. The TLV length must include * the NULL character so the length field should be set to strlen(str) + 1. * * A set request packet has a packet type of HDHOMERUN_TYPE_GETSET_REQ (same as a get request) * and should contain two tags: HDHOMERUN_TAG_GETSET_NAME and HDHOMERUN_TAG_GETSET_VALUE. * The HDHOMERUN_TAG_GETSET_NAME value should be a sequence of bytes forming a null-terminated * string, including the NULL. * The HDHOMERUN_TAG_GETSET_VALUE value should be a sequence of bytes forming a null-terminated * string, including the NULL. * * The get and set reply packets have the packet type HDHOMERUN_TYPE_GETSET_RPY and have the same * format as the set request packet with the two tags: HDHOMERUN_TAG_GETSET_NAME and * HDHOMERUN_TAG_GETSET_VALUE. A set request is also implicit get request so the updated value is * returned. * * If the device encounters an error handling the get or set request then the get/set reply packet * will contain the tag HDHOMERUN_TAG_ERROR_MESSAGE. The format of the value is a sequence of * bytes forming a null-terminated string, including the NULL. * * In the future additional tags may also be returned - unknown tags should be skipped and not * treated as an error. * * Security note: The application should not rely on the NULL character being present. The * application should write a NULL character based on the TLV length to protect the application * from a potential attack. * * Firmware Upgrade: * * A firmware upgrade packet has a packet type of HDHOMERUN_TYPE_UPGRADE_REQ and has a fixed format: * uint32_t Position in bytes from start of file. * uint8_t[256] Firmware data (256 bytes) * * The data must be uploaded in 256 byte chunks and must be uploaded in order. * The position number is in bytes so will increment by 256 each time. * * When all data is uploaded it should be signaled complete by sending another packet of type * HDHOMERUN_TYPE_UPGRADE_REQ with payload of a single uint32_t with the value 0xFFFFFFFF. */ #define HDHOMERUN_DISCOVER_UDP_PORT 65001 #define HDHOMERUN_CONTROL_TCP_PORT 65001 #define HDHOMERUN_MAX_PACKET_SIZE 1460 #define HDHOMERUN_MAX_PAYLOAD_SIZE 1452 #define HDHOMERUN_TYPE_DISCOVER_REQ 0x0002 #define HDHOMERUN_TYPE_DISCOVER_RPY 0x0003 #define HDHOMERUN_TYPE_GETSET_REQ 0x0004 #define HDHOMERUN_TYPE_GETSET_RPY 0x0005 #define HDHOMERUN_TYPE_UPGRADE_REQ 0x0006 #define HDHOMERUN_TYPE_UPGRADE_RPY 0x0007 #define HDHOMERUN_TAG_DEVICE_TYPE 0x01 #define HDHOMERUN_TAG_DEVICE_ID 0x02 #define HDHOMERUN_TAG_GETSET_NAME 0x03 #define HDHOMERUN_TAG_GETSET_VALUE 0x04 #define HDHOMERUN_TAG_GETSET_LOCKKEY 0x15 #define HDHOMERUN_TAG_ERROR_MESSAGE 0x05 #define HDHOMERUN_TAG_TUNER_COUNT 0x10 #define HDHOMERUN_TAG_LINEUP_URL 0x27 #define HDHOMERUN_TAG_STORAGE_URL 0x28 #define HDHOMERUN_TAG_DEVICE_AUTH_BIN_DEPRECATED 0x29 #define HDHOMERUN_TAG_BASE_URL 0x2A #define HDHOMERUN_TAG_DEVICE_AUTH_STR 0x2B #define HDHOMERUN_TAG_STORAGE_ID 0x2C #define HDHOMERUN_DEVICE_TYPE_WILDCARD 0xFFFFFFFF #define HDHOMERUN_DEVICE_TYPE_TUNER 0x00000001 #define HDHOMERUN_DEVICE_TYPE_STORAGE 0x00000005 #define HDHOMERUN_DEVICE_ID_WILDCARD 0xFFFFFFFF #define HDHOMERUN_MIN_PEEK_LENGTH 4 struct hdhomerun_pkt_t { uint8_t *pos; uint8_t *start; uint8_t *end; uint8_t *limit; uint8_t buffer[3074]; }; extern LIBHDHOMERUN_API struct hdhomerun_pkt_t *hdhomerun_pkt_create(void); extern LIBHDHOMERUN_API void hdhomerun_pkt_destroy(struct hdhomerun_pkt_t *pkt); extern LIBHDHOMERUN_API void hdhomerun_pkt_reset(struct hdhomerun_pkt_t *pkt); extern LIBHDHOMERUN_API uint8_t hdhomerun_pkt_read_u8(struct hdhomerun_pkt_t *pkt); extern LIBHDHOMERUN_API uint16_t hdhomerun_pkt_read_u16(struct hdhomerun_pkt_t *pkt); extern LIBHDHOMERUN_API uint32_t hdhomerun_pkt_read_u32(struct hdhomerun_pkt_t *pkt); extern LIBHDHOMERUN_API size_t hdhomerun_pkt_read_var_length(struct hdhomerun_pkt_t *pkt); extern LIBHDHOMERUN_API uint8_t *hdhomerun_pkt_read_tlv(struct hdhomerun_pkt_t *pkt, uint8_t *ptag, size_t *plength); extern LIBHDHOMERUN_API void hdhomerun_pkt_read_mem(struct hdhomerun_pkt_t *pkt, void *mem, size_t length); extern LIBHDHOMERUN_API void hdhomerun_pkt_write_u8(struct hdhomerun_pkt_t *pkt, uint8_t v); extern LIBHDHOMERUN_API void hdhomerun_pkt_write_u16(struct hdhomerun_pkt_t *pkt, uint16_t v); extern LIBHDHOMERUN_API void hdhomerun_pkt_write_u32(struct hdhomerun_pkt_t *pkt, uint32_t v); extern LIBHDHOMERUN_API void hdhomerun_pkt_write_var_length(struct hdhomerun_pkt_t *pkt, size_t v); extern LIBHDHOMERUN_API void hdhomerun_pkt_write_mem(struct hdhomerun_pkt_t *pkt, const void *mem, size_t length); extern LIBHDHOMERUN_API int hdhomerun_pkt_open_frame(struct hdhomerun_pkt_t *pkt, uint16_t *ptype); extern LIBHDHOMERUN_API void hdhomerun_pkt_seal_frame(struct hdhomerun_pkt_t *pkt, uint16_t frame_type); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_sock.h0000664000175000017500000000672213063620312015704 0ustar buildbuild/* * hdhomerun_sock.h * * Copyright © 2010-2016 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif struct hdhomerun_local_ip_info_t { uint32_t ip_addr; uint32_t subnet_mask; }; extern LIBHDHOMERUN_API int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count); extern LIBHDHOMERUN_API void hdhomerun_local_ip_info_set_str(const char *ip_info_str); /* WinRT only */ struct hdhomerun_sock_t; extern LIBHDHOMERUN_API struct hdhomerun_sock_t *hdhomerun_sock_create_udp(void); extern LIBHDHOMERUN_API struct hdhomerun_sock_t *hdhomerun_sock_create_tcp(void); extern LIBHDHOMERUN_API void hdhomerun_sock_stop(struct hdhomerun_sock_t *sock); extern LIBHDHOMERUN_API void hdhomerun_sock_destroy(struct hdhomerun_sock_t *sock); extern LIBHDHOMERUN_API void hdhomerun_sock_set_send_buffer_size(struct hdhomerun_sock_t *sock, size_t size); extern LIBHDHOMERUN_API void hdhomerun_sock_set_recv_buffer_size(struct hdhomerun_sock_t *sock, size_t size); extern LIBHDHOMERUN_API void hdhomerun_sock_set_allow_reuse(struct hdhomerun_sock_t *sock); extern LIBHDHOMERUN_API int hdhomerun_sock_getlasterror(void); extern LIBHDHOMERUN_API uint32_t hdhomerun_sock_getsockname_addr(struct hdhomerun_sock_t *sock); extern LIBHDHOMERUN_API uint16_t hdhomerun_sock_getsockname_port(struct hdhomerun_sock_t *sock); extern LIBHDHOMERUN_API uint32_t hdhomerun_sock_getpeername_addr(struct hdhomerun_sock_t *sock); extern LIBHDHOMERUN_API uint32_t hdhomerun_sock_getaddrinfo_addr(struct hdhomerun_sock_t *sock, const char *name); extern LIBHDHOMERUN_API bool hdhomerun_sock_join_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip); extern LIBHDHOMERUN_API bool hdhomerun_sock_leave_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip); extern LIBHDHOMERUN_API bool hdhomerun_sock_bind(struct hdhomerun_sock_t *sock, uint32_t local_addr, uint16_t local_port, bool allow_reuse); extern LIBHDHOMERUN_API bool hdhomerun_sock_connect(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout); extern LIBHDHOMERUN_API bool hdhomerun_sock_send(struct hdhomerun_sock_t *sock, const void *data, size_t length, uint64_t timeout); extern LIBHDHOMERUN_API bool hdhomerun_sock_sendto(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout); extern LIBHDHOMERUN_API bool hdhomerun_sock_recv(struct hdhomerun_sock_t *sock, void *data, size_t *length, uint64_t timeout); extern LIBHDHOMERUN_API bool hdhomerun_sock_recvfrom(struct hdhomerun_sock_t *sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout); #ifdef __cplusplus } #endif libhdhomerun/hdhomerun_sock_posix.c0000664000175000017500000003330113500005347017113 0ustar buildbuild/* * hdhomerun_sock_posix.c * * Copyright © 2010-2019 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" #if defined(LIBHDHOMERUN_USE_SIOCGIFCONF) #include #else #include #endif #include #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif struct hdhomerun_sock_t { int sock; }; #if defined(LIBHDHOMERUN_USE_SIOCGIFCONF) int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count) { int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock == -1) { return -1; } int ifreq_buffer_size = 128 * sizeof(struct ifreq); char *ifreq_buffer = (char *)calloc(ifreq_buffer_size, 1); if (!ifreq_buffer) { close(sock); return -1; } struct ifconf ifc; ifc.ifc_len = ifreq_buffer_size; ifc.ifc_buf = ifreq_buffer; if (ioctl(sock, SIOCGIFCONF, &ifc) != 0) { free(ifreq_buffer); close(sock); return -1; } if (ifc.ifc_len > ifreq_buffer_size) { ifc.ifc_len = ifreq_buffer_size; } struct hdhomerun_local_ip_info_t *ip_info = ip_info_list; int count = 0; char *ptr = ifc.ifc_buf; char *end = ifc.ifc_buf + ifc.ifc_len; while (ptr + sizeof(struct ifreq) <= end) { struct ifreq *ifr = (struct ifreq *)ptr; ptr += sizeof(struct ifreq); /* Local IP address. */ struct sockaddr_in *ip_addr_in = (struct sockaddr_in *)&ifr->ifr_addr; uint32_t ip_addr = ntohl(ip_addr_in->sin_addr.s_addr); if (ip_addr == 0) { continue; } /* Flags. */ if (ioctl(sock, SIOCGIFFLAGS, ifr) != 0) { continue; } unsigned int flags = ifr->ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_UP | IFF_RUNNING); if (flags != (IFF_UP | IFF_RUNNING)) { continue; } /* Subnet mask. */ if (ioctl(sock, SIOCGIFNETMASK, ifr) != 0) { continue; } struct sockaddr_in *subnet_mask_in = (struct sockaddr_in *)&ifr->ifr_addr; uint32_t subnet_mask = ntohl(subnet_mask_in->sin_addr.s_addr); /* Result. */ if (count < max_count) { ip_info->ip_addr = ip_addr; ip_info->subnet_mask = subnet_mask; ip_info++; } count++; } free(ifreq_buffer); close(sock); return count; } #else int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count) { struct ifaddrs *ifaddrs; if (getifaddrs(&ifaddrs) != 0) { return -1; } struct hdhomerun_local_ip_info_t *ip_info = ip_info_list; struct ifaddrs *ifa = ifaddrs; int count = 0; while (ifa) { if (ifa->ifa_addr == NULL) { ifa = ifa->ifa_next; continue; } if (ifa->ifa_addr->sa_family != AF_INET) { ifa = ifa->ifa_next; continue; } unsigned int flags = ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_UP | IFF_RUNNING); if (flags != (IFF_UP | IFF_RUNNING)) { ifa = ifa->ifa_next; continue; } struct sockaddr_in *addr_in = (struct sockaddr_in *)ifa->ifa_addr; uint32_t ip_addr = ntohl(addr_in->sin_addr.s_addr); struct sockaddr_in *netmask_in = (struct sockaddr_in *)ifa->ifa_netmask; uint32_t subnet_mask = ntohl(netmask_in->sin_addr.s_addr); ifa = ifa->ifa_next; if (count < max_count) { ip_info->ip_addr = ip_addr; ip_info->subnet_mask = subnet_mask; ip_info++; } count++; } freeifaddrs(ifaddrs); return count; } #endif static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int protocol) { struct hdhomerun_sock_t *sock = (struct hdhomerun_sock_t *)calloc(1, sizeof(struct hdhomerun_sock_t)); if (!sock) { return NULL; } /* Create socket. */ sock->sock = socket(AF_INET, protocol, 0); if (sock->sock == -1) { free(sock); return NULL; } /* Set non-blocking */ if (fcntl(sock->sock, F_SETFL, O_NONBLOCK) != 0) { hdhomerun_sock_destroy(sock); return NULL; } /* Configure socket not to generate pipe-error signal (BSD/OSX). */ #if defined(SO_NOSIGPIPE) int set = 1; setsockopt(sock->sock, SOL_SOCKET, SO_NOSIGPIPE, (char *)&set, sizeof(set)); #endif /* Success. */ return sock; } struct hdhomerun_sock_t *hdhomerun_sock_create_udp(void) { struct hdhomerun_sock_t *sock = hdhomerun_sock_create_internal(SOCK_DGRAM); if (!sock) { return NULL; } /* Allow broadcast. */ int sock_opt = 1; setsockopt(sock->sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt)); /* Success. */ return sock; } struct hdhomerun_sock_t *hdhomerun_sock_create_tcp(void) { return hdhomerun_sock_create_internal(SOCK_STREAM); } void hdhomerun_sock_destroy(struct hdhomerun_sock_t *sock) { close(sock->sock); free(sock); } void hdhomerun_sock_stop(struct hdhomerun_sock_t *sock) { shutdown(sock->sock, SHUT_RDWR); } void hdhomerun_sock_set_send_buffer_size(struct hdhomerun_sock_t *sock, size_t size) { int size_opt = (int)size; setsockopt(sock->sock, SOL_SOCKET, SO_SNDBUF, (char *)&size_opt, sizeof(size_opt)); } void hdhomerun_sock_set_recv_buffer_size(struct hdhomerun_sock_t *sock, size_t size) { int size_opt = (int)size; setsockopt(sock->sock, SOL_SOCKET, SO_RCVBUF, (char *)&size_opt, sizeof(size_opt)); } void hdhomerun_sock_set_allow_reuse(struct hdhomerun_sock_t *sock) { int sock_opt = 1; setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt)); } int hdhomerun_sock_getlasterror(void) { return errno; } uint32_t hdhomerun_sock_getsockname_addr(struct hdhomerun_sock_t *sock) { struct sockaddr_in sock_addr; socklen_t sockaddr_size = sizeof(sock_addr); if (getsockname(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) { return 0; } return ntohl(sock_addr.sin_addr.s_addr); } uint16_t hdhomerun_sock_getsockname_port(struct hdhomerun_sock_t *sock) { struct sockaddr_in sock_addr; socklen_t sockaddr_size = sizeof(sock_addr); if (getsockname(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) { return 0; } return ntohs(sock_addr.sin_port); } uint32_t hdhomerun_sock_getpeername_addr(struct hdhomerun_sock_t *sock) { struct sockaddr_in sock_addr; socklen_t sockaddr_size = sizeof(sock_addr); if (getpeername(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) { return 0; } return ntohl(sock_addr.sin_addr.s_addr); } uint32_t hdhomerun_sock_getaddrinfo_addr(struct hdhomerun_sock_t *sock, const char *name) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; struct addrinfo *sock_info; if (getaddrinfo(name, NULL, &hints, &sock_info) != 0) { return 0; } struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr; uint32_t addr = ntohl(sock_addr->sin_addr.s_addr); freeaddrinfo(sock_info); return addr; } bool hdhomerun_sock_join_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip) { struct ip_mreq imr; memset(&imr, 0, sizeof(imr)); imr.imr_multiaddr.s_addr = htonl(multicast_ip); imr.imr_interface.s_addr = htonl(local_ip); if (setsockopt(sock->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) { return false; } return true; } bool hdhomerun_sock_leave_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip) { struct ip_mreq imr; memset(&imr, 0, sizeof(imr)); imr.imr_multiaddr.s_addr = htonl(multicast_ip); imr.imr_interface.s_addr = htonl(local_ip); if (setsockopt(sock->sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) { return false; } return true; } bool hdhomerun_sock_bind(struct hdhomerun_sock_t *sock, uint32_t local_addr, uint16_t local_port, bool allow_reuse) { int sock_opt = allow_reuse; setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt)); struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(local_addr); sock_addr.sin_port = htons(local_port); if (bind(sock->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) { return false; } return true; } bool hdhomerun_sock_connect(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout) { struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(remote_addr); sock_addr.sin_port = htons(remote_port); if (connect(sock->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) { if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { return false; } } struct pollfd poll_event; poll_event.fd = sock->sock; poll_event.events = POLLOUT; poll_event.revents = 0; if (poll(&poll_event, 1, (int)timeout) <= 0) { return false; } if ((poll_event.revents & POLLOUT) == 0) { return false; } return true; } bool hdhomerun_sock_send(struct hdhomerun_sock_t *sock, const void *data, size_t length, uint64_t timeout) { const uint8_t *ptr = (const uint8_t *)data; ssize_t ret = send(sock->sock, ptr, length, MSG_NOSIGNAL); if (ret >= (ssize_t)length) { return true; } if ((ret < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { return false; } if (ret > 0) { ptr += ret; length -= ret; } uint64_t stop_time = getcurrenttime() + timeout; while (1) { struct pollfd poll_event; poll_event.fd = sock->sock; poll_event.events = POLLOUT; poll_event.revents = 0; if (poll(&poll_event, 1, (int)timeout) <= 0) { return false; } if ((poll_event.revents & POLLOUT) == 0) { return false; } ret = send(sock->sock, ptr, length, MSG_NOSIGNAL); if (ret >= (ssize_t)length) { return true; } if ((ret < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { return false; } if (ret > 0) { ptr += ret; length -= ret; } uint64_t current_time = getcurrenttime(); if (current_time >= stop_time) { return false; } timeout = stop_time - current_time; } } bool hdhomerun_sock_sendto(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout) { struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(remote_addr); sock_addr.sin_port = htons(remote_port); const uint8_t *ptr = (const uint8_t *)data; ssize_t ret = sendto(sock->sock, ptr, length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr)); if (ret >= (ssize_t)length) { return true; } if ((ret < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { return false; } if (ret > 0) { ptr += ret; length -= ret; } uint64_t stop_time = getcurrenttime() + timeout; while (1) { struct pollfd poll_event; poll_event.fd = sock->sock; poll_event.events = POLLOUT; poll_event.revents = 0; if (poll(&poll_event, 1, (int)timeout) <= 0) { return false; } if ((poll_event.revents & POLLOUT) == 0) { return false; } ret = sendto(sock->sock, ptr, length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr)); if (ret >= (ssize_t)length) { return true; } if ((ret < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { return false; } if (ret > 0) { ptr += ret; length -= ret; } uint64_t current_time = getcurrenttime(); if (current_time >= stop_time) { return false; } timeout = stop_time - current_time; } } bool hdhomerun_sock_recv(struct hdhomerun_sock_t *sock, void *data, size_t *length, uint64_t timeout) { ssize_t ret = recv(sock->sock, data, *length, 0); if (ret > 0) { *length = (size_t)ret; return true; } if (ret == 0) { return false; } if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { return false; } struct pollfd poll_event; poll_event.fd = sock->sock; poll_event.events = POLLIN; poll_event.revents = 0; if (poll(&poll_event, 1, (int)timeout) <= 0) { return false; } if ((poll_event.revents & POLLIN) == 0) { return false; } ret = recv(sock->sock, data, *length, 0); if (ret > 0) { *length = (size_t)ret; return true; } return false; } bool hdhomerun_sock_recvfrom(struct hdhomerun_sock_t *sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout) { struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); socklen_t sockaddr_size = sizeof(sock_addr); ssize_t ret = recvfrom(sock->sock, data, *length, 0, (struct sockaddr *)&sock_addr, &sockaddr_size); if (ret > 0) { *remote_addr = ntohl(sock_addr.sin_addr.s_addr); *remote_port = ntohs(sock_addr.sin_port); *length = (size_t)ret; return true; } if (ret == 0) { return false; } if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { return false; } struct pollfd poll_event; poll_event.fd = sock->sock; poll_event.events = POLLIN; poll_event.revents = 0; if (poll(&poll_event, 1, (int)timeout) <= 0) { return false; } if ((poll_event.revents & POLLIN) == 0) { return false; } ret = recvfrom(sock->sock, data, *length, 0, (struct sockaddr *)&sock_addr, &sockaddr_size); if (ret > 0) { *remote_addr = ntohl(sock_addr.sin_addr.s_addr); *remote_port = ntohs(sock_addr.sin_port); *length = (size_t)ret; return true; } return false; } libhdhomerun/hdhomerun_sock_windows.c0000664000175000017500000003120113144735121017444 0ustar buildbuild/* * hdhomerun_sock_windows.c * * Copyright © 2010-2016 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" #include struct hdhomerun_sock_t { SOCKET sock; HANDLE event; long events_selected; }; #if defined(_WINRT) static char *hdhomerun_local_ip_info_str = NULL; /* * String format: ip address '/' subnet mask bits ... * Example: "192.168.0.100/24 169.254.0.100/16" */ void hdhomerun_local_ip_info_set_str(const char *ip_info_str) { if (hdhomerun_local_ip_info_str) { free(hdhomerun_local_ip_info_str); } hdhomerun_local_ip_info_str = strdup(ip_info_str); } int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count) { const char *ptr = hdhomerun_local_ip_info_str; if (!ptr) { return 0; } struct hdhomerun_local_ip_info_t *ip_info = ip_info_list; int count = 0; while (count < max_count) { unsigned int a[4]; unsigned int mask_bitcount; if (sscanf(ptr, "%u.%u.%u.%u/%u", &a[0], &a[1], &a[2], &a[3], &mask_bitcount) != 5) { break; } ip_info->ip_addr = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0)); ip_info->subnet_mask = 0xFFFFFFFF << (32 - mask_bitcount); ip_info++; count++; ptr = strchr(ptr, ' '); if (!ptr) { break; } ptr++; } return count; } #endif #if !defined(_WINRT) int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count) { PIP_ADAPTER_INFO AdapterInfo; ULONG AdapterInfoLength = sizeof(IP_ADAPTER_INFO) * 16; while (1) { AdapterInfo = (IP_ADAPTER_INFO *)malloc(AdapterInfoLength); if (!AdapterInfo) { return -1; } ULONG LengthNeeded = AdapterInfoLength; DWORD Ret = GetAdaptersInfo(AdapterInfo, &LengthNeeded); if (Ret == NO_ERROR) { break; } free(AdapterInfo); if (Ret != ERROR_BUFFER_OVERFLOW) { return -1; } if (AdapterInfoLength >= LengthNeeded) { return -1; } AdapterInfoLength = LengthNeeded; } int count = 0; PIP_ADAPTER_INFO Adapter = AdapterInfo; while (Adapter) { IP_ADDR_STRING *IPAddr = &Adapter->IpAddressList; while (IPAddr) { uint32_t ip_addr = ntohl(inet_addr(IPAddr->IpAddress.String)); uint32_t subnet_mask = ntohl(inet_addr(IPAddr->IpMask.String)); if (ip_addr == 0) { IPAddr = IPAddr->Next; continue; } if (count < max_count) { struct hdhomerun_local_ip_info_t *ip_info = &ip_info_list[count]; ip_info->ip_addr = ip_addr; ip_info->subnet_mask = subnet_mask; } count++; IPAddr = IPAddr->Next; } if (count >= max_count) { break; } Adapter = Adapter->Next; } free(AdapterInfo); return count; } #endif static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int protocol) { struct hdhomerun_sock_t *sock = (struct hdhomerun_sock_t *)calloc(1, sizeof(struct hdhomerun_sock_t)); if (!sock) { return NULL; } /* Create socket. */ sock->sock = socket(AF_INET, protocol, 0); if (sock->sock == INVALID_SOCKET) { free(sock); return NULL; } /* Set non-blocking */ unsigned long mode = 1; if (ioctlsocket(sock->sock, FIONBIO, &mode) != 0) { hdhomerun_sock_destroy(sock); return NULL; } /* Event */ sock->event = CreateEvent(NULL, false, false, NULL); if (!sock->event) { hdhomerun_sock_destroy(sock); return NULL; } /* Success. */ return sock; } struct hdhomerun_sock_t *hdhomerun_sock_create_udp(void) { struct hdhomerun_sock_t *sock = hdhomerun_sock_create_internal(SOCK_DGRAM); if (!sock) { return NULL; } /* Allow broadcast. */ int sock_opt = 1; setsockopt(sock->sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt)); /* Success. */ return sock; } struct hdhomerun_sock_t *hdhomerun_sock_create_tcp(void) { return hdhomerun_sock_create_internal(SOCK_STREAM); } void hdhomerun_sock_destroy(struct hdhomerun_sock_t *sock) { if (sock->event) { CloseHandle(sock->event); } closesocket(sock->sock); free(sock); } void hdhomerun_sock_stop(struct hdhomerun_sock_t *sock) { shutdown(sock->sock, SD_BOTH); } void hdhomerun_sock_set_send_buffer_size(struct hdhomerun_sock_t *sock, size_t size) { int size_opt = (int)size; setsockopt(sock->sock, SOL_SOCKET, SO_SNDBUF, (char *)&size_opt, sizeof(size_opt)); } void hdhomerun_sock_set_recv_buffer_size(struct hdhomerun_sock_t *sock, size_t size) { int size_opt = (int)size; setsockopt(sock->sock, SOL_SOCKET, SO_RCVBUF, (char *)&size_opt, sizeof(size_opt)); } void hdhomerun_sock_set_allow_reuse(struct hdhomerun_sock_t *sock) { int sock_opt = 1; setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt)); } int hdhomerun_sock_getlasterror(void) { return WSAGetLastError(); } uint32_t hdhomerun_sock_getsockname_addr(struct hdhomerun_sock_t *sock) { struct sockaddr_in sock_addr; int sockaddr_size = sizeof(sock_addr); if (getsockname(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) { return 0; } return ntohl(sock_addr.sin_addr.s_addr); } uint16_t hdhomerun_sock_getsockname_port(struct hdhomerun_sock_t *sock) { struct sockaddr_in sock_addr; int sockaddr_size = sizeof(sock_addr); if (getsockname(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) { return 0; } return ntohs(sock_addr.sin_port); } uint32_t hdhomerun_sock_getpeername_addr(struct hdhomerun_sock_t *sock) { struct sockaddr_in sock_addr; int sockaddr_size = sizeof(sock_addr); if (getpeername(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) { return 0; } return ntohl(sock_addr.sin_addr.s_addr); } uint32_t hdhomerun_sock_getaddrinfo_addr(struct hdhomerun_sock_t *sock, const char *name) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; struct addrinfo *sock_info; if (getaddrinfo(name, NULL, &hints, &sock_info) != 0) { return 0; } struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr; uint32_t addr = ntohl(sock_addr->sin_addr.s_addr); freeaddrinfo(sock_info); return addr; } bool hdhomerun_sock_join_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip) { struct ip_mreq imr; memset(&imr, 0, sizeof(imr)); imr.imr_multiaddr.s_addr = htonl(multicast_ip); imr.imr_interface.s_addr = htonl(local_ip); if (setsockopt(sock->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) { return false; } return true; } bool hdhomerun_sock_leave_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip) { struct ip_mreq imr; memset(&imr, 0, sizeof(imr)); imr.imr_multiaddr.s_addr = htonl(multicast_ip); imr.imr_interface.s_addr = htonl(local_ip); if (setsockopt(sock->sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) { return false; } return true; } bool hdhomerun_sock_bind(struct hdhomerun_sock_t *sock, uint32_t local_addr, uint16_t local_port, bool allow_reuse) { int sock_opt = allow_reuse; setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt)); struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(local_addr); sock_addr.sin_port = htons(local_port); if (bind(sock->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) { return false; } return true; } static bool hdhomerun_sock_event_select(struct hdhomerun_sock_t *sock, long events) { if (sock->events_selected != events) { if (WSAEventSelect(sock->sock, sock->event, events) == SOCKET_ERROR) { return false; } sock->events_selected = events; } ResetEvent(sock->event); return true; } bool hdhomerun_sock_connect(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout) { if (!hdhomerun_sock_event_select(sock, FD_WRITE | FD_CLOSE)) { return false; } struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(remote_addr); sock_addr.sin_port = htons(remote_port); if (connect(sock->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) { if (WSAGetLastError() != WSAEWOULDBLOCK) { return false; } } DWORD wait_ret = WaitForSingleObjectEx(sock->event, (DWORD)timeout, false); if (wait_ret != WAIT_OBJECT_0) { return false; } WSANETWORKEVENTS network_events; if (WSAEnumNetworkEvents(sock->sock, sock->event, &network_events) == SOCKET_ERROR) { return false; } if ((network_events.lNetworkEvents & FD_WRITE) == 0) { return false; } if (network_events.lNetworkEvents & FD_CLOSE) { return false; } return true; } bool hdhomerun_sock_send(struct hdhomerun_sock_t *sock, const void *data, size_t length, uint64_t timeout) { if (!hdhomerun_sock_event_select(sock, FD_WRITE | FD_CLOSE)) { return false; } uint64_t stop_time = getcurrenttime() + timeout; const uint8_t *ptr = (uint8_t *)data; while (1) { int ret = send(sock->sock, (char *)ptr, (int)length, 0); if (ret >= (int)length) { return true; } if ((ret == SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) { return false; } if (ret > 0) { ptr += ret; length -= ret; } uint64_t current_time = getcurrenttime(); if (current_time >= stop_time) { return false; } if (WaitForSingleObjectEx(sock->event, (DWORD)(stop_time - current_time), false) != WAIT_OBJECT_0) { return false; } } } bool hdhomerun_sock_sendto(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout) { if (!hdhomerun_sock_event_select(sock, FD_WRITE | FD_CLOSE)) { return false; } struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(remote_addr); sock_addr.sin_port = htons(remote_port); int ret = sendto(sock->sock, (char *)data, (int)length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr)); if (ret >= (int)length) { return true; } if (ret >= 0) { return false; } if (WSAGetLastError() != WSAEWOULDBLOCK) { return false; } if (WaitForSingleObjectEx(sock->event, (DWORD)timeout, false) != WAIT_OBJECT_0) { return false; } ret = sendto(sock->sock, (char *)data, (int)length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr)); if (ret >= (int)length) { return true; } return false; } bool hdhomerun_sock_recv(struct hdhomerun_sock_t *sock, void *data, size_t *length, uint64_t timeout) { if (!hdhomerun_sock_event_select(sock, FD_READ | FD_CLOSE)) { return false; } int ret = recv(sock->sock, (char *)data, (int)(*length), 0); if (ret > 0) { *length = ret; return true; } if (ret == 0) { return false; } if (WSAGetLastError() != WSAEWOULDBLOCK) { return false; } if (WaitForSingleObjectEx(sock->event, (DWORD)timeout, false) != WAIT_OBJECT_0) { return false; } ret = recv(sock->sock, (char *)data, (int)(*length), 0); if (ret > 0) { *length = ret; return true; } return false; } bool hdhomerun_sock_recvfrom(struct hdhomerun_sock_t *sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout) { if (!hdhomerun_sock_event_select(sock, FD_READ | FD_CLOSE)) { return false; } struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); int sockaddr_size = sizeof(sock_addr); int ret = recvfrom(sock->sock, (char *)data, (int)(*length), 0, (struct sockaddr *)&sock_addr, &sockaddr_size); if (ret > 0) { *remote_addr = ntohl(sock_addr.sin_addr.s_addr); *remote_port = ntohs(sock_addr.sin_port); *length = ret; return true; } if (ret == 0) { return false; } if (WSAGetLastError() != WSAEWOULDBLOCK) { return false; } if (WaitForSingleObjectEx(sock->event, (DWORD)timeout, false) != WAIT_OBJECT_0) { return false; } ret = recvfrom(sock->sock, (char *)data, (int)(*length), 0, (struct sockaddr *)&sock_addr, &sockaddr_size); if (ret > 0) { *remote_addr = ntohl(sock_addr.sin_addr.s_addr); *remote_port = ntohs(sock_addr.sin_port); *length = ret; return true; } return false; } libhdhomerun/hdhomerun_types.h0000664000175000017500000000436713063620312016114 0ustar buildbuild/* * hdhomerun_types.h * * Copyright © 2008-2015 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define HDHOMERUN_STATUS_COLOR_NEUTRAL 0xFFFFFFFF #define HDHOMERUN_STATUS_COLOR_RED 0xFFFF0000 #define HDHOMERUN_STATUS_COLOR_YELLOW 0xFFFFFF00 #define HDHOMERUN_STATUS_COLOR_GREEN 0xFF00C000 struct hdhomerun_device_t; struct hdhomerun_device_allocation_t; struct hdhomerun_tuner_status_t { char channel[32]; char lock_str[32]; bool signal_present; bool lock_supported; bool lock_unsupported; unsigned int signal_strength; unsigned int signal_to_noise_quality; unsigned int symbol_error_quality; uint32_t raw_bits_per_second; uint32_t packets_per_second; }; struct hdhomerun_tuner_vstatus_t { char vchannel[32]; char name[32]; char auth[32]; char cci[32]; char cgms[32]; bool not_subscribed; bool not_available; bool copy_protected; }; struct hdhomerun_channelscan_program_t { char program_str[64]; uint16_t program_number; uint16_t virtual_major; uint16_t virtual_minor; uint16_t type; char name[32]; }; #define HDHOMERUN_CHANNELSCAN_MAX_PROGRAM_COUNT 64 struct hdhomerun_channelscan_result_t { char channel_str[64]; uint32_t channelmap; uint32_t frequency; struct hdhomerun_tuner_status_t status; int program_count; struct hdhomerun_channelscan_program_t programs[HDHOMERUN_CHANNELSCAN_MAX_PROGRAM_COUNT]; bool transport_stream_id_detected; bool original_network_id_detected; uint16_t transport_stream_id; uint16_t original_network_id; }; struct hdhomerun_plotsample_t { int16_t real; int16_t imag; }; libhdhomerun/hdhomerun_video.c0000664000175000017500000002635213366173765016074 0ustar buildbuild/* * hdhomerun_video.c * * Copyright © 2006-2016 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hdhomerun.h" struct hdhomerun_video_sock_t { thread_mutex_t lock; struct hdhomerun_debug_t *dbg; struct hdhomerun_sock_t *sock; uint32_t keepalive_lockkey; uint32_t keepalive_addr; uint16_t keepalive_port; volatile bool keepalive_start; volatile size_t head; volatile size_t tail; uint8_t *buffer; size_t buffer_size; size_t advance; thread_task_t thread; volatile bool terminate; volatile uint32_t packet_count; volatile uint32_t transport_error_count; volatile uint32_t network_error_count; volatile uint32_t sequence_error_count; volatile uint32_t overflow_error_count; volatile uint32_t rtp_sequence; volatile uint8_t sequence[0x2000]; }; static void hdhomerun_video_thread_execute(void *arg); struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, bool allow_port_reuse, size_t buffer_size, struct hdhomerun_debug_t *dbg) { /* Create object. */ struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)calloc(1, sizeof(struct hdhomerun_video_sock_t)); if (!vs) { hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate video object\n"); return NULL; } vs->dbg = dbg; thread_mutex_init(&vs->lock); /* Reset sequence tracking. */ hdhomerun_video_flush(vs); /* Buffer size. */ vs->buffer_size = (buffer_size / VIDEO_DATA_PACKET_SIZE) * VIDEO_DATA_PACKET_SIZE; if (vs->buffer_size == 0) { hdhomerun_debug_printf(dbg, "hdhomerun_video_create: invalid buffer size (%lu bytes)\n", (unsigned long)buffer_size); goto error; } vs->buffer_size += VIDEO_DATA_PACKET_SIZE; /* Create buffer. */ vs->buffer = (uint8_t *)malloc(vs->buffer_size); if (!vs->buffer) { hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate buffer (%lu bytes)\n", (unsigned long)vs->buffer_size); goto error; } /* Create socket. */ vs->sock = hdhomerun_sock_create_udp(); if (!vs->sock) { hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate socket\n"); goto error; } /* Expand socket buffer size. */ hdhomerun_sock_set_recv_buffer_size(vs->sock, 1024 * 1024); /* Bind socket. */ if (!hdhomerun_sock_bind(vs->sock, INADDR_ANY, listen_port, allow_port_reuse)) { hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to bind socket (port %u)\n", listen_port); goto error; } /* Start thread. */ if (!thread_task_create(&vs->thread, &hdhomerun_video_thread_execute, vs)) { hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to start thread\n"); goto error; } /* Success. */ return vs; error: if (vs->sock) { hdhomerun_sock_destroy(vs->sock); } if (vs->buffer) { free(vs->buffer); } thread_mutex_dispose(&vs->lock); free(vs); return NULL; } void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs) { vs->terminate = true; thread_task_join(vs->thread); hdhomerun_sock_destroy(vs->sock); thread_mutex_dispose(&vs->lock); free(vs->buffer); free(vs); } void hdhomerun_video_set_keepalive(struct hdhomerun_video_sock_t *vs, uint32_t remote_addr, uint16_t remote_port, uint32_t lockkey) { thread_mutex_lock(&vs->lock); vs->keepalive_addr = remote_addr; vs->keepalive_port = remote_port; vs->keepalive_lockkey = lockkey; if ((remote_addr != 0) && (remote_port != 0)) { vs->keepalive_start = true; } thread_mutex_unlock(&vs->lock); } struct hdhomerun_sock_t *hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs) { return vs->sock; } uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs) { uint16_t port = hdhomerun_sock_getsockname_port(vs->sock); if (port == 0) { hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_get_local_port: getsockname failed (%d)\n", hdhomerun_sock_getlasterror()); return 0; } return port; } int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip) { if (!hdhomerun_sock_join_multicast_group(vs->sock, multicast_ip, local_ip)) { hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_join_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror()); return -1; } return 1; } void hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip) { if (!hdhomerun_sock_leave_multicast_group(vs->sock, multicast_ip, local_ip)) { hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_leave_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror()); } } static void hdhomerun_video_stats_ts_pkt(struct hdhomerun_video_sock_t *vs, uint8_t *ptr) { uint16_t packet_identifier; packet_identifier = (uint16_t)(ptr[1] & 0x1F) << 8; packet_identifier |= (uint16_t)ptr[2] << 0; bool transport_error = (ptr[1] & 0x80) != 0; if (transport_error) { vs->transport_error_count++; vs->sequence[packet_identifier] = 0xFF; return; } if (packet_identifier == 0x1FFF) { return; } bool payload_present = (ptr[3] & 0x10) != 0; if (!payload_present) { return; } uint8_t sequence = ptr[3] & 0x0F; uint8_t previous_sequence = vs->sequence[packet_identifier]; vs->sequence[packet_identifier] = sequence; if (previous_sequence == 0xFF) { return; } if (sequence == ((previous_sequence + 1) & 0x0F)) { return; } vs->sequence_error_count++; } static void hdhomerun_video_parse_rtp(struct hdhomerun_video_sock_t *vs, struct hdhomerun_pkt_t *pkt) { pkt->pos += 2; uint32_t rtp_sequence = hdhomerun_pkt_read_u16(pkt); pkt->pos += 8; uint32_t previous_rtp_sequence = vs->rtp_sequence; vs->rtp_sequence = rtp_sequence; /* Initial case - first packet received. */ if (previous_rtp_sequence == 0xFFFFFFFF) { return; } /* Normal case - next sequence number. */ if (rtp_sequence == ((previous_rtp_sequence + 1) & 0xFFFF)) { return; } /* Error case - sequence missed. */ vs->network_error_count++; /* Restart pid sequence check after packet loss. */ int i; for (i = 0; i < 0x2000; i++) { vs->sequence[i] = 0xFF; } } static void hdhomerun_video_thread_send_keepalive(struct hdhomerun_video_sock_t *vs) { thread_mutex_lock(&vs->lock); uint32_t keepalive_lockkey = vs->keepalive_lockkey; uint32_t keepalive_addr = vs->keepalive_addr; uint16_t keepalive_port = vs->keepalive_port; vs->keepalive_start = false; thread_mutex_unlock(&vs->lock); if ((keepalive_addr == 0) || (keepalive_port == 0)) { return; } struct hdhomerun_pkt_t pkt; hdhomerun_pkt_reset(&pkt); hdhomerun_pkt_write_u32(&pkt, keepalive_lockkey); hdhomerun_sock_sendto(vs->sock, keepalive_addr, keepalive_port, pkt.start, pkt.end - pkt.start, 25); } static void hdhomerun_video_thread_execute(void *arg) { struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)arg; uint64_t send_time = getcurrenttime(); while (!vs->terminate) { uint64_t current_time = getcurrenttime(); if (vs->keepalive_start || (current_time >= send_time)) { hdhomerun_video_thread_send_keepalive(vs); send_time = current_time + 1000; } /* Receive. */ struct hdhomerun_pkt_t pkt; hdhomerun_pkt_reset(&pkt); size_t length = VIDEO_RTP_DATA_PACKET_SIZE; if (!hdhomerun_sock_recv(vs->sock, pkt.end, &length, 25)) { continue; } pkt.end += length; if (length == VIDEO_RTP_DATA_PACKET_SIZE) { hdhomerun_video_parse_rtp(vs, &pkt); length = pkt.end - pkt.pos; } if (length != VIDEO_DATA_PACKET_SIZE) { /* Data received but not valid - ignore. */ continue; } thread_mutex_lock(&vs->lock); /* Store in ring buffer. */ size_t head = vs->head; uint8_t *ptr = vs->buffer + head; memcpy(ptr, pkt.pos, length); /* Stats. */ vs->packet_count++; hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 0); hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 1); hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 2); hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 3); hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 4); hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 5); hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 6); /* Calculate new head. */ head += length; if (head >= vs->buffer_size) { head -= vs->buffer_size; } /* Check for buffer overflow. */ if (head == vs->tail) { vs->overflow_error_count++; thread_mutex_unlock(&vs->lock); continue; } vs->head = head; thread_mutex_unlock(&vs->lock); } } uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size, size_t *pactual_size) { thread_mutex_lock(&vs->lock); size_t head = vs->head; size_t tail = vs->tail; if (vs->advance > 0) { tail += vs->advance; if (tail >= vs->buffer_size) { tail -= vs->buffer_size; } vs->tail = tail; } if (head == tail) { vs->advance = 0; *pactual_size = 0; thread_mutex_unlock(&vs->lock); return NULL; } size_t size = (max_size / VIDEO_DATA_PACKET_SIZE) * VIDEO_DATA_PACKET_SIZE; if (size == 0) { vs->advance = 0; *pactual_size = 0; thread_mutex_unlock(&vs->lock); return NULL; } size_t avail; if (head > tail) { avail = head - tail; } else { avail = vs->buffer_size - tail; } if (size > avail) { size = avail; } vs->advance = size; *pactual_size = size; uint8_t *result = vs->buffer + tail; thread_mutex_unlock(&vs->lock); return result; } void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs) { thread_mutex_lock(&vs->lock); vs->tail = vs->head; vs->advance = 0; vs->rtp_sequence = 0xFFFFFFFF; int i; for (i = 0; i < 0x2000; i++) { vs->sequence[i] = 0xFF; } vs->packet_count = 0; vs->transport_error_count = 0; vs->network_error_count = 0; vs->sequence_error_count = 0; vs->overflow_error_count = 0; thread_mutex_unlock(&vs->lock); } void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs) { struct hdhomerun_video_stats_t stats; hdhomerun_video_get_stats(vs, &stats); hdhomerun_debug_printf(vs->dbg, "video sock: pkt=%u net=%u te=%u miss=%u drop=%u\n", (unsigned int)stats.packet_count, (unsigned int)stats.network_error_count, (unsigned int)stats.transport_error_count, (unsigned int)stats.sequence_error_count, (unsigned int)stats.overflow_error_count ); } void hdhomerun_video_get_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_video_stats_t *stats) { memset(stats, 0, sizeof(struct hdhomerun_video_stats_t)); thread_mutex_lock(&vs->lock); stats->packet_count = vs->packet_count; stats->network_error_count = vs->network_error_count; stats->transport_error_count = vs->transport_error_count; stats->sequence_error_count = vs->sequence_error_count; stats->overflow_error_count = vs->overflow_error_count; thread_mutex_unlock(&vs->lock); } libhdhomerun/hdhomerun_video.h0000664000175000017500000001015513063620312016046 0ustar buildbuild/* * hdhomerun_video.h * * Copyright © 2006-2016 Silicondust USA Inc. . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __cplusplus extern "C" { #endif struct hdhomerun_video_sock_t; struct hdhomerun_video_stats_t { uint32_t packet_count; uint32_t network_error_count; uint32_t transport_error_count; uint32_t sequence_error_count; uint32_t overflow_error_count; }; #define TS_PACKET_SIZE 188 #define VIDEO_DATA_PACKET_SIZE (188 * 7) #define VIDEO_DATA_BUFFER_SIZE_1S (20000000 / 8) #define VIDEO_RTP_DATA_PACKET_SIZE ((188 * 7) + 12) /* * Create a video/data socket. * * uint16_t listen_port: Port number to listen on. Set to 0 to auto-select. * size_t buffer_size: Size of receive buffer. For 1 second of buffer use VIDEO_DATA_BUFFER_SIZE_1S. * struct hdhomerun_debug_t *dbg: Pointer to debug logging object. May be NULL. * * Returns a pointer to the newly created control socket. * * When no longer needed, the socket should be destroyed by calling hdhomerun_control_destroy. */ extern LIBHDHOMERUN_API struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, bool allow_port_reuse, size_t buffer_size, struct hdhomerun_debug_t *dbg); extern LIBHDHOMERUN_API void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs); /* * Configure to send a keepalive packet every second. */ extern LIBHDHOMERUN_API void hdhomerun_video_set_keepalive(struct hdhomerun_video_sock_t *vs, uint32_t remote_addr, uint16_t remote_port, uint32_t lockkey); /* * Get the port the socket is listening on. * * Returns 16-bit port with native endianness, or 0 on error. */ extern LIBHDHOMERUN_API uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs); /* * Join/leave multicast group. */ extern LIBHDHOMERUN_API int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip); extern LIBHDHOMERUN_API void hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip); /* * Read data from buffer. * * size_t max_size: The maximum amount of data to be returned. * size_t *pactual_size: The caller-supplied pactual_size value will be updated to contain the amount * of data available to the caller. * * Returns a pointer to the data, or NULL if no data is available. * The data will remain valid until another call to hdhomerun_video_recv. * * The amount of data returned will always be a multiple of VIDEO_DATA_PACKET_SIZE (1316). * Attempting to read a single TS frame (188 bytes) will not return data as it is less than * the minimum size. * * The buffer is implemented as a ring buffer. It is possible for this function to return a small * amount of data when more is available due to the wrap-around case. */ extern LIBHDHOMERUN_API uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size, size_t *pactual_size); /* * Flush the buffer. */ extern LIBHDHOMERUN_API void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs); /* * Debug print internal stats. */ extern LIBHDHOMERUN_API void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs); extern LIBHDHOMERUN_API void hdhomerun_video_get_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_video_stats_t *stats); /* * Internal use only. */ extern LIBHDHOMERUN_API struct hdhomerun_sock_t *hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs); #ifdef __cplusplus } #endif libhdhomerun/Makefile0000664000175000017500000000262413463077107014174 0ustar buildbuild LIBSRCS += hdhomerun_channels.c LIBSRCS += hdhomerun_channelscan.c LIBSRCS += hdhomerun_control.c LIBSRCS += hdhomerun_debug.c LIBSRCS += hdhomerun_device.c LIBSRCS += hdhomerun_device_selector.c LIBSRCS += hdhomerun_discover.c LIBSRCS += hdhomerun_os_posix.c LIBSRCS += hdhomerun_pkt.c LIBSRCS += hdhomerun_sock_posix.c LIBSRCS += hdhomerun_video.c CC := $(CROSS_COMPILE)gcc STRIP := $(CROSS_COMPILE)strip CFLAGS += -O2 -Wall -Wextra -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wpointer-arith -Wno-unused-parameter LDFLAGS += -lpthread SHARED = -shared -Wl,-soname,libhdhomerun$(LIBEXT) ifeq ($(OS),Windows_NT) BINEXT := .exe LIBEXT := .dll LDFLAGS += -liphlpapi else OS := $(shell uname -s) LIBEXT := .so ifeq ($(OS),Linux) LDFLAGS += -lrt endif ifeq ($(OS),SunOS) LDFLAGS += -lsocket endif ifeq ($(OS),Darwin) CFLAGS += -arch x86_64 LIBEXT := .dylib SHARED := -dynamiclib -install_name libhdhomerun$(LIBEXT) endif endif all : hdhomerun_config$(BINEXT) libhdhomerun$(LIBEXT) hdhomerun_config$(BINEXT) : hdhomerun_config.c $(LIBSRCS) $(CC) $(CFLAGS) $+ $(LDFLAGS) -o $@ $(STRIP) $@ libhdhomerun$(LIBEXT) : $(LIBSRCS) $(CC) $(CFLAGS) -fPIC -DDLL_EXPORT $(SHARED) $+ $(LDFLAGS) -o $@ clean : -rm -f hdhomerun_config$(BINEXT) -rm -f libhdhomerun$(LIBEXT) distclean : clean %: @echo "(ignoring request to make $@)" .PHONY: all list clean distclean libhdhomerun/LICENSE0000664000175000017500000006353513013646353013545 0ustar buildbuild GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. (This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.) Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. {signature of Ty Coon}, 1 April 1990 Ty Coon, President of Vice That's all there is to it! libhdhomerun/README.md0000664000175000017500000000076713117566021014013 0ustar buildbuildCopyright © 2005-2017 Silicondust USA Inc. . This library implements the libhdhomerun protocol for use with Silicondust HDHomeRun TV tuners. To compile simply "make" - this will compile both the library and the hdhomerun_config command line utility suitable for sending commands or scripting control of a HDHomeRun. The top level API is hdhomerun_device - see hdhomerun_device.h for documentation. Additional libraries required: - pthread (osx, linux, bsd) - iphlpapi (windows)