diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/README release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/README
--- mythtv/libs/libmythtv/hdhomerun/README	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/README	2009-01-16 18:31:54.000000000 -0600
@@ -1,7 +1,41 @@
+/*
+ * README
+ *
+ * Copyright © 2005-2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
 
-This directory contains Silicondust Engineering's hdhomerun
-library used for communicating with the HDHomeRun hardware.
+Top level include file: hdhomerun.h
 
-The files in this directory are released under the GNU LESSER GENERAL PUBLIC LICENSE
-For details see the lgpl.txt file which should be located in this directory.
-If you can not find it, it can also be seen at http://www.gnu.org/licenses/lgpl.txt
+Top level API: hdhomerun_device. See hdhomerun_device.h for documentation.
+
+The hdhomerun_device API should be used rather than the low level control and video APIs required with previous versions.
+
+Additional libraries required:
+- pthread
+- iphlpapi (windows only)
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/README.MythTV release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/README.MythTV
--- mythtv/libs/libmythtv/hdhomerun/README.MythTV	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/README.MythTV	2009-01-16 18:17:24.000000000 -0600
@@ -0,0 +1,7 @@
+
+This directory contains Silicondust Engineering's hdhomerun
+library used for communicating with the HDHomeRun hardware.
+
+The files in this directory are released under the GNU LESSER GENERAL PUBLIC LICENSE
+For details see the lgpl.txt file which should be located in this directory.
+If you can not find it, it can also be seen at http://www.gnu.org/licenses/lgpl.txt
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun.h	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun.h	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
- * hdhomerun_device.h
+ * hdhomerun.h
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,13 +14,29 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 
 #include "hdhomerun_os.h"
+#include "hdhomerun_types.h"
 #include "hdhomerun_pkt.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"
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_channels.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_channels.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_channels.c	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_channels.c	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,378 @@
+/*
+ * hdhomerun_channels.c
+ *
+ * Copyright Â© 2007-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+#include "hdhomerun.h"
+
+#define FREQUENCY_RESOLUTION 62500
+
+struct hdhomerun_channel_entry_t {
+	struct hdhomerun_channel_entry_t *next;
+	struct hdhomerun_channel_entry_t *prev;
+	uint32_t frequency;
+	uint8_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 {
+	uint8_t channel_range_start;
+	uint8_t channel_range_end;
+	uint32_t frequency;
+	uint32_t spacing;
+};
+
+struct hdhomerun_channelmap_record_t {
+	const char *channelmap_prefix;
+	const char *channelmap;
+	const struct hdhomerun_channelmap_range_t *range_list;
+	const char *channelmap_scan_group;
+	const char *countrycodes;
+};
+
+/* AU antenna channels. Channels {0, 1, 2, 6, 7, 8, 9, 9A} are numbered {2, 3, 4, 5, 6, 7, 8, 9} by the HDHomeRun. */
+static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_au_bcast[] = {
+	{  2,   2,  48500000, 7000000},
+	{  3,   4,  59500000, 7000000},
+	{  5,  12, 177500000, 7000000},
+	{ 28,  69, 529500000, 7000000},
+	{  0,   0,         0,       0}
+};
+
+/* AU cable channels. TBD. */
+static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_au_cable[] = {
+	{  0,   0,         0,       0}
+};
+
+/* EU antenna channels. */
+static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_eu_bcast[] = {
+	{  2,   4,  50500000, 7000000},
+	{  5,  12, 177500000, 7000000},
+	{ 21,  69, 474000000, 8000000},
+	{  0,   0,         0,       0}
+};
+
+/* EU cable channels. Channels do not have simple numbers - the HDHomeRun uses its own numbering scheme (subject to change). */
+static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_eu_cable[] = {
+	{  6,   7, 113000000, 8000000},
+	{  9, 100, 138000000, 8000000},
+	{  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,  69, 473000000, 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, 135, 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, 135, 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, 135, 651012500, 6000000},
+	{  0,   0,         0,       0}
+};
+
+static const struct hdhomerun_channelmap_record_t hdhomerun_channelmap_table[] = {
+	{"au", "au-bcast", hdhomerun_channelmap_range_au_bcast, "au-bcast",               "AU"},
+	{"au", "au-cable", hdhomerun_channelmap_range_au_cable, "au-cable",               "AU"},
+	{"eu", "eu-bcast", hdhomerun_channelmap_range_eu_bcast, "eu-bcast",               "EU"},
+	{"eu", "eu-cable", hdhomerun_channelmap_range_eu_cable, "eu-cable",               "EU"},
+	{"tw", "tw-bcast", hdhomerun_channelmap_range_us_bcast, "tw-bcast",               "TW"},
+	{"tw", "tw-cable", hdhomerun_channelmap_range_us_cable, "tw-cable",               "TW"},
+	{"us", "us-bcast", hdhomerun_channelmap_range_us_bcast, "us-bcast",               "CA US"},
+	{"us", "us-cable", hdhomerun_channelmap_range_us_cable, "us-cable us-hrc us-irc", "CA US"},
+	{"us", "us-hrc",   hdhomerun_channelmap_range_us_hrc  , "us-cable us-hrc us-irc", "CA US"},
+	{"us", "us-irc",   hdhomerun_channelmap_range_us_irc,   "us-cable us-hrc us-irc", "CA US"},
+	{NULL, NULL,       NULL,                                NULL,                     NULL}
+};
+
+const char *hdhomerun_channelmap_convert_countrycode_to_channelmap_prefix(const char *countrycode)
+{
+	const struct hdhomerun_channelmap_record_t *record = hdhomerun_channelmap_table;
+	while (record->channelmap) {
+		if (strstr(record->countrycodes, countrycode)) {
+			return record->channelmap_prefix;
+		}
+		record++;
+	}
+
+	return "eu";
+}
+
+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;
+}
+
+uint8_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_truncate(uint32_t frequency)
+{
+	return (frequency / FREQUENCY_RESOLUTION) * FREQUENCY_RESOLUTION;
+}
+
+uint32_t hdhomerun_channel_number_to_frequency(struct hdhomerun_channel_list_t *channel_list, uint8_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;
+}
+
+uint8_t hdhomerun_channel_frequency_to_number(struct hdhomerun_channel_list_t *channel_list, uint32_t frequency)
+{
+	frequency = hdhomerun_channel_frequency_truncate(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)
+{
+	uint8_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_truncate(entry->frequency);
+		sprintf(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;
+}
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_channels.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_channels.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_channels.h	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_channels.h	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,63 @@
+/*
+ * hdhomerun_channels.h
+ *
+ * Copyright Â© 2007-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct hdhomerun_channel_entry_t;
+struct hdhomerun_channel_list_t;
+
+extern LIBTYPE const char *hdhomerun_channelmap_convert_countrycode_to_channelmap_prefix(const char *countrycode);
+extern LIBTYPE const char *hdhomerun_channelmap_get_channelmap_scan_group(const char *channelmap);
+
+extern LIBTYPE uint8_t hdhomerun_channel_entry_channel_number(struct hdhomerun_channel_entry_t *entry);
+extern LIBTYPE uint32_t hdhomerun_channel_entry_frequency(struct hdhomerun_channel_entry_t *entry);
+extern LIBTYPE const char *hdhomerun_channel_entry_name(struct hdhomerun_channel_entry_t *entry);
+
+extern LIBTYPE struct hdhomerun_channel_list_t *hdhomerun_channel_list_create(const char *channelmap);
+extern LIBTYPE void hdhomerun_channel_list_destroy(struct hdhomerun_channel_list_t *channel_list);
+
+extern LIBTYPE struct hdhomerun_channel_entry_t *hdhomerun_channel_list_first(struct hdhomerun_channel_list_t *channel_list);
+extern LIBTYPE struct hdhomerun_channel_entry_t *hdhomerun_channel_list_last(struct hdhomerun_channel_list_t *channel_list);
+extern LIBTYPE struct hdhomerun_channel_entry_t *hdhomerun_channel_list_next(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry);
+extern LIBTYPE struct hdhomerun_channel_entry_t *hdhomerun_channel_list_prev(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry);
+extern LIBTYPE uint32_t hdhomerun_channel_list_total_count(struct hdhomerun_channel_list_t *channel_list);
+extern LIBTYPE uint32_t hdhomerun_channel_list_frequency_count(struct hdhomerun_channel_list_t *channel_list);
+
+extern LIBTYPE uint32_t hdhomerun_channel_frequency_truncate(uint32_t frequency);
+extern LIBTYPE uint32_t hdhomerun_channel_number_to_frequency(struct hdhomerun_channel_list_t *channel_list, uint8_t channel_number);
+extern LIBTYPE uint8_t hdhomerun_channel_frequency_to_number(struct hdhomerun_channel_list_t *channel_list, uint32_t frequency);
+
+#ifdef __cplusplus
+}
+#endif
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_channelscan.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_channelscan.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_channelscan.c	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_channelscan.c	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,314 @@
+/*
+ * hdhomerun_channelscan.c
+ *
+ * Copyright Â© 2007-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+#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)
+{
+	free(scan);
+}
+
+static int channelscan_execute_find_lock(struct hdhomerun_channelscan_t *scan, uint32_t frequency, struct hdhomerun_channelscan_result_t *result)
+{
+	/* Set channel. */
+	char channel_str[64];
+	sprintf(channel_str, "auto:%ld", (unsigned long)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%. */
+	int i;
+	for (i = 0; i < 5 * 4; i++) {
+		usleep(250000);
+
+		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;
+		}
+	}
+
+	/* Timeout. */
+	return 1;
+}
+
+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_execute_detect_programs(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result, int *pchanged)
+{
+	*pchanged = FALSE;
+
+	char *streaminfo;
+	int ret = hdhomerun_device_get_tuner_streaminfo(scan->hd, &streaminfo);
+	if (ret <= 0) {
+		return ret;
+	}
+
+	char *line = streaminfo;
+	int program_count = 0;
+
+	while (1) {
+		char *end = strchr(line, '\n');
+		if (!end) {
+			break;
+		}
+
+		*end = 0;
+
+		struct hdhomerun_channelscan_program_t program;
+		memset(&program, 0, sizeof(program));
+
+		strncpy(program.program_str, line, sizeof(program.program_str));
+		program.program_str[sizeof(program.program_str) - 1] = 0;
+
+		unsigned int program_number;
+		unsigned int virtual_major, virtual_minor;
+		if (sscanf(line, "%u: %u.%u", &program_number, &virtual_major, &virtual_minor) != 3) {
+			continue;
+		}
+
+		program.program_number = program_number;
+		program.virtual_major = virtual_major;
+		program.virtual_minor = virtual_minor;
+
+		channelscan_extract_name(&program, line);
+
+		program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_NORMAL;
+		if (strstr(line, "(no data)")) {
+			program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_NODATA;
+		}
+		if (strstr(line, "(control)")) {
+			program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_CONTROL;
+		}
+		if (strstr(line, "(encrypted)")) {
+			program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_ENCRYPTED;
+		}
+
+		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 >= HDHOMERUN_CHANNELSCAN_MAX_PROGRAM_COUNT) {
+			break;
+		}
+
+		line = end + 1;
+	}
+
+	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);
+	strncpy(result->channel_str, hdhomerun_channel_entry_name(entry), sizeof(result->channel_str) - 1);
+	result->channel_str[sizeof(result->channel_str) - 1] = 0;
+
+	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;
+		}
+
+		char *ptr = strchr(result->channel_str, 0);
+		sprintf(ptr, ", %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_execute_find_lock(scan, result->frequency, result);
+	if (ret <= 0) {
+		return ret;
+	}
+	if (!result->status.lock_supported) {
+		return 1;
+	}
+
+	/* Detect programs. */
+	result->program_count = 0;
+
+	int changed;
+	ret = channelscan_execute_detect_programs(scan, result, &changed);
+	if (ret <= 0) {
+		return ret;
+	}
+
+	int same_count = 0;
+	int i;
+	for (i = 0; i < 5 * 4; i++) {
+		usleep(250000);
+
+		ret = channelscan_execute_detect_programs(scan, result, &changed);
+		if (ret <= 0) {
+			return ret;
+		}
+
+		if (changed) {
+			same_count = 0;
+			continue;
+		}
+
+		same_count++;
+		if (same_count >= 8) {
+			break;
+		}
+	}
+
+	/* Complete. */
+	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 scan->scanned_channels * 100 / (scan->scanned_channels + channels_remaining);
+}
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_channelscan.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_channelscan.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_channelscan.h	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_channelscan.h	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,53 @@
+/*
+ * hdhomerun_channelscan.h
+ *
+ * Copyright Â© 2007-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+#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 LIBTYPE struct hdhomerun_channelscan_t *channelscan_create(struct hdhomerun_device_t *hd, const char *channelmap);
+extern LIBTYPE void channelscan_destroy(struct hdhomerun_channelscan_t *scan);
+
+extern LIBTYPE int channelscan_advance(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result);
+extern LIBTYPE int channelscan_detect(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result);
+extern LIBTYPE uint8_t channelscan_get_progress(struct hdhomerun_channelscan_t *scan);
+
+#ifdef __cplusplus
+}
+#endif
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_config.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_config.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_config.c	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_config.c	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_config.c
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,12 +14,34 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 
 #include "hdhomerun.h"
 
+/*
+ * The console output format should be set to UTF-8, however in XP and Vista this breaks batch file processing.
+ * Attempting to restore on exit fails to restore if the program is terminated by the user.
+ * Solution - set the output format each printf.
+ */
+#if defined(__WINDOWS__)
+#define printf console_printf
+#define vprintf console_vprintf
+#endif
+
 static const char *appname;
 
 struct hdhomerun_device_t *hd;
@@ -31,7 +53,8 @@
 	printf("\t%s <id> get help\n", appname);
 	printf("\t%s <id> get <item>\n", appname);
 	printf("\t%s <id> set <item> <value>\n", appname);
-	printf("\t%s <id> scan <tuner> <starting channel>\n", appname);
+	printf("\t%s <id> scan <tuner> [<filename>]\n", appname);
+	printf("\t%s <id> save <tuner> <filename>\n", appname);
 	printf("\t%s <id> upgrade <filename>\n", appname);
 	return -1;
 }
@@ -68,10 +91,29 @@
 	return FALSE;
 }
 
-static int discover_print(void)
+static uint32_t parse_ip_addr(const char *str)
+{
+	unsigned long a[4];
+	if (sscanf(str, "%lu.%lu.%lu.%lu", &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 count = hdhomerun_discover_find_devices(HDHOMERUN_DEVICE_TYPE_TUNER, result_list, 64);
+	int count = hdhomerun_discover_find_devices_custom(target_ip, HDHOMERUN_DEVICE_TYPE_TUNER, HDHOMERUN_DEVICE_ID_WILDCARD, result_list, 64);
 	if (count < 0) {
 		fprintf(stderr, "error sending discover request\n");
 		return -1;
@@ -94,32 +136,6 @@
 	return count;
 }
 
-static bool_t parse_device_id_str(const char *s, uint32_t *pdevice_id, uint32_t *pdevice_ip)
-{
-	unsigned long a[4];
-	if (sscanf(s, "%lu.%lu.%lu.%lu", &a[0], &a[1], &a[2], &a[3]) == 4) {
-		*pdevice_id = HDHOMERUN_DEVICE_ID_WILDCARD;
-		*pdevice_ip = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
-		return TRUE;
-	}
-
-	unsigned long device_id_raw;
-	if (sscanf(s, "%lx", &device_id_raw) != 1) {
-		fprintf(stderr, "invalid device id: %s\n", s);
-		return FALSE;
-	}
-
-	uint32_t device_id = (uint32_t)device_id_raw;
-	if (!hdhomerun_discover_validate_device_id(device_id)) {
-		fprintf(stderr, "invalid device id: %s\n", s);
-		return FALSE;
-	}
-
-	*pdevice_id = device_id;
-	*pdevice_ip = 0;
-	return TRUE;
-}
-
 static int cmd_get(const char *item)
 {
 	char *ret_value;
@@ -138,7 +154,7 @@
 	return 1;
 }
 
-static int cmd_set(const char *item, const char *value)
+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) {
@@ -154,117 +170,216 @@
 	return 1;
 }
 
-static int cmd_streaminfo(const char *tuner_str)
+static int cmd_set(const char *item, const char *value)
 {
-	fprintf(stderr, "streaminfo: use \"get /tuner<n>/streaminfo\"\n");
-	return -1;
+	if (strcmp(value, "-") == 0) {
+		char *buffer = NULL;
+		size_t pos = 0;
+
+		while (1) {
+			buffer = (char *)realloc(buffer, pos + 1024);
+			if (!buffer) {
+				fprintf(stderr, "out of memory\n");
+				return -1;
+			}
+
+			int 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 int cmd_scan(const char *tuner_str, const char *start_value)
+static void cmd_scan_printf(FILE *fp, const char *fmt, ...)
 {
-	unsigned int tuner;
-	if (sscanf(tuner_str, "%u", &tuner) != 1) {
-		fprintf(stderr, "invalid tuner number\n");
-		return -1;
-	}
+	va_list ap;
+	va_start(ap, fmt);
 
-	hdhomerun_device_set_tuner(hd, tuner);
+	if (fp) {
+		va_list apc;
+		va_copy(apc, ap);
 
-	char channel_str[64];
-	strncpy(channel_str, start_value, sizeof(channel_str));
-	channel_str[sizeof(channel_str) - 8] = 0;
-
-	char *channel_number_ptr = strrchr(channel_str, ':');
-	if (!channel_number_ptr) {
-		channel_number_ptr = channel_str;
-	} else {
-		channel_number_ptr++;
+		vfprintf(fp, fmt, apc);
+		fflush(fp);
+
+		va_end(apc);
 	}
 
-	unsigned int channel_number = atol(channel_number_ptr);
-	if (channel_number == 0) {
-		fprintf(stderr, "invalid starting channel\n");
+	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;
 	}
 
-	/* Test starting channel. */
-	int ret = hdhomerun_device_set_tuner_channel(hd, channel_str);
-	if (ret < 0) {
-		fprintf(stderr, "communication error sending request to hdhomerun device\n");
+	char *channelmap;
+	if (hdhomerun_device_get_tuner_channelmap(hd, &channelmap) <= 0) {
+		fprintf(stderr, "failed to query channelmap from device\n");
 		return -1;
 	}
-	if (ret == 0) {
-		fprintf(stderr, "invalid starting channel\n");
+
+	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;
 	}
 
-	while (1) {
-		/* Update channel value */
-		sprintf(channel_number_ptr, "%u", channel_number);
+	if (hdhomerun_device_channelscan_init(hd, channelmap_scan_group) <= 0) {
+		fprintf(stderr, "failed to initialize channel scan\n");
+		return -1;
+	}
 
-		/* Set channel. */
-		ret = hdhomerun_device_set_tuner_channel(hd, channel_str);
-		if (ret < 0) {
-			fprintf(stderr, "communication error sending request to hdhomerun device\n");
+	FILE *fp = NULL;
+	if (filename) {
+		fp = fopen(filename, "w");
+		if (!fp) {
+			fprintf(stderr, "unable to create file: %s\n", filename);
 			return -1;
 		}
-		if (ret == 0) {
-			return 0;
+	}
+
+	int ret;
+	while (1) {
+		struct hdhomerun_channelscan_result_t result;
+		ret = hdhomerun_device_channelscan_advance(hd, &result);
+		if (ret <= 0) {
+			break;
 		}
 
-		/* Wait 1.5s for lock (qam auto is the slowest to lock). */
-		usleep(HDHOMERUN_DEVICE_MAX_TUNE_TO_LOCK_TIME * 1000);
+		cmd_scan_printf(fp, "SCANNING: %lu (%s)\n",
+			result.frequency, result.channel_str
+		);
 
-		/* Get status to check for signal. Quality numbers will not be valid yet. */
-		struct hdhomerun_tuner_status_t status;
-		if (hdhomerun_device_get_tuner_status(hd, &status) < 0) {
-			fprintf(stderr, "communication error sending request to hdhomerun device\n");
-			return -1;
+		ret = hdhomerun_device_channelscan_detect(hd, &result);
+		if (ret <= 0) {
+			break;
 		}
 
-		/* If no signal then advance to next channel. */
-		if (status.signal_strength == 0) {
-			printf("%s: no signal\n", channel_str);
-			channel_number++;
-			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
+		);
+
+		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);
 		}
+	}
+
+	if (fp) {
+		fclose(fp);
+	}
+	if (ret < 0) {
+		fprintf(stderr, "communication error sending request to hdhomerun device\n");
+	}
+	return ret;
+}
+
+static void cmd_save_abort(int junk)
+{
+	struct hdhomerun_video_stats_t stats;
+	hdhomerun_device_get_video_stats(hd, &stats);
+	hdhomerun_device_stream_stop(hd);
+	hdhomerun_device_destroy(hd);
+
+	fprintf(stderr, "\n");
+	fprintf(stderr, "-- Video statistics --\n");
+	fprintf(stderr, "%u packets received, %u network errors, %u transport errors, %u sequence errors\n",
+		(unsigned)stats.packet_count, 
+		(unsigned)stats.network_error_count, 
+		(unsigned)stats.transport_error_count, 
+		(unsigned)stats.sequence_error_count);
+
+	exit(0);
+}
 
-		/* Wait for 2s. */
-		usleep(HDHOMERUN_DEVICE_MAX_LOCK_TO_DATA_TIME * 1000);
+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;
+	}
 
-		/* Get status to check quality numbers. */
-		if (hdhomerun_device_get_tuner_status(hd, &status) < 0) {
-			fprintf(stderr, "communication error sending request to hdhomerun device\n");
+	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;
 		}
-		if (status.signal_strength == 0) {
-			printf("%s: no signal\n", channel_str);
-			channel_number++;
-			continue;
-		}
-		printf("%s: ss=%u snq=%u seq=%u\n", channel_str, status.signal_strength, status.signal_to_noise_quality, status.symbol_error_quality);
+	}
+
+	int ret = hdhomerun_device_stream_start(hd);
+	if (ret <= 0) {
+		fprintf(stderr, "unable to start stream\n");
+		return ret;
+	}
+
+	signal(SIGINT, cmd_save_abort);
+	signal(SIGPIPE, cmd_save_abort);
 
-		/* Detect sub channels. */
-		usleep(4 * 1000000);
-		char *streaminfo;
-		if (hdhomerun_device_get_tuner_streaminfo(hd, &streaminfo) <= 0) {
-			channel_number++;
+	struct hdhomerun_video_stats_t stats_old, stats_cur;
+	hdhomerun_device_get_video_stats(hd, &stats_old);
+
+	uint64_t next_progress = getcurrenttime() + 1000;
+	while (1) {
+		usleep(64000);
+
+		size_t actual_size;
+		uint8_t *ptr = hdhomerun_device_stream_recv(hd, VIDEO_DATA_BUFFER_SIZE_1S, &actual_size);
+		if (!ptr) {
 			continue;
 		}
-		while (1) {
-			char *end = strchr(streaminfo, '\n');
-			if (!end) {
-				break;
+
+		if (fp) {
+			if (fwrite(ptr, 1, actual_size, fp) != actual_size) {
+				fprintf(stderr, "error writing output\n");
+				return -1;
 			}
+		}
 
-			*end++ = 0;
-			printf("program %s\n", streaminfo);
+		uint64_t current_time = getcurrenttime();
+		if (current_time >= next_progress) {
+			next_progress = current_time + 1000;
+
+			hdhomerun_device_get_video_stats(hd, &stats_cur);
+
+			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, ".");
+			}
 
-			streaminfo = end;
+			stats_old = stats_cur;
+			fflush(stderr);
 		}
-
-		/* Advance to next channel. */
-		channel_number++;
 	}
 }
 
@@ -276,13 +391,36 @@
 		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;
 	}
+	sleep(2);
+
+	printf("upgrading firmware...\n");
+	sleep(8);
+
+	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");
+			fclose(fp);
+			return -1;
+		}
+
+		sleep(1);
+	}
 
-	printf("upgrade complete\n");
+	printf("upgrade complete - now running firmware %s\n", version_str);
 	return 0;
 }
 
@@ -308,18 +446,22 @@
 		return cmd_set(argv[0], argv[1]);
 	}
 
-	if (contains(cmd, "streaminfo")) {
+	if (contains(cmd, "scan")) {
 		if (argc < 1) {
 			return help();
 		}
-		return cmd_streaminfo(argv[0]);
+		if (argc < 2) {
+			return cmd_scan(argv[0], NULL);
+		} else {
+			return cmd_scan(argv[0], argv[1]);
+		}
 	}
 
-	if (contains(cmd, "scan")) {
+	if (contains(cmd, "save")) {
 		if (argc < 2) {
 			return help();
 		}
-		return cmd_scan(argv[0], argv[1]);
+		return cmd_save(argv[0], argv[1]);
 	}
 
 	if (contains(cmd, "upgrade")) {
@@ -335,10 +477,7 @@
 static int main_internal(int argc, char *argv[])
 {
 #if defined(__WINDOWS__)
-	//Start pthreads
-	pthread_win32_process_attach_np();
-
-	// Start WinSock
+	/* Initialize network socket support. */
 	WORD wVersionRequested = MAKEWORD(2, 0);
 	WSADATA wsaData;
 	WSAStartup(wVersionRequested, &wsaData);
@@ -357,35 +496,36 @@
 		return help();
 	}
 	if (contains(id_str, "discover")) {
-		return discover_print();
-	}
-
-	/* Device ID. */
-	uint32_t device_id, device_ip;
-	if (!parse_device_id_str(id_str, &device_id, &device_ip)) {
-		return -1;
+		if (argc < 1) {
+			return discover_print(NULL);
+		} else {
+			return discover_print(argv[0]);
+		}
 	}
 
 	/* Device object. */
-	hd = hdhomerun_device_create(device_id, device_ip, 0);
+	hd = hdhomerun_device_create_from_str(id_str);
 	if (!hd) {
-		fprintf(stderr, "unable to create device\n");
+		fprintf(stderr, "invalid device id: %s\n", id_str);
 		return -1;
 	}
 
-	/* Connect to device and check firmware version. */
-	int ret = hdhomerun_device_firmware_version_check(hd, 0);
-	if (ret < 0) {
+	/* 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: %08lX\n", (unsigned long)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;
 	}
-	if (ret == 0) {
-		fprintf(stderr, "WARNING: firmware upgrade needed for all operations to function\n");
-	}
 
 	/* Command. */
-	ret = main_cmd(argc, argv);
+	int ret = main_cmd(argc, argv);
 
 	/* Cleanup. */
 	hdhomerun_device_destroy(hd);
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_control.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_control.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_control.c	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_control.c	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_control.c
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,48 +14,81 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 
-#include "hdhomerun_os.h"
-#include "hdhomerun_pkt.h"
-#include "hdhomerun_discover.h"
-#include "hdhomerun_control.h"
+#include "hdhomerun.h"
+
+#define HDHOMERUN_CONTROL_SEND_TIMEOUT 5000
+#define HDHOMERUN_CONTROL_RECV_TIMEOUT 5000
+#define HDHOMERUN_CONTROL_UPGRADE_TIMEOUT 20000
 
 struct hdhomerun_control_sock_t {
-	uint32_t device_id;
-	uint32_t device_ip;
+	uint32_t desired_device_id;
+	uint32_t desired_device_ip;
+	uint32_t actual_device_id;
+	uint32_t actual_device_ip;
 	int sock;
-	uint8_t buffer[16384];
+	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 == -1) {
+		return;
+	}
+
+	close(cs->sock);
+	cs->sock = -1;
+}
+
+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_control_sock_t *cs = (struct hdhomerun_control_sock_t *)malloc(sizeof(struct hdhomerun_control_sock_t));
+	struct hdhomerun_control_sock_t *cs = (struct hdhomerun_control_sock_t *)calloc(1, sizeof(struct hdhomerun_control_sock_t));
 	if (!cs) {
 		return NULL;
 	}
-	
-	cs->device_id = device_id;
-	cs->device_ip = device_ip;
+
 	cs->sock = -1;
+	hdhomerun_control_set_device(cs, device_id, device_ip);
 
 	return cs;
 }
 
 void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs)
 {
-	if (cs->sock != -1) {
-		close(cs->sock);
-	}
+	hdhomerun_control_close_sock(cs);
 	free(cs);
 }
 
-static void hdhomerun_control_close_sock(struct hdhomerun_control_sock_t *cs)
+void hdhomerun_control_set_debug(struct hdhomerun_control_sock_t *cs, struct hdhomerun_debug_t *dbg)
 {
-	close(cs->sock);
-	cs->sock = -1;
+	cs->dbg = dbg;
 }
 
 static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs)
@@ -64,33 +97,39 @@
 		return TRUE;
 	}
 
-	/* Find ip address. */
-	uint32_t device_ip = cs->device_ip;
-	if (device_ip == 0) {
-		struct hdhomerun_discover_device_t result;
-		if (hdhomerun_discover_find_device(cs->device_id, &result) <= 0) {
-			return FALSE;
-		}
-		device_ip = result.ip_addr;
+	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;
+	}
+
+	/* Find device. */
+	struct hdhomerun_discover_device_t result;
+	if (hdhomerun_discover_find_devices_custom(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 = (int)socket(AF_INET, SOCK_STREAM, 0);
 	if (cs->sock == -1) {
+		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to create socket (%d)\n", sock_getlasterror);
 		return FALSE;
 	}
 
 	/* Set timeouts. */
-	setsocktimeout(cs->sock, SOL_SOCKET, SO_SNDTIMEO, 1000);
-	setsocktimeout(cs->sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
+	setsocktimeout(cs->sock, SOL_SOCKET, SO_SNDTIMEO, HDHOMERUN_CONTROL_SEND_TIMEOUT);
+	setsocktimeout(cs->sock, SOL_SOCKET, SO_RCVTIMEO, HDHOMERUN_CONTROL_RECV_TIMEOUT);
 
 	/* Initiate connection. */
 	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(device_ip);
+	sock_addr.sin_addr.s_addr = htonl(cs->actual_device_ip);
 	sock_addr.sin_port = htons(HDHOMERUN_CONTROL_TCP_PORT);
 	if (connect(cs->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
+		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to connect (%d)\n", sock_getlasterror);
 		hdhomerun_control_close_sock(cs);
 		return FALSE;
 	}
@@ -99,125 +138,211 @@
 	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;
 	}
 
 	struct sockaddr_in sock_addr;
 	socklen_t sockaddr_size = sizeof(sock_addr);
 	if (getsockname(cs->sock, (struct sockaddr*)&sock_addr, &sockaddr_size) != 0) {
+		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: getsockname failed (%d)\n", sock_getlasterror);
 		return 0;
 	}
 
 	return ntohl(sock_addr.sin_addr.s_addr);
 }
 
-static int hdhomerun_control_send(struct hdhomerun_control_sock_t *cs, uint8_t *start, uint8_t *end)
+static int hdhomerun_control_send_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt)
 {
-	int length = (int)(end - start);
-	if (send(cs->sock, (char *)start, (int)length, 0) != length) {
+	int length = (int)(tx_pkt->end - tx_pkt->start);
+	if (send(cs->sock, (char *)tx_pkt->start, (int)length, 0) != length) {
+		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_sock: send failed (%d)\n", sock_getlasterror);
+		hdhomerun_control_close_sock(cs);
 		return -1;
 	}
 
-	return length;
+	return 1;
 }
 
-static int hdhomerun_control_recv_sock(struct hdhomerun_control_sock_t *cs, uint8_t *buffer, uint8_t *limit)
+static int hdhomerun_control_recv_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *rx_pkt, uint16_t *ptype, uint64_t recv_timeout)
 {
-	struct timeval t;
-	t.tv_sec = 0;
-	t.tv_usec = 250000;
-
-	fd_set readfds;
-	FD_ZERO(&readfds);
-	FD_SET(cs->sock, &readfds);
+	uint64_t stop_time = getcurrenttime() + recv_timeout;
+	hdhomerun_pkt_reset(rx_pkt);
 
-	if (select(cs->sock+1, &readfds, NULL, NULL, &t) < 0) {
-		return -1;
-	}
+	while (getcurrenttime() < stop_time) {
+		struct timeval t;
+		t.tv_sec = 0;
+		t.tv_usec = 250000;
+	
+		fd_set readfds;
+		FD_ZERO(&readfds);
+		FD_SET(cs->sock, &readfds);
+	
+		if (select(cs->sock+1, &readfds, NULL, NULL, &t) < 0) {
+			hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: select failed (%d)\n", sock_getlasterror);
+			hdhomerun_control_close_sock(cs);
+			return -1;
+		}
+	
+		if (!FD_ISSET(cs->sock, &readfds)) {
+			continue;
+		}
+	
+		int rx_length = recv(cs->sock, (char *)rx_pkt->end, (int)(rx_pkt->limit - rx_pkt->end), 0);
+		if (rx_length <= 0) {
+			hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: recv failed (%d)\n", sock_getlasterror);
+			hdhomerun_control_close_sock(cs);
+			return -1;
+		}
+		rx_pkt->end += rx_length;
 
-	if (!FD_ISSET(cs->sock, &readfds)) {
-		return 0;
-	}
+		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 -1;
+		}
+		if (ret == 0) {
+			continue;
+		}
 
-	int length = recv(cs->sock, (char *)buffer, (int)(limit - buffer), 0);
-	if (length <= 0) {
-		return -1;
+		return 1;
 	}
 
-	return length;
+	hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: timeout\n");
+	hdhomerun_control_close_sock(cs);
+	return -1;
 }
 
-static int hdhomerun_control_recv(struct hdhomerun_control_sock_t *cs, uint8_t *buffer, uint8_t *limit)
+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)
 {
-	uint64_t timeout = getcurrenttime() + 1000;
-	uint8_t *ptr = buffer;
+	hdhomerun_pkt_seal_frame(tx_pkt, type);
 
-	while (getcurrenttime() < timeout) {
-		int length = hdhomerun_control_recv_sock(cs, ptr, limit);
-		if (length < 0) {
-			return -1;
+	int i;
+	for (i = 0; i < 2; i++) {
+		if (cs->sock == -1) {
+			if (!hdhomerun_control_connect_sock(cs)) {
+				hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: connect failed\n");
+				return -1;
+			}
 		}
-		if (length == 0) {
+
+		if (hdhomerun_control_send_sock(cs, tx_pkt) < 0) {
 			continue;
 		}
-		ptr += length;
+		if (!rx_pkt) {
+			return 1;
+		}
 
-		if (buffer + HDHOMERUN_MIN_PEEK_LENGTH > limit) {
+		uint16_t rsp_type;
+		if (hdhomerun_control_recv_sock(cs, rx_pkt, &rsp_type, recv_timeout) < 0) {
 			continue;
 		}
-
-		length = (int)hdhomerun_peek_packet_length(buffer);
-		if (buffer + length > limit) {
+		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 length;
+		return 1;
 	}
 
+	hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: failed\n");
 	return -1;
 }
 
-static int hdhomerun_control_get_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror)
+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)
 {
-	/* Send request. */
-	uint8_t *ptr = cs->buffer;
-	hdhomerun_write_get_set_request(&ptr, name, value);
-	if (hdhomerun_control_send(cs, cs->buffer, ptr) < 0) {
+	struct hdhomerun_pkt_t *tx_pkt = &cs->tx_pkt;
+	struct hdhomerun_pkt_t *rx_pkt = &cs->rx_pkt;
+
+	/* Request. */
+	hdhomerun_pkt_reset(tx_pkt);
+
+	int name_len = (int)strlen(name) + 1;
+	if (tx_pkt->end + 3 + name_len > tx_pkt->limit) {
 		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, (void *)name, name_len);
 
-	/* Receive response. */
-	int length = hdhomerun_control_recv(cs, cs->buffer, cs->buffer + sizeof(cs->buffer));
-	if (length <= 0) {
-		return -1;
+	if (value) {
+		int value_len = (int)strlen(value) + 1;
+		if (tx_pkt->end + 3 + value_len > tx_pkt->limit) {
+			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, (void *)value, value_len);
 	}
 
-	/* Parse response. */
-	ptr = cs->buffer;
-	uint8_t *end = ptr + length;
-	int type = hdhomerun_process_packet(&ptr, &end);
-	if (type < 0) {
-		return -1;
+	if (lockkey != 0) {
+		if (tx_pkt->end + 6 > tx_pkt->limit) {
+			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);
 	}
-	if (type != HDHOMERUN_TYPE_GETSET_RPY) {
+
+	/* Send/Recv. */
+	if (hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, HDHOMERUN_TYPE_GETSET_REQ, HDHOMERUN_CONTROL_RECV_TIMEOUT) < 0) {
 		return -1;
 	}
 
-	while (ptr < end) {
+	/* Response. */
+	while (1) {
 		uint8_t tag;
 		size_t len;
-		uint8_t *val;
-		if (hdhomerun_read_tlv(&ptr, end, &tag, &len, &val) < 0) {
+		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 *)val;
-				val[len] = 0;
+				*pvalue = (char *)rx_pkt->pos;
+				rx_pkt->pos[len] = 0;
 			}
 			if (perror) {
 				*perror = NULL;
@@ -229,55 +354,41 @@
 				*pvalue = NULL;
 			}
 			if (perror) {
-				*perror = (char *)val;
-				val[len] = 0;
+				*perror = (char *)rx_pkt->pos;
+				rx_pkt->pos[len] = 0;
 			}
 			return 0;
 		}
+
+		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)
 {
-	if (!hdhomerun_control_connect_sock(cs)) {
-		return -1;
-	}
-
-	int ret = hdhomerun_control_get_set(cs, name, NULL, pvalue, perror);
-	if (ret < 0) {
-		hdhomerun_control_close_sock(cs);
-		return -1;
-	}
-
-	return ret;
+	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)
 {
-	if (!hdhomerun_control_connect_sock(cs)) {
-		return -1;
-	}
-
-	int ret = hdhomerun_control_get_set(cs, name, value, pvalue, perror);
-	if (ret < 0) {
-		hdhomerun_control_close_sock(cs);
-		return -1;
-	}
+	return hdhomerun_control_get_set(cs, name, value, 0, pvalue, perror);
+}
 
-	return ret;
+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)
 {
-	if (!hdhomerun_control_connect_sock(cs)) {
-		return -1;
-	}
-
+	struct hdhomerun_pkt_t *tx_pkt = &cs->tx_pkt;
+	struct hdhomerun_pkt_t *rx_pkt = &cs->rx_pkt;
 	uint32_t sequence = 0;
-	uint8_t *ptr;
 
+	/* Upload. */
 	while (1) {
 		uint8_t data[256];
 		size_t length = fread(data, 1, 256, upgrade_file);
@@ -285,10 +396,11 @@
 			break;
 		}
 
-		ptr = cs->buffer;
-		hdhomerun_write_upgrade_request(&ptr, sequence, data, length);
-		if (hdhomerun_control_send(cs, cs->buffer, ptr) < 0) {
-			hdhomerun_control_close_sock(cs);
+		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) {
 			return -1;
 		}
 
@@ -300,12 +412,33 @@
 		return 0;
 	}
 
-	ptr = cs->buffer;
-	hdhomerun_write_upgrade_request(&ptr, 0xFFFFFFFF, NULL, 0);
-	if (hdhomerun_control_send(cs, cs->buffer, ptr) < 0) {
-		hdhomerun_control_close_sock(cs);
+	/* 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) {
 		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:
+			return 0;
+
+		default:
+			break;
+		}
+
+		rx_pkt->pos = next;
+	}
+
 	return 1;
 }
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_control.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_control.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_control.h	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_control.h	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_control.h
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,8 +14,20 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 #ifdef __cplusplus
 extern "C" {
@@ -36,8 +48,20 @@
  *
  * When no longer needed, the socket should be destroyed by calling hdhomerun_control_destroy.
  */
-extern struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip);
-extern void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs);
+extern LIBTYPE struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip);
+extern LIBTYPE 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 LIBTYPE uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs);
+extern LIBTYPE uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs);
+extern LIBTYPE uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs);
+extern LIBTYPE uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs);
+
+extern LIBTYPE 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.
@@ -46,7 +70,12 @@
  *
  * Returns 32-bit IP address with native endianness, or 0 on error.
  */
-extern uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs);
+extern LIBTYPE uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs);
+
+/*
+ * Low-level communication.
+ */
+extern LIBTYPE 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.
@@ -65,8 +94,9 @@
  * Returns 0 if the operation was rejected (pvalue NULL, perror set).
  * Returns -1 if a communication error occurs.
  */
-extern int hdhomerun_control_get(struct hdhomerun_control_sock_t *cs, const char *name, char **pvalue, char **perror);
-extern int hdhomerun_control_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror);
+extern LIBTYPE int hdhomerun_control_get(struct hdhomerun_control_sock_t *cs, const char *name, char **pvalue, char **perror);
+extern LIBTYPE int hdhomerun_control_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror);
+extern LIBTYPE 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.
@@ -77,7 +107,12 @@
  * Returns 0 if the upload was rejected.
  * Returns -1 if an error occurs.
  */
-extern int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade_file);
+extern LIBTYPE int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade_file);
+
+/*
+ * Debug logging.
+ */
+extern LIBTYPE void hdhomerun_control_set_debug(struct hdhomerun_control_sock_t *cs, struct hdhomerun_debug_t *dbg);
 
 #ifdef __cplusplus
 }
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_debug.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_debug.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_debug.c	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_debug.c	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,464 @@
+/*
+ * hdhomerun_debug.c
+ *
+ * Copyright Â© 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+/*
+ * 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
+
+struct hdhomerun_debug_message_t
+{
+	struct hdhomerun_debug_message_t *next;
+	struct hdhomerun_debug_message_t *prev;
+	char buffer[2048];
+};
+
+struct hdhomerun_debug_t
+{
+	pthread_t thread;
+	volatile bool_t enabled;
+	volatile bool_t terminate;
+	char *prefix;
+
+	pthread_mutex_t print_lock;
+	pthread_mutex_t queue_lock;
+	pthread_mutex_t send_lock;
+
+	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;
+	int sock;
+};
+
+static THREAD_FUNC_PREFIX 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;
+	}
+
+	dbg->sock = -1;
+
+	pthread_mutex_init(&dbg->print_lock, NULL);
+	pthread_mutex_init(&dbg->queue_lock, NULL);
+	pthread_mutex_init(&dbg->send_lock, NULL);
+
+	if (pthread_create(&dbg->thread, NULL, &hdhomerun_debug_thread_execute, dbg) != 0) {
+		free(dbg);
+		return NULL;
+	}
+
+	return dbg;
+}
+
+/* Send lock held by caller */
+static void hdhomerun_debug_close_file(struct hdhomerun_debug_t *dbg)
+{
+	if (!dbg->file_fp) {
+		return;
+	}
+
+	fclose(dbg->file_fp);
+	dbg->file_fp = NULL;
+}
+
+/* Send lock held by caller */
+static void hdhomerun_debug_close_sock(struct hdhomerun_debug_t *dbg)
+{
+	if (dbg->sock == -1) {
+		return;
+	}
+
+	close(dbg->sock);
+	dbg->sock = -1;
+}
+
+void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg)
+{
+	dbg->terminate = TRUE;
+	pthread_join(dbg->thread, NULL);
+
+	hdhomerun_debug_close_file(dbg);
+	hdhomerun_debug_close_sock(dbg);
+
+	if (dbg->prefix) {
+		free(dbg->prefix);
+	}
+
+	free(dbg);
+}
+
+void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix)
+{
+	pthread_mutex_lock(&dbg->print_lock);
+
+	if (dbg->prefix) {
+		free(dbg->prefix);
+		dbg->prefix = NULL;
+	}
+
+	if (prefix) {
+		dbg->prefix = strdup(prefix);
+	}
+
+	pthread_mutex_unlock(&dbg->print_lock);
+}
+
+void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename)
+{
+	pthread_mutex_lock(&dbg->send_lock);
+
+	if (!filename && !dbg->file_name) {
+		pthread_mutex_unlock(&dbg->send_lock);
+		return;
+	}
+	if (filename && dbg->file_name) {
+		if (strcmp(filename, dbg->file_name) == 0) {
+			pthread_mutex_unlock(&dbg->send_lock);
+			return;
+		}
+	}
+
+	hdhomerun_debug_close_file(dbg);
+	hdhomerun_debug_close_sock(dbg);
+
+	if (dbg->file_name) {
+		free(dbg->file_name);
+		dbg->file_name = NULL;
+	}
+	if (filename) {
+		dbg->file_name = strdup(filename);
+	}
+
+	pthread_mutex_unlock(&dbg->send_lock);
+}
+
+void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg)
+{
+	pthread_mutex_lock(&dbg->send_lock);
+
+	dbg->enabled = TRUE;
+
+	pthread_mutex_unlock(&dbg->send_lock);
+}
+
+void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg)
+{
+	pthread_mutex_lock(&dbg->send_lock);
+
+	dbg->enabled = FALSE;
+	hdhomerun_debug_close_file(dbg);
+	hdhomerun_debug_close_sock(dbg);
+
+	pthread_mutex_unlock(&dbg->send_lock);
+}
+
+bool_t 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)
+{
+	timeout = getcurrenttime() + timeout;
+
+	while (getcurrenttime() < timeout) {
+		pthread_mutex_lock(&dbg->queue_lock);
+		struct hdhomerun_debug_message_t *message = dbg->queue_tail;
+		pthread_mutex_unlock(&dbg->queue_lock);
+
+		if (!message) {
+			return;
+		}
+
+		usleep(10*1000);
+	}
+}
+
+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;
+	}
+	if (!dbg->enabled) {
+		return;
+	}
+
+	struct hdhomerun_debug_message_t *message = (struct hdhomerun_debug_message_t *)malloc(sizeof(struct hdhomerun_debug_message_t));
+	if (!message) {
+		return;
+	}
+
+	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(&current_time));
+	if (ptr > end) {
+		ptr = end;
+	}
+
+	/*
+	 * Debug prefix.
+	 */
+	pthread_mutex_lock(&dbg->print_lock);
+
+	if (dbg->prefix) {
+		int len = snprintf(ptr, end - ptr, "%s ", dbg->prefix);
+		len = (len <= 0) ? 0 : len;
+		ptr += len;
+		if (ptr > end) {
+			ptr = end;
+		}
+	}
+
+	pthread_mutex_unlock(&dbg->print_lock);
+
+	/*
+	 * Message text.
+	 */
+	int len = vsnprintf(ptr, end - ptr, fmt, args);
+	len = (len < 0) ? 0 : len; /* len does not include null */
+	ptr += len;
+	if (ptr > end) {
+		ptr = end;
+	}
+
+	/*
+	 * Force newline.
+	 */
+	if ((ptr[-1] != '\n') && (ptr + 1 <= end)) {
+		*ptr++ = '\n';
+	}
+
+	/*
+	 * Force NULL.
+	 */
+	if (ptr + 1 > end) {
+		ptr = end - 1;
+	}
+	*ptr++ = 0;
+
+	/*
+	 * Enqueue.
+	 */
+	pthread_mutex_lock(&dbg->queue_lock);
+
+	message->prev = NULL;
+	message->next = dbg->queue_head;
+	dbg->queue_head = message;
+	if (message->next) {
+		message->next->prev = message;
+	} else {
+		dbg->queue_tail = message;
+	}
+	dbg->queue_depth++;
+
+	pthread_mutex_unlock(&dbg->queue_lock);
+}
+
+/* Send lock held by caller */
+static bool_t 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 + 60*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 */
+#if defined(__CYGWIN__)
+static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
+{
+	return TRUE;
+}
+#else
+static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
+{
+	if (dbg->sock == -1) {
+		uint64_t current_time = getcurrenttime();
+		if (current_time < dbg->connect_delay) {
+			return FALSE;
+		}
+		dbg->connect_delay = current_time + 60*1000;
+
+		dbg->sock = (int)socket(AF_INET, SOCK_STREAM, 0);
+		if (dbg->sock == -1) {
+			return FALSE;
+		}
+
+		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(HDHOMERUN_DEBUG_HOST, HDHOMERUN_DEBUG_PORT, &hints, &sock_info) != 0) {
+			hdhomerun_debug_close_sock(dbg);
+			return FALSE;
+		}
+		if (connect(dbg->sock, sock_info->ai_addr, (int)sock_info->ai_addrlen) != 0) {
+			freeaddrinfo(sock_info);
+			hdhomerun_debug_close_sock(dbg);
+			return FALSE;
+		}
+		freeaddrinfo(sock_info);
+	}
+
+	size_t length = strlen(message->buffer);
+	if (send(dbg->sock, (char *)message->buffer, (int)length, 0) != length) {
+		hdhomerun_debug_close_sock(dbg);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+#endif
+
+static bool_t hdhomerun_debug_output_message(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
+{
+	pthread_mutex_lock(&dbg->send_lock);
+
+	if (!dbg->enabled) {
+		pthread_mutex_unlock(&dbg->send_lock);
+		return TRUE;
+	}
+
+	bool_t ret;
+	if (dbg->file_name) {
+		ret = hdhomerun_debug_output_message_file(dbg, message);
+	} else {
+		ret = hdhomerun_debug_output_message_sock(dbg, message);
+	}
+
+	pthread_mutex_unlock(&dbg->send_lock);
+	return ret;
+}
+
+static void hdhomerun_debug_pop_and_free_message(struct hdhomerun_debug_t *dbg)
+{
+	pthread_mutex_lock(&dbg->queue_lock);
+
+	struct hdhomerun_debug_message_t *message = dbg->queue_tail;
+	dbg->queue_tail = message->prev;
+	if (message->prev) {
+		message->prev->next = NULL;
+	} else {
+		dbg->queue_head = NULL;
+	}
+	dbg->queue_depth--;
+
+	pthread_mutex_unlock(&dbg->queue_lock);
+
+	free(message);
+}
+
+static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg)
+{
+	struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)arg;
+
+	while (!dbg->terminate) {
+
+		pthread_mutex_lock(&dbg->queue_lock);
+		struct hdhomerun_debug_message_t *message = dbg->queue_tail;
+		uint32_t queue_depth = dbg->queue_depth;
+		pthread_mutex_unlock(&dbg->queue_lock);
+
+		if (!message) {
+			sleep(1);
+			continue;
+		}
+
+		if (queue_depth > 256) {
+			hdhomerun_debug_pop_and_free_message(dbg);
+			continue;
+		}
+
+		if (!hdhomerun_debug_output_message(dbg, message)) {
+			sleep(1);
+			continue;
+		}
+
+		hdhomerun_debug_pop_and_free_message(dbg);
+	}
+
+	return 0;
+}
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_debug.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_debug.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_debug.h	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_debug.h	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,63 @@
+/*
+ * hdhomerun_debug.h
+ *
+ * Copyright Â© 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+/*
+ * 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 LIBTYPE struct hdhomerun_debug_t *hdhomerun_debug_create(void);
+extern LIBTYPE void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg);
+
+extern LIBTYPE void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix);
+extern LIBTYPE void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename);
+extern LIBTYPE void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg);
+extern LIBTYPE void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg);
+extern LIBTYPE bool_t hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg);
+
+extern LIBTYPE void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout);
+
+extern LIBTYPE void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...);
+extern LIBTYPE void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_list args);
+
+#ifdef __cplusplus
+}
+#endif
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_device.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_device.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_device.c	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_device.c	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
- * hdhomerun_record.c
+ * hdhomerun_device.c
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,23 +14,56 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 
-#include "hdhomerun_os.h"
-#include "hdhomerun_pkt.h"
-#include "hdhomerun_control.h"
-#include "hdhomerun_video.h"
-#include "hdhomerun_device.h"
+#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;
 	unsigned int tuner;
-	char result_buffer[1024];
+	uint32_t lockkey;
+	char name[32];
+	char model[32];
 };
 
+static void hdhomerun_device_set_update(struct hdhomerun_device_t *hd)
+{
+	/* Clear cached information. */
+	*hd->model = 0;
+
+	/* New name. */
+	sprintf(hd->name, "%08lX-%u", (unsigned long)hdhomerun_control_get_device_id(hd->cs), hd->tuner);
+}
+
+void hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip)
+{
+	hdhomerun_control_set_device(hd->cs, device_id, device_ip);
+	hdhomerun_device_set_update(hd);
+}
+
+void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner)
+{
+	hd->tuner = tuner;
+	hdhomerun_device_set_update(hd);
+}
+
 struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t device_ip, unsigned int tuner)
 {
 	struct hdhomerun_device_t *hd = (struct hdhomerun_device_t *)calloc(1, sizeof(struct hdhomerun_device_t));
@@ -38,19 +71,24 @@
 		return NULL;
 	}
 
-	hd->tuner = tuner;
-
-	hd->cs = hdhomerun_control_create(device_id, device_ip);
+	hd->cs = hdhomerun_control_create(0, 0);
 	if (!hd->cs) {
 		free(hd);
 		return NULL;
 	}
 
+	hdhomerun_device_set_device(hd, device_id, device_ip);
+	hdhomerun_device_set_tuner(hd, tuner);
+
 	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);
 	}
@@ -60,9 +98,157 @@
 	free(hd);
 }
 
-void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner)
+static bool_t is_hex_char(char c)
 {
-	hd->tuner = tuner;
+	if ((c >= '0') && (c <= '9')) {
+		return TRUE;
+	}
+	if ((c >= 'A') && (c <= 'F')) {
+		return TRUE;
+	}
+	if ((c >= 'a') && (c <= 'f')) {
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static struct hdhomerun_device_t *hdhomerun_device_create_from_str_device_id(const char *device_str)
+{
+	int i;
+	const char *ptr = device_str;
+	for (i = 0; i < 8; i++) {
+		if (!is_hex_char(*ptr++)) {
+			return NULL;
+		}
+	}
+
+	if (*ptr == 0) {
+		unsigned long device_id;
+		if (sscanf(device_str, "%lx", &device_id) != 1) {
+			return NULL;
+		}
+		return hdhomerun_device_create((uint32_t)device_id, 0, 0);
+	}
+
+	if (*ptr == '-') {
+		unsigned long device_id;
+		unsigned int tuner;
+		if (sscanf(device_str, "%lx-%u", &device_id, &tuner) != 2) {
+			return NULL;
+		}
+		return hdhomerun_device_create((uint32_t)device_id, 0, tuner);
+	}
+
+	return NULL;
+}
+
+static struct hdhomerun_device_t *hdhomerun_device_create_from_str_ip(const char *device_str)
+{
+	unsigned long a[4];
+	if (sscanf(device_str, "%lu.%lu.%lu.%lu", &a[0], &a[1], &a[2], &a[3]) != 4) {
+		return NULL;
+	}
+
+	unsigned long device_ip = (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0);
+	return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, (uint32_t)device_ip, 0);
+}
+
+static struct hdhomerun_device_t *hdhomerun_device_create_from_str_dns(const char *device_str)
+{
+#if defined(__CYGWIN__)
+	return NULL;
+#else
+	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 device_ip = ntohl(sock_addr->sin_addr.s_addr);
+	freeaddrinfo(sock_info);
+
+	if (device_ip == 0) {
+		return NULL;
+	}
+
+	return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, (uint32_t)device_ip, 0);
+#endif
+}
+
+struct hdhomerun_device_t *hdhomerun_device_create_from_str(const char *device_str)
+{
+	struct hdhomerun_device_t *device = hdhomerun_device_create_from_str_device_id(device_str);
+	if (device) {
+		return device;
+	}
+
+	device = hdhomerun_device_create_from_str_ip(device_str);
+	if (device) {
+		return device;
+	}
+
+	device = hdhomerun_device_create_from_str_dns(device_str);
+	if (device) {
+		return device;
+	}
+
+	return NULL;
+}
+
+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;
+}
+
+void hdhomerun_device_set_debug(struct hdhomerun_device_t *hd, struct hdhomerun_debug_t *dbg)
+{
+	hd->dbg = dbg;
+	hdhomerun_control_set_debug(hd->cs, dbg);
+
+	if (hd->vs) {
+		hdhomerun_video_set_debug(hd->vs, dbg);
+	}
+}
+
+uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd)
+{
+	return hdhomerun_control_get_device_id(hd->cs);
+}
+
+uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd)
+{
+	return hdhomerun_control_get_device_ip(hd->cs);
+}
+
+uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd)
+{
+	return hdhomerun_control_get_device_id_requested(hd->cs);
+}
+
+uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd)
+{
+	return hdhomerun_control_get_device_ip_requested(hd->cs);
+}
+
+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)
@@ -72,9 +258,16 @@
 
 struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdhomerun_device_t *hd)
 {
+	if (hd->vs) {
+		return hd->vs;
+	}
+
+	hd->vs = hdhomerun_video_create(0, VIDEO_DATA_BUFFER_SIZE_1S * 2);
 	if (!hd->vs) {
-		hd->vs = hdhomerun_video_create(0, VIDEO_DATA_BUFFER_SIZE_1S);
+		return NULL;
 	}
+
+	hdhomerun_video_set_debug(hd->vs, hd->dbg);
 	return hd->vs;
 }
 
@@ -96,7 +289,73 @@
 	return (uint32_t)value;
 }
 
-int hdhomerun_device_get_tuner_status(struct hdhomerun_device_t *hd, struct hdhomerun_tuner_status_t *status)
+static bool_t hdhomerun_device_get_tuner_status_lock_is_bcast(struct hdhomerun_tuner_status_t *status)
+{
+	if (strcmp(status->lock_str, "8vsb") == 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)
 {
 	memset(status, 0, sizeof(struct hdhomerun_tuner_status_t));
 
@@ -109,9 +368,18 @@
 		return ret;
 	}
 
+	if (pstatus_str) {
+		*pstatus_str = status_str;
+	}
+
 	char *channel = strstr(status_str, "ch=");
 	if (channel) {
-		sscanf(channel + 3, "%s", status->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=");
@@ -120,6 +388,16 @@
 	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 >= 45;
+
+	if (strcmp(status->lock_str, "none") != 0) {
+		if (status->lock_str[0] == '(') {
+			status->lock_unsupported = TRUE;
+		} else {
+			status->lock_supported = TRUE;
+		}
+	}
+
 	return 1;
 }
 
@@ -151,26 +429,74 @@
 	return hdhomerun_control_get(hd->cs, name, pfilter, NULL);
 }
 
-int hdhomerun_device_get_tuner_program(struct hdhomerun_device_t *hd, uint16_t *pprogram_number)
+int hdhomerun_device_get_tuner_program(struct hdhomerun_device_t *hd, char **pprogram)
 {
 	char name[32];
 	sprintf(name, "/tuner%u/program", hd->tuner);
+	return hdhomerun_control_get(hd->cs, name, pprogram, NULL);
+}
 
-	char *program_str;
-	int ret = hdhomerun_control_get(hd->cs, name, &program_str, NULL);
+int hdhomerun_device_get_tuner_target(struct hdhomerun_device_t *hd, char **ptarget)
+{
+	char name[32];
+	sprintf(name, "/tuner%u/target", hd->tuner);
+	return hdhomerun_control_get(hd->cs, name, ptarget, NULL);
+}
+
+int hdhomerun_device_get_tuner_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount)
+{
+	char name[32];
+	sprintf(name, "/tuner%u/plotsample", hd->tuner);
+
+	char *result;
+	int ret = hdhomerun_control_get(hd->cs, name, &result, NULL);
 	if (ret <= 0) {
 		return ret;
 	}
 
-	*pprogram_number = (uint16_t)atol(program_str);
+	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 long raw;
+		if (sscanf(result, "%lx", &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_target(struct hdhomerun_device_t *hd, char **ptarget)
+int hdhomerun_device_get_tuner_lockkey_owner(struct hdhomerun_device_t *hd, char **powner)
 {
 	char name[32];
-	sprintf(name, "/tuner%u/target", hd->tuner);
-	return hdhomerun_control_get(hd->cs, name, ptarget, NULL);
+	sprintf(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)
@@ -178,6 +504,11 @@
 	return hdhomerun_control_get(hd->cs, "/ir/target", ptarget, NULL);
 }
 
+int hdhomerun_device_get_lineup_location(struct hdhomerun_device_t *hd, char **plocation)
+{
+	return hdhomerun_control_get(hd->cs, "/lineup/location", plocation, NULL);
+}
+
 int hdhomerun_device_get_version(struct hdhomerun_device_t *hd, char **pversion_str, uint32_t *pversion_num)
 {
 	char *version_str;
@@ -206,44 +537,119 @@
 {
 	char name[32];
 	sprintf(name, "/tuner%u/channel", hd->tuner);
-	return hdhomerun_control_set(hd->cs, name, channel, NULL, NULL);
+	return hdhomerun_control_set_with_lockkey(hd->cs, name, channel, hd->lockkey, NULL, NULL);
 }
 
 int hdhomerun_device_set_tuner_channelmap(struct hdhomerun_device_t *hd, const char *channelmap)
 {
 	char name[32];
 	sprintf(name, "/tuner%u/channelmap", hd->tuner);
-	return hdhomerun_control_set(hd->cs, name, channelmap, NULL, NULL);
+	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)
 {
 	char name[32];
 	sprintf(name, "/tuner%u/filter", hd->tuner);
-	return hdhomerun_control_set(hd->cs, name, filter, NULL, NULL);
+	return hdhomerun_control_set_with_lockkey(hd->cs, name, filter, hd->lockkey, NULL, NULL);
+}
+
+static int hdhomerun_device_set_tuner_filter_by_array_append(char **pptr, char *end, uint16_t range_begin, uint16_t range_end)
+{
+	char *ptr = *pptr;
+
+	size_t available = end - ptr;
+	size_t required;
+
+	if (range_begin == range_end) {
+		required = snprintf(ptr, available, "0x%04x ", range_begin) + 1;
+	} else {
+		required = snprintf(ptr, available, "0x%04x-0x%04x ", range_begin, range_end) + 1;
+	}
+
+	if (required > available) {
+		return FALSE;
+	}
+
+	*pptr = strchr(ptr, 0);
+	return TRUE;
 }
 
-int hdhomerun_device_set_tuner_program(struct hdhomerun_device_t *hd, uint16_t program_number)
+int hdhomerun_device_set_tuner_filter_by_array(struct hdhomerun_device_t *hd, unsigned char filter_array[0x2000])
 {
-	char name[32], value[32];
+	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;
+			}
+			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;
+		}
+	}
+
+	/* 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)
+{
+	char name[32];
 	sprintf(name, "/tuner%u/program", hd->tuner);
-	sprintf(value, "%u", program_number);
-	return hdhomerun_control_set(hd->cs, name, value, NULL, NULL);
+	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, char *target)
 {
 	char name[32];
 	sprintf(name, "/tuner%u/target", hd->tuner);
-	return hdhomerun_control_set(hd->cs, name, target, NULL, NULL);
+	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)
+int hdhomerun_device_set_tuner_target_to_local_protocol(struct hdhomerun_device_t *hd, const char *protocol)
 {
+	/* Create video socket. */
+	hdhomerun_device_get_video_sock(hd);
+	if (!hd->vs) {
+		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);
-	sprintf(target, "%u.%u.%u.%u:%u",
+	sprintf(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
@@ -252,11 +658,21 @@
 	return hdhomerun_device_set_tuner_target(hd, target);
 }
 
+int hdhomerun_device_set_tuner_target_to_local(struct hdhomerun_device_t *hd)
+{
+	return hdhomerun_device_set_tuner_target_to_local_protocol(hd, HDHOMERUN_TARGET_PROTOCOL_UDP); 
+}
+
 int hdhomerun_device_set_ir_target(struct hdhomerun_device_t *hd, const char *target)
 {
 	return hdhomerun_control_set(hd->cs, "/ir/target", target, NULL, NULL);
 }
 
+int hdhomerun_device_set_lineup_location(struct hdhomerun_device_t *hd, const char *location)
+{
+	return hdhomerun_control_set(hd->cs, "/lineup/location", location, NULL, NULL);
+}
+
 int hdhomerun_device_get_var(struct hdhomerun_device_t *hd, const char *name, char **pvalue, char **perror)
 {
 	return hdhomerun_control_get(hd->cs, name, pvalue, perror);
@@ -267,16 +683,68 @@
 	return hdhomerun_control_set(hd->cs, name, value, pvalue, perror);
 }
 
-int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd)
+int hdhomerun_device_tuner_lockkey_request(struct hdhomerun_device_t *hd)
 {
-	/* Create video socket. */
-	hdhomerun_device_get_video_sock(hd);
-	if (!hd->vs) {
-		return -1;
+	uint32_t new_lockkey = (uint32_t)getcurrenttime();
+
+	char name[32];
+	sprintf(name, "/tuner%u/lockkey", hd->tuner);
+
+	char new_lockkey_str[64];
+	sprintf(new_lockkey_str, "%u", (unsigned int)new_lockkey);
+
+	int ret = hdhomerun_control_set_with_lockkey(hd->cs, name, new_lockkey_str, hd->lockkey, NULL, NULL);
+	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->lockkey == 0) {
+		return 1;
+	}
+
+	char name[32];
+	sprintf(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_wait_for_lock(struct hdhomerun_device_t *hd, struct hdhomerun_tuner_status_t *status)
+{
+	/* Wait for up to 2.5 seconds for lock. */
+	int i;
+	for (i = 0; i < 10; i++) {
+		usleep(250000);
+
+		/* 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;
+		}
+	}
+
+	return 1;
+}
+
+int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd)
+{
 	/* Set target. */
-	int ret = hdhomerun_device_set_tuner_target_to_local(hd);
+	int ret = hdhomerun_device_stream_refresh_target(hd);
 	if (ret <= 0) {
 		return ret;
 	}
@@ -289,16 +757,82 @@
 	return 1;
 }
 
+int hdhomerun_device_stream_refresh_target(struct hdhomerun_device_t *hd)
+{
+	return hdhomerun_device_set_tuner_target_to_local_protocol(hd, HDHOMERUN_TARGET_PROTOCOL_RTP);
+}
+
 uint8_t *hdhomerun_device_stream_recv(struct hdhomerun_device_t *hd, size_t max_size, size_t *pactual_size)
 {
+	if (!hd->vs) {
+		return NULL;
+	}
 	return hdhomerun_video_recv(hd->vs, max_size, pactual_size);
 }
 
+void hdhomerun_device_stream_flush(struct hdhomerun_device_t *hd)
+{
+	hdhomerun_video_flush(hd->vs);
+}
+
 void hdhomerun_device_stream_stop(struct hdhomerun_device_t *hd)
 {
 	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) {
+		return -1;
+	}
+
+	return 1;
+}
+
+int hdhomerun_device_channelscan_advance(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result)
+{
+	if (!hd->scan) {
+		return 0;
+	}
+
+	int ret = channelscan_advance(hd->scan, result);
+	if (ret <= 0) {
+		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) {
+		return 0;
+	}
+
+	int ret = channelscan_detect(hd->scan, result);
+	if (ret <= 0) {
+		channelscan_destroy(hd->scan);
+		hd->scan = NULL;
+	}
+
+	return ret;
+}
+
+uint8_t hdhomerun_device_channelscan_get_progress(struct hdhomerun_device_t *hd)
+{
+	if (!hd->scan) {
+		return 0;
+	}
+
+	return channelscan_get_progress(hd->scan);
+}
+
 int hdhomerun_device_firmware_version_check(struct hdhomerun_device_t *hd, uint32_t features)
 {
 	uint32_t version;
@@ -306,16 +840,74 @@
 		return -1;
 	}
 
-	if (version >= 20061213) {
-		return 1;
+	if (version < 20070219) {
+		return 0;
+	}
+
+	return 1;
+}
+
+const char *hdhomerun_device_get_model_str(struct hdhomerun_device_t *hd)
+{
+	if (*hd->model) {
+		return hd->model;
+	}
+
+	char *model_str;
+	int ret = hdhomerun_control_get(hd->cs, "/sys/model", &model_str, NULL);
+	if (ret < 0) {
+		return NULL;
+	}
+	if (ret == 0) {
+		model_str = "hdhomerun_atsc";
 	}
 
-	return 0;
+	strncpy(hd->model, model_str, sizeof(hd->model) - 1);
+	hd->model[sizeof(hd->model) - 1] = 0;
+
+	return hd->model;
 }
 
 int hdhomerun_device_upgrade(struct hdhomerun_device_t *hd, FILE *upgrade_file)
 {
+	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;
+	}
+
+	char name[32];
+	sprintf(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)
+{
+	hdhomerun_video_get_stats(hd->vs, stats);
+}
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_device.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_device.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_device.h	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_device.h	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_device.h
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,8 +14,20 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 
 #ifdef __cplusplus
@@ -26,16 +38,13 @@
 #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)
 
-struct hdhomerun_device_t;
+#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_tuner_status_t {
-	char channel[32];
-	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;
-};
+#define HDHOMERUN_TARGET_PROTOCOL_UDP "udp"
+#define HDHOMERUN_TARGET_PROTOCOL_RTP "rtp"
 
 /*
  * Create a device object.
@@ -57,10 +66,36 @@
  * 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:
+ *     <device id>
+ *     <device id>-<tuner index>
+ *     <ip address>
+ * 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 index>
+ *     /tuner<tuner index>
+ */
+extern LIBTYPE struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t device_ip, unsigned int tuner);
+extern LIBTYPE struct hdhomerun_device_t *hdhomerun_device_create_from_str(const char *device_str);
+extern LIBTYPE void hdhomerun_device_destroy(struct hdhomerun_device_t *hd);
+
+/*
+ * Get the device id, ip, or tuner of the device instance.
  */
-extern struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t device_ip, unsigned int tuner);
-extern void hdhomerun_device_destroy(struct hdhomerun_device_t *hd);
-extern void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner);
+extern LIBTYPE uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd);
+extern LIBTYPE uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd);
+extern LIBTYPE uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd);
+extern LIBTYPE uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd);
+extern LIBTYPE unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd);
+
+extern LIBTYPE void hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip);
+extern LIBTYPE void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner);
+extern LIBTYPE 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.
@@ -69,7 +104,7 @@
  *
  * Returns 32-bit IP address with native endianness, or 0 on error.
  */
-extern uint32_t hdhomerun_device_get_local_machine_addr(struct hdhomerun_device_t *hd);
+extern LIBTYPE uint32_t hdhomerun_device_get_local_machine_addr(struct hdhomerun_device_t *hd);
 
 /*
  * Get operations.
@@ -82,15 +117,24 @@
  * Returns 0 if the operation was rejected.
  * Returns -1 if a communication error occurred.
  */
-extern int hdhomerun_device_get_tuner_status(struct hdhomerun_device_t *hd, struct hdhomerun_tuner_status_t *status);
-extern int hdhomerun_device_get_tuner_streaminfo(struct hdhomerun_device_t *hd, char **pstreaminfo);
-extern int hdhomerun_device_get_tuner_channel(struct hdhomerun_device_t *hd, char **pchannel);
-extern int hdhomerun_device_get_tuner_channelmap(struct hdhomerun_device_t *hd, char **pchannelmap);
-extern int hdhomerun_device_get_tuner_filter(struct hdhomerun_device_t *hd, char **pfilter);
-extern int hdhomerun_device_get_tuner_program(struct hdhomerun_device_t *hd, uint16_t *pprogram_number);
-extern int hdhomerun_device_get_tuner_target(struct hdhomerun_device_t *hd, char **ptarget);
-extern int hdhomerun_device_get_ir_target(struct hdhomerun_device_t *hd, char **ptarget);
-extern int hdhomerun_device_get_version(struct hdhomerun_device_t *hd, char **pversion_str, uint32_t *pversion_num);
+extern LIBTYPE int hdhomerun_device_get_tuner_status(struct hdhomerun_device_t *hd, char **pstatus_str, struct hdhomerun_tuner_status_t *status);
+extern LIBTYPE int hdhomerun_device_get_tuner_streaminfo(struct hdhomerun_device_t *hd, char **pstreaminfo);
+extern LIBTYPE int hdhomerun_device_get_tuner_channel(struct hdhomerun_device_t *hd, char **pchannel);
+extern LIBTYPE int hdhomerun_device_get_tuner_channelmap(struct hdhomerun_device_t *hd, char **pchannelmap);
+extern LIBTYPE int hdhomerun_device_get_tuner_filter(struct hdhomerun_device_t *hd, char **pfilter);
+extern LIBTYPE int hdhomerun_device_get_tuner_program(struct hdhomerun_device_t *hd, char **pprogram);
+extern LIBTYPE int hdhomerun_device_get_tuner_target(struct hdhomerun_device_t *hd, char **ptarget);
+extern LIBTYPE int hdhomerun_device_get_tuner_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount);
+extern LIBTYPE int hdhomerun_device_get_tuner_lockkey_owner(struct hdhomerun_device_t *hd, char **powner);
+extern LIBTYPE int hdhomerun_device_get_ir_target(struct hdhomerun_device_t *hd, char **ptarget);
+extern LIBTYPE int hdhomerun_device_get_lineup_location(struct hdhomerun_device_t *hd, char **plocation);
+extern LIBTYPE int hdhomerun_device_get_version(struct hdhomerun_device_t *hd, char **pversion_str, uint32_t *pversion_num);
+
+extern LIBTYPE uint32_t hdhomerun_device_get_tuner_status_ss_color(struct hdhomerun_tuner_status_t *status);
+extern LIBTYPE uint32_t hdhomerun_device_get_tuner_status_snq_color(struct hdhomerun_tuner_status_t *status);
+extern LIBTYPE uint32_t hdhomerun_device_get_tuner_status_seq_color(struct hdhomerun_tuner_status_t *status);
+
+extern LIBTYPE const char *hdhomerun_device_get_model_str(struct hdhomerun_device_t *hd);
 
 /*
  * Set operations.
@@ -101,12 +145,16 @@
  * Returns 0 if the operation was rejected.
  * Returns -1 if a communication error occurred.
  */
-extern int hdhomerun_device_set_tuner_channel(struct hdhomerun_device_t *hd, const char *channel);
-extern int hdhomerun_device_set_tuner_channelmap(struct hdhomerun_device_t *hd, const char *channelmap);
-extern int hdhomerun_device_set_tuner_filter(struct hdhomerun_device_t *hd, const char *filter);
-extern int hdhomerun_device_set_tuner_program(struct hdhomerun_device_t *hd, uint16_t program_number);
-extern int hdhomerun_device_set_tuner_target(struct hdhomerun_device_t *hd, char *target);
-extern int hdhomerun_device_set_ir_target(struct hdhomerun_device_t *hd, const char *target);
+extern LIBTYPE int hdhomerun_device_set_tuner_channel(struct hdhomerun_device_t *hd, const char *channel);
+extern LIBTYPE int hdhomerun_device_set_tuner_channelmap(struct hdhomerun_device_t *hd, const char *channelmap);
+extern LIBTYPE int hdhomerun_device_set_tuner_filter(struct hdhomerun_device_t *hd, const char *filter);
+extern LIBTYPE int hdhomerun_device_set_tuner_filter_by_array(struct hdhomerun_device_t *hd, unsigned char filter_array[0x2000]);
+extern LIBTYPE int hdhomerun_device_set_tuner_program(struct hdhomerun_device_t *hd, const char *program);
+extern LIBTYPE int hdhomerun_device_set_tuner_target(struct hdhomerun_device_t *hd, char *target);
+extern LIBTYPE int hdhomerun_device_set_tuner_target_to_local_protocol(struct hdhomerun_device_t *hd, const char *protocol);
+extern LIBTYPE int hdhomerun_device_set_tuner_target_to_local(struct hdhomerun_device_t *hd);
+extern LIBTYPE int hdhomerun_device_set_ir_target(struct hdhomerun_device_t *hd, const char *target);
+extern LIBTYPE int hdhomerun_device_set_lineup_location(struct hdhomerun_device_t *hd, const char *location);
 
 /*
  * Get/set a named control variable on the device.
@@ -125,8 +173,36 @@
  * Returns 0 if the operation was rejected (pvalue NULL, perror set).
  * Returns -1 if a communication error occurs.
  */
-extern int hdhomerun_device_get_var(struct hdhomerun_device_t *hd, const char *name, char **pvalue, char **perror);
-extern int hdhomerun_device_set_var(struct hdhomerun_device_t *hd, const char *name, const char *value, char **pvalue, char **perror);
+extern LIBTYPE int hdhomerun_device_get_var(struct hdhomerun_device_t *hd, const char *name, char **pvalue, char **perror);
+extern LIBTYPE 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 LIBTYPE int hdhomerun_device_tuner_lockkey_request(struct hdhomerun_device_t *hd);
+extern LIBTYPE int hdhomerun_device_tuner_lockkey_release(struct hdhomerun_device_t *hd);
+
+/*
+ * 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 LIBTYPE 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.
@@ -144,9 +220,19 @@
  *
  * The hdhomerun_device_stream_stop function tells the device to stop streaming data.
  */
-extern int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd);
-extern uint8_t *hdhomerun_device_stream_recv(struct hdhomerun_device_t *hd, size_t max_size, size_t *pactual_size);
-extern void hdhomerun_device_stream_stop(struct hdhomerun_device_t *hd);
+extern LIBTYPE int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd);
+extern LIBTYPE int hdhomerun_device_stream_refresh_target(struct hdhomerun_device_t *hd);
+extern LIBTYPE uint8_t *hdhomerun_device_stream_recv(struct hdhomerun_device_t *hd, size_t max_size, size_t *pactual_size);
+extern LIBTYPE void hdhomerun_device_stream_flush(struct hdhomerun_device_t *hd);
+extern LIBTYPE void hdhomerun_device_stream_stop(struct hdhomerun_device_t *hd);
+
+/*
+ * Channel scan API.
+ */
+extern LIBTYPE int hdhomerun_device_channelscan_init(struct hdhomerun_device_t *hd, const char *channelmap);
+extern LIBTYPE int hdhomerun_device_channelscan_advance(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result);
+extern LIBTYPE int hdhomerun_device_channelscan_detect(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result);
+extern LIBTYPE uint8_t hdhomerun_device_channelscan_get_progress(struct hdhomerun_device_t *hd);
 
 /*
  * Check that the device is running the recommended firmware.
@@ -157,7 +243,7 @@
  * Returns 0 if th firmware does not meet the minimum requriements for all operations.
  * Returns -1 if an error occurs.
  */
-extern int hdhomerun_device_firmware_version_check(struct hdhomerun_device_t *hd, uint32_t features);
+extern LIBTYPE int hdhomerun_device_firmware_version_check(struct hdhomerun_device_t *hd, uint32_t features);
 
 /*
  * Upload new firmware to the device.
@@ -168,13 +254,21 @@
  * Returns 0 if the upload was rejected.
  * Returns -1 if an error occurs.
  */
-extern int hdhomerun_device_upgrade(struct hdhomerun_device_t *hd, FILE *upgrade_file);
+extern LIBTYPE int hdhomerun_device_upgrade(struct hdhomerun_device_t *hd, FILE *upgrade_file);
 
 /*
  * Low level accessor functions. 
  */
-extern struct hdhomerun_control_sock_t *hdhomerun_device_get_control_sock(struct hdhomerun_device_t *hd);
-extern struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdhomerun_device_t *hd);
+extern LIBTYPE struct hdhomerun_control_sock_t *hdhomerun_device_get_control_sock(struct hdhomerun_device_t *hd);
+extern LIBTYPE struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdhomerun_device_t *hd);
+
+/*
+ * Debug print internal stats.
+ */
+extern LIBTYPE void hdhomerun_device_set_debug(struct hdhomerun_device_t *hd, struct hdhomerun_debug_t *dbg);
+extern LIBTYPE void hdhomerun_device_debug_print_video_stats(struct hdhomerun_device_t *hd);
+
+extern LIBTYPE void hdhomerun_device_get_video_stats(struct hdhomerun_device_t *hd, struct hdhomerun_video_stats_t *stats);
 
 #ifdef __cplusplus
 }
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_dhcp.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_dhcp.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_dhcp.c	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_dhcp.c	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,248 @@
+/*
+ * hdhomerun_dhcp.c
+ *
+ * Copyright Â© 2006-2007 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+#include "hdhomerun.h"
+#include "hdhomerun_dhcp.h"
+
+struct dhcp_hdr_t {
+	uint8_t bootp_message_type;
+	uint8_t hardware_type;
+	uint8_t hardware_address_length;
+	uint8_t hops;
+	uint32_t transaction_id;
+	uint16_t seconds_elapsed;
+	uint16_t bootp_flags;
+	uint32_t client_ip;
+	uint32_t your_ip;
+	uint32_t next_server_ip;
+	uint32_t relay_agent_ip;
+	uint8_t client_mac[16];
+	uint8_t server_host_name[64];
+	uint8_t boot_file_name[128];
+	uint32_t magic_cookie;
+};
+
+struct hdhomerun_dhcp_t {
+	int sock;
+	uint32_t local_address;
+	pthread_t thread;
+	volatile bool_t terminate;
+};
+
+static THREAD_FUNC_PREFIX hdhomerun_dhcp_thread_execute(void *arg);
+
+struct hdhomerun_dhcp_t *hdhomerun_dhcp_create(uint32_t bind_address)
+{
+	if (bind_address != 0) {
+		if ((bind_address & 0xFFFF0000) != 0xA9FE0000) {
+			return NULL;
+		}
+	}
+
+	/* Create socket. */
+	int sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock == -1) {
+		return NULL;
+	}
+
+	/* Set timeout. */
+	setsocktimeout(sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
+
+	/* Allow broadcast. */
+	int sock_opt = 1;
+	setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
+
+	/* Allow reuse. */
+	sock_opt = 1;
+	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));
+
+	/* Bind socket. */
+	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(bind_address);
+	sock_addr.sin_port = htons(67);
+	if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
+		close(sock);
+		return NULL;
+	}
+
+	/* Allocate object. */
+	struct hdhomerun_dhcp_t *dhcp = (struct hdhomerun_dhcp_t *)calloc(1, sizeof(struct hdhomerun_dhcp_t));
+	if (!dhcp) {
+		close(sock);
+		return NULL;
+	}
+
+	dhcp->sock = sock;
+
+	if (bind_address != 0) {
+		dhcp->local_address = bind_address;
+	} else {
+		dhcp->local_address = 0xA9FEFFFF;
+	}
+
+	/* Spawn thread. */
+	if (pthread_create(&dhcp->thread, NULL, &hdhomerun_dhcp_thread_execute, dhcp) != 0) {
+		close(sock);
+		free(dhcp);
+		return NULL;
+	}
+
+	/* Success. */
+	return dhcp;
+}
+
+void hdhomerun_dhcp_destroy(struct hdhomerun_dhcp_t *dhcp)
+{
+	dhcp->terminate = TRUE;
+	pthread_join(dhcp->thread, NULL);
+
+	close(dhcp->sock);
+	free(dhcp);
+}
+
+static void hdhomerun_dhcp_send(struct hdhomerun_dhcp_t *dhcp, uint8_t message_type, struct hdhomerun_pkt_t *pkt)
+{
+	pkt->pos = pkt->start;
+	struct dhcp_hdr_t *hdr = (struct dhcp_hdr_t *)pkt->pos;
+	pkt->pos += sizeof(struct dhcp_hdr_t);
+	pkt->end = pkt->pos;
+
+	uint32_t remote_addr = 0xA9FE0000;
+	remote_addr |= (uint32_t)hdr->client_mac[4] << 8;
+	remote_addr |= (uint32_t)hdr->client_mac[5] << 0;
+	if ((remote_addr == 0xA9FE0000) || (remote_addr == 0xA9FEFFFF)) {
+		remote_addr = 0xA9FE8080;
+	}
+
+	hdr->bootp_message_type = 0x02;
+	hdr->your_ip = htonl(remote_addr);
+	hdr->next_server_ip = htonl(0x00000000);
+
+	hdhomerun_pkt_write_u8(pkt, 53);
+	hdhomerun_pkt_write_u8(pkt, 1);
+	hdhomerun_pkt_write_u8(pkt, message_type);
+
+	hdhomerun_pkt_write_u8(pkt, 54);
+	hdhomerun_pkt_write_u8(pkt, 4);
+	hdhomerun_pkt_write_u32(pkt, dhcp->local_address);
+
+	hdhomerun_pkt_write_u8(pkt, 51);
+	hdhomerun_pkt_write_u8(pkt, 4);
+	hdhomerun_pkt_write_u32(pkt, 7*24*60*60);
+
+	hdhomerun_pkt_write_u8(pkt, 1);
+	hdhomerun_pkt_write_u8(pkt, 4);
+	hdhomerun_pkt_write_u32(pkt, 0xFFFF0000);
+
+	hdhomerun_pkt_write_u8(pkt, 0xFF);
+
+	while (pkt->pos < pkt->start + 300) {
+		hdhomerun_pkt_write_u8(pkt, 0x00);
+	}
+
+	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(0xFFFFFFFF);
+	sock_addr.sin_port = htons(68);
+
+	sendto(dhcp->sock, (char *)pkt->start, (int)(pkt->end - pkt->start), 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
+}
+
+static void hdhomerun_dhcp_recv(struct hdhomerun_dhcp_t *dhcp, struct hdhomerun_pkt_t *pkt)
+{
+	pkt->pos = pkt->start;
+	struct dhcp_hdr_t *hdr = (struct dhcp_hdr_t *)pkt->pos;
+	pkt->pos += sizeof(struct dhcp_hdr_t);
+	if (pkt->pos > pkt->end) {
+		return;
+	}
+
+	if (ntohl(hdr->magic_cookie) != 0x63825363) {
+		return;
+	}
+
+	static uint8_t vendor[3] = {0x00, 0x18, 0xDD};
+	if (memcmp(hdr->client_mac, vendor, 3) != 0) {
+		return;
+	}
+
+	if (pkt->pos + 3 > pkt->end) {
+		return;
+	}
+	if (hdhomerun_pkt_read_u8(pkt) != 53) {
+		return;
+	}
+	if (hdhomerun_pkt_read_u8(pkt) != 1) {
+		return;
+	}
+	uint8_t message_type_val = hdhomerun_pkt_read_u8(pkt);
+
+	switch (message_type_val) {
+	case 0x01:
+		hdhomerun_dhcp_send(dhcp, 0x02, pkt);
+		break;
+	case 0x03:
+		hdhomerun_dhcp_send(dhcp, 0x05, pkt);
+		break;
+	default:
+		return;
+	}
+}
+
+static THREAD_FUNC_PREFIX hdhomerun_dhcp_thread_execute(void *arg)
+{
+	struct hdhomerun_dhcp_t *dhcp = (struct hdhomerun_dhcp_t *)arg;
+	struct hdhomerun_pkt_t pkt_inst;
+
+	while (1) {
+		if (dhcp->terminate) {
+			return NULL;
+		}
+
+		struct hdhomerun_pkt_t *pkt = &pkt_inst;
+		hdhomerun_pkt_reset(pkt);
+
+		int rx_length = recv(dhcp->sock, (char *)pkt->end, (int)(pkt->limit - pkt->end), 0);
+		if (rx_length <= 0) {
+			if (!sock_getlasterror_socktimeout) {
+				sleep(1);
+			}
+			continue;
+		}
+		pkt->end += rx_length;
+
+		hdhomerun_dhcp_recv(dhcp, pkt);
+	}
+}
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_dhcp.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_dhcp.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_dhcp.h	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_dhcp.h	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,44 @@
+/*
+ * hdhomerun_dhcp.h
+ *
+ * Copyright Â© 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct hdhomerun_dhcp_t;
+
+extern LIBTYPE struct hdhomerun_dhcp_t *hdhomerun_dhcp_create(uint32_t bind_address);
+extern LIBTYPE void hdhomerun_dhcp_destroy(struct hdhomerun_dhcp_t *dhcp);
+
+#ifdef __cplusplus
+}
+#endif
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_discover.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_discover.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_discover.c	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_discover.c	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_discover.c
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006-2007 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,206 +14,93 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 
-#include "hdhomerun_os.h"
-#include "hdhomerun_pkt.h"
-#include "hdhomerun_discover.h"
+#include "hdhomerun.h"
 
 #if defined(__CYGWIN__) || defined(__WINDOWS__)
 #include <windows.h>
-#include <iptypes.h>
 #include <iphlpapi.h>
+#define USE_IPHLPAPI 1
+#else
+#include <net/if.h>
+#include <sys/ioctl.h>
+#ifndef _SIZEOF_ADDR_IFREQ
+#define _SIZEOF_ADDR_IFREQ(x) sizeof(x)
+#endif
 #endif
 
-// for OSX
-#include <unistd.h>    // execl, pipe, sysconf
-#include <sys/types.h> // waitpid
-#include <sys/wait.h>  // waitpid
+#define HDHOMERUN_DISOCVER_MAX_SOCK_COUNT 16
 
 struct hdhomerun_discover_sock_t {
 	int sock;
+	uint32_t local_ip;
+	uint32_t subnet_mask;
 };
 
-static struct hdhomerun_discover_sock_t *hdhomerun_discover_create(void)
+struct hdhomerun_discover_t {
+	struct hdhomerun_discover_sock_t socks[HDHOMERUN_DISOCVER_MAX_SOCK_COUNT];
+	unsigned int sock_count;
+	struct hdhomerun_pkt_t tx_pkt;
+	struct hdhomerun_pkt_t rx_pkt;
+};
+
+static bool_t hdhomerun_discover_sock_create(struct hdhomerun_discover_t *ds, uint32_t local_ip, uint32_t subnet_mask)
 {
-	struct hdhomerun_discover_sock_t *ds = (struct hdhomerun_discover_sock_t *)malloc(sizeof(struct hdhomerun_discover_sock_t));
-	if (!ds) {
-		return NULL;
+	if (ds->sock_count >= HDHOMERUN_DISOCVER_MAX_SOCK_COUNT) {
+		return FALSE;
 	}
-	
+
 	/* Create socket. */
-	ds->sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
-	if (ds->sock == -1) {
-		free(ds);
-		return NULL;
+	int sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock == -1) {
+		return FALSE;
 	}
 
 	/* Set timeouts. */
-	setsocktimeout(ds->sock, SOL_SOCKET, SO_SNDTIMEO, 1000);
-	setsocktimeout(ds->sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
+	setsocktimeout(sock, SOL_SOCKET, SO_SNDTIMEO, 1000);
+	setsocktimeout(sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
 
 	/* Allow broadcast. */
 	int sock_opt = 1;
-	setsockopt(ds->sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
+	setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
 
 	/* Bind socket. */
 	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(INADDR_ANY);
+	sock_addr.sin_addr.s_addr = htonl(local_ip);
 	sock_addr.sin_port = htons(0);
-	if (bind(ds->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
-		close(ds->sock);
-		free(ds);
-		return NULL;
+	if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
+		close(sock);
+		return FALSE;
 	}
 
-	/* Success. */
-	return ds;
-}
+	/* Write sock entry. */
+	struct hdhomerun_discover_sock_t *dss = &ds->socks[ds->sock_count++];
+	dss->sock = sock;
+	dss->local_ip = local_ip;
+	dss->subnet_mask = subnet_mask;
 
-static void hdhomerun_discover_destroy(struct hdhomerun_discover_sock_t *ds)
-{
-	close(ds->sock);
-	free(ds);
+	return TRUE;
 }
 
-static int hdhomerun_discover_send_packet(struct hdhomerun_discover_sock_t *ds, uint32_t ip_addr, uint32_t device_type, uint32_t device_id)
-{
-	uint8_t buffer[1024];
-	uint8_t *ptr = buffer;
-	hdhomerun_write_discover_request(&ptr, device_type, device_id);
-
-	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(ip_addr);
-	sock_addr.sin_port = htons(HDHOMERUN_DISCOVER_UDP_PORT);
-
-	int length = (int)(ptr - buffer);
-	if (sendto(ds->sock, (char *)buffer, (int)length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != length) {
-		return -1;
-	}
-
-	return 0;
-}
-
-#if defined(__APPLE__)
-static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, unsigned long device_type, unsigned long device_id)
-{
-    /* printf("Looking for 0x%lx with id 0x%lx\n", device_type, device_id); */
-    int fds[2];
-    if (pipe(fds) < 0)
-    {
-        printf("Pipe Failed\n");
-        return -1;
-    }
-
-    pid_t child = fork();
-    if (child < 0)
-    {
-        printf("Fork Failed\n");
-        return -1;
-    }
-    else if (child == 0)
-    {
-        /* Child */
-        int i = 0;
-
-        /* Attach stdout to pipe */
-        close(1);
-        dup2(fds[1], 1);
-
-        /* Close all open file descriptors except stdout/stderr */
-        for (i = sysconf(_SC_OPEN_MAX) - 1; i > 2; i--)
-            close(i);
-
-        /* Run command */
-        execl("/bin/sh", "sh", "-c", "ifconfig", NULL);
-
-        /* Failed to exec */
-        _exit(1); /* this exit is ok */
-    }
-    else
-    {
-        /* Parent */
-        int send_count = 0;
-        int status;
-        FILE *fp;
-        char line[1024];
-        char adaptor[1024];
-
-        close(fds[1]);
-
-        if (waitpid(child, &status, 0) < 0)
-            return -1;
-
-        if (WEXITSTATUS(status))
-            return -1;
-
-        fp = fdopen(fds[0], "r");
-        while (1)
-        {
-            char *ptr = NULL;
-            int netmask, broadcast;
-            int a,b,c,d;
-
-            if (!fgets(line, sizeof(line) - 1, fp))
-            {
-                break;
-            }
-
-            line[1023] = 0;
-
-            /* find ": flags" */
-            ptr = strnstr(line, ": flags", 1024 - 1);
-            if (ptr >= line)
-            {
-                /* grab adaptor before that */
-                strncpy(adaptor, line, ptr-line);
-                adaptor[ptr-line] = 0;
-            }
-
-            /* find "netmask " */
-            ptr = strnstr(line, "netmask ", 1024 - 1);
-            if (ptr <= line)
-                continue;
-            ptr += strlen("netmask ");
-            sscanf(ptr, "%x", &netmask);
-
-            /* find "broadcast " */
-            ptr = strnstr(ptr, "broadcast ", 1024 - 1);
-            if (ptr <= line)
-                continue;
-            ptr += strlen("broadcast ");
-            sscanf(ptr, "%i.%i.%i.%i", &a, &b, &c, &d);
-            broadcast = a<<24 | b<<16 | c<<8 | d;
-            /*
-            printf("Adaptor: '%s' 0x%08x %i.%i.%i.%i\n",
-                   adaptor, broadcast, a,b,c,d);
-            */
-
-            /* send discover packet this adaptor */
-            if (hdhomerun_discover_send_packet(
-                    ds, broadcast, device_type, device_id) >= 0)
-            {
-                send_count++;
-            }
-        }
-
-        fclose(fp); /* this closes fds[0] as well */
-
-        /* printf("send_count: %i\n\n", send_count); */
-        return (send_count == 0) ? -1 : 0;
-    }
-}
-
-#elif defined(__CYGWIN__) || defined(__WINDOWS__)
-
-static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, uint32_t device_type, uint32_t device_id)
+#if defined(USE_IPHLPAPI)
+static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
 {
 	PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));
 	ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
@@ -222,37 +109,29 @@
 	if (Ret != NO_ERROR) {
 		free(pAdapterInfo);
 		if (Ret != ERROR_BUFFER_OVERFLOW) {
-			return -1;
+			return;
 		}
 		pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); 
 		Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
 		if (Ret != NO_ERROR) {
 			free(pAdapterInfo);
-			return -1;
+			return;
 		}
 	}
 
-	unsigned int send_count = 0;
 	PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
 	while (pAdapter) {
 		IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList;
 		while (pIPAddr) {
-			uint32_t addr = ntohl(inet_addr(pIPAddr->IpAddress.String));
+			uint32_t local_ip = ntohl(inet_addr(pIPAddr->IpAddress.String));
 			uint32_t mask = ntohl(inet_addr(pIPAddr->IpMask.String));
-			
-			uint32_t broadcast = addr | ~mask;
-			if ((broadcast == 0x00000000) || (broadcast == 0xFFFFFFFF)) {
-				pIPAddr = pIPAddr->Next;
-				continue;
-			}
 
-			if (hdhomerun_discover_send_packet(ds, broadcast, device_type, device_id) < 0) {
+			if (local_ip == 0) {
 				pIPAddr = pIPAddr->Next;
 				continue;
 			}
 
-			send_count++;
-
+			hdhomerun_discover_sock_create(ds, local_ip, mask);
 			pIPAddr = pIPAddr->Next;
 		}
 
@@ -260,111 +139,198 @@
 	}
 
 	free(pAdapterInfo);
-
-	if (send_count == 0) {
-		return -1;
-	}
-	return 0;
 }
 
-#elif defined(__linux__)
+#else
 
-static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, uint32_t device_type, uint32_t device_id)
+static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
 {
-	FILE *fp = fopen("/proc/net/route", "r");
-	if (!fp) {
-		return -1;
+	int fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd == -1) {
+		return;
 	}
 
-	unsigned int send_count = 0;
-	while (1) {
-		char line[256];
-		if (!fgets(line, sizeof(line), fp)) {
-			break;
-		}
-		line[255] = 0;
+	struct ifconf ifc;
+	uint8_t buf[8192];
+	ifc.ifc_len = sizeof(buf);
+	ifc.ifc_buf = (char *)buf;
+
+	memset(buf, 0, sizeof(buf));
 
-		uint32_t dest;
-		uint32_t mask;
-		if (sscanf(line, "%*s %x %*x %*x %*d %*d %*d %x", &dest, &mask) != 2) {
+	if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
+		close(fd);
+		return;
+	}
+
+	uint8_t *ptr = (uint8_t *)ifc.ifc_req;
+	uint8_t	*end = (uint8_t *)&ifc.ifc_buf[ifc.ifc_len];
+
+	while (ptr <= end) {
+		struct ifreq *ifr = (struct ifreq *)ptr;
+		ptr += _SIZEOF_ADDR_IFREQ(*ifr);
+
+		if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
 			continue;
 		}
-		dest = ntohl(dest);
-		mask = ntohl(mask);
-		
-		uint32_t broadcast = dest | ~mask;
-
-		if ((broadcast == 0x00000000) || (broadcast == 0xFFFFFFFF)) {
+		struct sockaddr_in *addr_in = (struct sockaddr_in *)&(ifr->ifr_addr);
+		uint32_t local_ip = ntohl(addr_in->sin_addr.s_addr);
+		if (local_ip == 0) {
 			continue;
 		}
 
-		if (hdhomerun_discover_send_packet(ds, broadcast, device_type, device_id) < 0) {
+		if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
 			continue;
 		}
+		struct sockaddr_in *mask_in = (struct sockaddr_in *)&(ifr->ifr_addr);
+		uint32_t mask = ntohl(mask_in->sin_addr.s_addr);
 
-		send_count++;
+		hdhomerun_discover_sock_create(ds, local_ip, mask);
 	}
+}
+#endif
 
-	fclose(fp);
-	if (send_count == 0) {
-		return -1;
+static struct hdhomerun_discover_t *hdhomerun_discover_create(void)
+{
+	struct hdhomerun_discover_t *ds = (struct hdhomerun_discover_t *)calloc(1, sizeof(struct hdhomerun_discover_t));
+	if (!ds) {
+		return NULL;
 	}
-	return 0;
+
+	/* Create a routable socket. */
+	if (!hdhomerun_discover_sock_create(ds, 0, 0)) {
+		free(ds);
+		return NULL;
+	}
+
+	/* Detect & create local sockets. */
+	hdhomerun_discover_sock_detect(ds);
+
+	/* Success. */
+	return ds;
 }
 
-#else
+static 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];
+		close(dss->sock);
+	}
 
-static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, uint32_t device_type, uint32_t device_id)
+	free(ds);
+}
+
+static bool_t hdhomerun_discover_send_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
 {
-	return -1;
+	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);
+
+	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(target_ip);
+	sock_addr.sin_port = htons(HDHOMERUN_DISCOVER_UDP_PORT);
+
+	int length = (int)(tx_pkt->end - tx_pkt->start);
+	if (sendto(dss->sock, (char *)tx_pkt->start, length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != length) {
+		return FALSE;
+	}
+
+	return TRUE;
 }
-#endif
 
-static int hdhomerun_discover_send(struct hdhomerun_discover_sock_t *ds, uint32_t device_type, uint32_t device_id)
+static bool_t hdhomerun_discover_send_wildcard_ip(struct hdhomerun_discover_t *ds, uint32_t device_type, uint32_t device_id)
 {
-	if (hdhomerun_discover_send_internal(ds, device_type, device_id) < 0) {
-		return hdhomerun_discover_send_packet(ds, 0xFFFFFFFF, device_type, device_id);
+	bool_t result = FALSE;
+
+	/*
+	 * Send subnet broadcast using each local ip socket.
+	 * This will work with multiple separate 169.254.x.x interfaces.
+	 */
+	unsigned int i;
+	for (i = 1; i < ds->sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
+		uint32_t target_ip = dss->local_ip | ~dss->subnet_mask;
+		result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
 	}
-	return 0;
+
+	/*
+	 * If no local ip sockets then fall back to sending a global broadcast letting the OS choose the interface.
+	 */
+	if (!result) {
+		struct hdhomerun_discover_sock_t *dss = &ds->socks[0];
+		result = hdhomerun_discover_send_internal(ds, dss, 0xFFFFFFFF, device_type, device_id);
+	}
+
+	return result;
 }
 
-static int hdhomerun_discover_recv(struct hdhomerun_discover_sock_t *ds, struct hdhomerun_discover_device_t *result)
+static bool_t hdhomerun_discover_send_target_ip(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
 {
-	struct timeval t;
-	t.tv_sec = 0;
-	t.tv_usec = 250000;
+	bool_t result = FALSE;
 
-	fd_set readfds;
-	FD_ZERO(&readfds);
-	FD_SET(ds->sock, &readfds);
+	/*
+	 * Send targeted packet from any local ip that is in the same subnet.
+	 * This will work with multiple separate 169.254.x.x interfaces.
+	 */
+	unsigned int i;
+	for (i = 1; i < ds->sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
+		if ((target_ip & dss->subnet_mask) != (dss->local_ip & dss->subnet_mask)) {
+			continue;
+		}
 
-	if (select(ds->sock+1, &readfds, NULL, NULL, &t) < 0) {
-		return -1;
+		result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
 	}
-	if (!FD_ISSET(ds->sock, &readfds)) {
-		return 0;
+
+	/*
+	 * If target IP does not match a local subnet then fall back to letting the OS choose the gateway interface.
+	 */
+	if (!result) {
+		struct hdhomerun_discover_sock_t *dss = &ds->socks[0];
+		result = hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
+	}
+
+	return result;
+}
+
+static bool_t hdhomerun_discover_send(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
+{
+	if (target_ip != 0) {
+		return hdhomerun_discover_send_target_ip(ds, target_ip, device_type, device_id);
 	}
 
-	uint8_t buffer[1024];
+	return hdhomerun_discover_send_wildcard_ip(ds, device_type, device_id);
+}
+
+static int hdhomerun_discover_recv_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, struct hdhomerun_discover_device_t *result)
+{
+	struct hdhomerun_pkt_t *rx_pkt = &ds->rx_pkt;
+	hdhomerun_pkt_reset(rx_pkt);
+
 	struct sockaddr_in sock_addr;
+	memset(&sock_addr, 0, sizeof(sock_addr));
 	socklen_t sockaddr_size = sizeof(sock_addr);
-	int rx_length = recvfrom(ds->sock, (char *)buffer, sizeof(buffer), 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
+
+	int rx_length = recvfrom(dss->sock, (char *)rx_pkt->end, (int)(rx_pkt->limit - rx_pkt->end), 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
 	if (rx_length <= 0) {
 		/* Don't return error - windows machine on VPN can sometimes cause a sock error here but otherwise works. */
 		return 0;
 	}
-	if (rx_length < HDHOMERUN_MIN_PEEK_LENGTH) {
-		return 0;
-	}
+	rx_pkt->end += rx_length;
 
-	size_t length = hdhomerun_peek_packet_length(buffer);
-	if (length > (size_t)rx_length) {
+	uint16_t type;
+	if (hdhomerun_pkt_open_frame(rx_pkt, &type) <= 0) {
 		return 0;
 	}
-
-	uint8_t *ptr = buffer;
-	uint8_t *end = buffer + length;
-	int type = hdhomerun_process_packet(&ptr, &end);
 	if (type != HDHOMERUN_TYPE_DISCOVER_RPY) {
 		return 0;
 	}
@@ -372,11 +338,12 @@
 	result->ip_addr = ntohl(sock_addr.sin_addr.s_addr);
 	result->device_type = 0;
 	result->device_id = 0;
+
 	while (1) {
 		uint8_t tag;
 		size_t len;
-		uint8_t *value;
-		if (hdhomerun_read_tlv(&ptr, end, &tag, &len, &value) < 0) {
+		uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len);
+		if (!next) {
 			break;
 		}
 
@@ -385,28 +352,71 @@
 			if (len != 4) {
 				break;
 			}
-			result->device_type = hdhomerun_read_u32(&value);
+			result->device_type = hdhomerun_pkt_read_u32(rx_pkt);
 			break;
+
 		case HDHOMERUN_TAG_DEVICE_ID:
 			if (len != 4) {
 				break;
 			}
-			result->device_id = hdhomerun_read_u32(&value);
+			result->device_id = hdhomerun_pkt_read_u32(rx_pkt);
 			break;
+
 		default:
 			break;
 		}
+
+		rx_pkt->pos = next;
 	}
 
 	return 1;
 }
 
-static struct hdhomerun_discover_device_t *hdhomerun_discover_find_in_list(struct hdhomerun_discover_device_t result_list[], int count, uint32_t device_id)
+static int hdhomerun_discover_recv(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_device_t *result)
+{
+	struct timeval t;
+	t.tv_sec = 0;
+	t.tv_usec = 250000;
+
+	fd_set readfds;
+	FD_ZERO(&readfds);
+	int max_sock = -1;
+
+	unsigned int i;
+	for (i = 0; i < ds->sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
+		FD_SET(dss->sock, &readfds);
+		if (dss->sock > max_sock) {
+			max_sock = dss->sock;
+		}
+	}
+
+	if (select(max_sock+1, &readfds, NULL, NULL, &t) < 0) {
+		return -1;
+	}
+
+	for (i = 0; i < ds->sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
+		if (!FD_ISSET(dss->sock, &readfds)) {
+			continue;
+		}
+
+		if (hdhomerun_discover_recv_internal(ds, dss, result) <= 0) {
+			continue;
+		}
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static struct hdhomerun_discover_device_t *hdhomerun_discover_find_in_list(struct hdhomerun_discover_device_t result_list[], int count, uint32_t ip_addr)
 {
 	int index;
 	for (index = 0; index < count; index++) {
 		struct hdhomerun_discover_device_t *result = &result_list[index];
-		if (result->device_id == device_id) {
+		if (result->ip_addr == ip_addr) {
 			return result;
 		}
 	}
@@ -414,13 +424,12 @@
 	return NULL;
 }
 
-static int hdhomerun_discover_find_devices_internal(struct hdhomerun_discover_sock_t *ds, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count)
+static int hdhomerun_discover_find_devices_internal(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count)
 {
 	int count = 0;
-
 	int attempt;
 	for (attempt = 0; attempt < 4; attempt++) {
-		if (hdhomerun_discover_send(ds, device_type, device_id) < 0) {
+		if (!hdhomerun_discover_send(ds, target_ip, device_type, device_id)) {
 			return -1;
 		}
 
@@ -433,7 +442,7 @@
 				return -1;
 			}
 			if (ret == 0) {
-				break;
+				continue;
 			}
 
 			/* Filter. */
@@ -449,7 +458,7 @@
 			}
 
 			/* Ensure not already in list. */
-			if (hdhomerun_discover_find_in_list(result_list, count, result->device_id)) {
+			if (hdhomerun_discover_find_in_list(result_list, count, result->ip_addr)) {
 				continue;
 			}
 
@@ -464,27 +473,14 @@
 	return count;
 }
 
-int hdhomerun_discover_find_device(uint32_t device_id, struct hdhomerun_discover_device_t *result)
-{
-	struct hdhomerun_discover_sock_t *ds = hdhomerun_discover_create();
-	if (!ds) {
-		return -1;
-	}
-
-	int ret = hdhomerun_discover_find_devices_internal(ds, HDHOMERUN_DEVICE_TYPE_WILDCARD, device_id, result, 1);
-
-	hdhomerun_discover_destroy(ds);
-	return ret;
-}
-
-int hdhomerun_discover_find_devices(uint32_t device_type, struct hdhomerun_discover_device_t result_list[], int max_count)
+int hdhomerun_discover_find_devices_custom(uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count)
 {
-	struct hdhomerun_discover_sock_t *ds = hdhomerun_discover_create();
+	struct hdhomerun_discover_t *ds = hdhomerun_discover_create();
 	if (!ds) {
 		return -1;
 	}
 
-	int ret = hdhomerun_discover_find_devices_internal(ds, device_type, HDHOMERUN_DEVICE_ID_WILDCARD, result_list, max_count);
+	int ret = hdhomerun_discover_find_devices_internal(ds, target_ip, device_type, device_id, result_list, max_count);
 
 	hdhomerun_discover_destroy(ds);
 	return ret;
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_discover.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_discover.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_discover.h	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_discover.h	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_discover.h
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006-2007 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,8 +14,20 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 #ifdef __cplusplus
 extern "C" {
@@ -28,29 +40,18 @@
 };
 
 /*
- * Find a device by device ID.
- *
- * The device information is stored in caller-supplied hdhomerun_discover_device_t var.
- * Multiple attempts are made to find the device.
- * Worst-case execution time is 1 second.
- *
- * Returns 1 on success.
- * Returns 0 if not found.
- * Retruns -1 on error.
- */
-extern int hdhomerun_discover_find_device(uint32_t device_id, struct hdhomerun_discover_device_t *result);
-
-/*
- * Find all devices of a given type.
+ * 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 1 second.
  *
+ * Set target_ip to zero to auto-detect IP address.
+ *
  * Returns the number of devices found.
  * Retruns -1 on error.
  */
-extern int hdhomerun_discover_find_devices(uint32_t device_type, struct hdhomerun_discover_device_t result_list[], int max_count);
+extern LIBTYPE int hdhomerun_discover_find_devices_custom(uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count);
 
 /*
  * Verify that the device ID given is valid.
@@ -61,7 +62,7 @@
  * Returns TRUE if valid.
  * Returns FALSE if not valid.
  */
-extern bool_t hdhomerun_discover_validate_device_id(uint32_t device_id);
+extern LIBTYPE bool_t hdhomerun_discover_validate_device_id(uint32_t device_id);
 
 #ifdef __cplusplus
 }
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_os.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_os.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_os.h	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_os.h	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_os.h
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,97 +14,36 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#if defined(WIN32)
+#if defined(_WIN32) || defined(_WIN64)
 #define __WINDOWS__
 #endif
 
 #if defined(__WINDOWS__)
-#include <windows.h>
-#include <sys/types.h>
-#include <sys/timeb.h>
+#include "hdhomerun_os_windows.h"
 #else
-#include <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <sys/time.h>
-#include <sys/timeb.h>
-#include <fcntl.h>
+#include "hdhomerun_os_posix.h"
 #endif
 
-#include <pthread.h>
-
 #if !defined(TRUE)
 #define TRUE 1
 #endif
+
 #if !defined(FALSE)
 #define FALSE 0
 #endif
-
-#if defined(__WINDOWS__)
-
-typedef int bool_t;
-typedef unsigned __int8 uint8_t;
-typedef unsigned __int16 uint16_t;
-typedef unsigned __int32 uint32_t;
-typedef unsigned __int64 uint64_t;
-
-#define socklen_t int
-#define close closesocket
-#define sock_getlasterror WSAGetLastError()
-#define sock_getlasterror_socktimeout (WSAGetLastError() == WSAETIMEDOUT)
-#define atoll _atoi64
-#define strcasecmp _stricmp
-#define fseeko _fseeki64
-#define ftello _ftelli64
-#define usleep(us) Sleep((us)/1000)
-#define sleep(sec) Sleep((sec)*1000)
-
-static inline uint64_t getcurrenttime(void)
-{
-	struct timeb tb;
-	ftime(&tb);
-	return ((uint64_t)tb.time * 1000) + tb.millitm;
-}
-
-static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
-{
-	int t = (int)timeout;
-	return setsockopt(s, level, optname, (char *)&t, sizeof(t));
-}
-
-#else
-
-typedef int bool_t;
-
-#define sock_getlasterror errno
-#define sock_getlasterror_socktimeout (errno == EAGAIN)
-
-static inline uint64_t getcurrenttime(void)
-{
-	struct timeval t;
-	gettimeofday(&t, NULL);
-	return ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
-}
-
-static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
-{
-	struct timeval t;
-	t.tv_sec = timeout / 1000;
-	t.tv_usec = (timeout % 1000) * 1000;
-	return setsockopt(s, level, optname, (char *)&t, sizeof(t));
-}
-
-#endif
-
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_os_posix.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_os_posix.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_os_posix.h	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_os_posix.h	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,74 @@
+/*
+ * hdhomerun_os_posix.h
+ *
+ * Copyright Â© 2006-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+
+typedef int bool_t;
+
+#define LIBTYPE
+#define sock_getlasterror errno
+#define sock_getlasterror_socktimeout (errno == EAGAIN)
+#define console_vprintf vprintf
+#define console_printf printf
+#define THREAD_FUNC_PREFIX void *
+
+static inline uint64_t getcurrenttime(void)
+{
+	struct timeval t;
+	gettimeofday(&t, NULL);
+	return ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
+}
+
+static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
+{
+	struct timeval t;
+	t.tv_sec = timeout / 1000;
+	t.tv_usec = (timeout % 1000) * 1000;
+	return setsockopt(s, level, optname, (char *)&t, sizeof(t));
+}
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_os_windows.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_os_windows.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_os_windows.h	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_os_windows.h	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,162 @@
+/*
+ * hdhomerun_os_windows.h
+ *
+ * Copyright Â© 2006-2008 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+#define _WINSOCKAPI_
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <wspiapi.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+
+#if defined(DLL_IMPORT)
+#define LIBTYPE __declspec( dllexport )
+#elif  defined(DLL_EXPORT)
+#define LIBTYPE __declspec( dllimport )
+#else
+#define LIBTYPE
+#endif
+
+typedef int bool_t;
+typedef signed __int8 int8_t;
+typedef signed __int16 int16_t;
+typedef signed __int32 int32_t;
+typedef signed __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef HANDLE pthread_t;
+typedef HANDLE pthread_mutex_t;
+
+#define socklen_t int
+#define close closesocket
+#define sock_getlasterror WSAGetLastError()
+#define sock_getlasterror_socktimeout (WSAGetLastError() == WSAETIMEDOUT)
+#define va_copy(x, y) x = y
+#define atoll _atoi64
+#define strdup _strdup
+#define strcasecmp _stricmp
+#define snprintf _snprintf
+#define fseeko _fseeki64
+#define ftello _ftelli64
+#define THREAD_FUNC_PREFIX DWORD WINAPI
+#define SIGPIPE SIGABRT
+
+static inline int usleep(unsigned int us)
+{
+	Sleep((us)/1000);
+	return 0;
+}
+
+static inline int sleep(unsigned int sec)
+{
+	Sleep((sec)*1000);
+	return 0;
+}
+
+static inline uint64_t getcurrenttime(void)
+{
+	struct timeb tb;
+	ftime(&tb);
+	return ((uint64_t)tb.time * 1000) + tb.millitm;
+}
+
+static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
+{
+	int t = (int)timeout;
+	return setsockopt(s, level, optname, (char *)&t, sizeof(t));
+}
+
+static inline int pthread_create(pthread_t *tid, void *attr, LPTHREAD_START_ROUTINE start, void *arg)
+{
+	*tid = CreateThread(NULL, 0, start, arg, 0, NULL);
+	if (!*tid) {
+		return (int)GetLastError();
+	}
+	return 0;
+}
+
+static inline int pthread_join(pthread_t tid, void **value_ptr)
+{
+	while (1) {
+		DWORD ExitCode = 0;
+		if (!GetExitCodeThread(tid, &ExitCode)) {
+			return (int)GetLastError();
+		}
+		if (ExitCode != STILL_ACTIVE) {
+			return 0;
+		}
+	}
+}
+
+static inline void pthread_mutex_init(pthread_mutex_t *mutex, void *attr)
+{
+	*mutex = CreateMutex(NULL, FALSE, NULL);
+}
+
+static inline void pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+	WaitForSingleObject(*mutex, INFINITE);
+}
+
+static inline void pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+	ReleaseMutex(*mutex);
+}
+
+/*
+ * The console output format should be set to UTF-8, however in XP and Vista this breaks batch file processing.
+ * Attempting to restore on exit fails to restore if the program is terminated by the user.
+ * Solution - set the output format each printf.
+ */
+static inline void console_vprintf(const char *fmt, va_list ap)
+{
+	UINT cp = GetConsoleOutputCP();
+	SetConsoleOutputCP(CP_UTF8);
+	vprintf(fmt, ap);
+	SetConsoleOutputCP(cp);
+}
+
+static inline void console_printf(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	console_vprintf(fmt, ap);
+	va_end(ap);
+}
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_pkt.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_pkt.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_pkt.c	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_pkt.c	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_pkt.c
  *
- * Copyright © 2005-2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2005-2006 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,255 +14,232 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 
-#include "hdhomerun_os.h"
-#include "hdhomerun_pkt.h"
+#include "hdhomerun.h"
 
-uint8_t hdhomerun_read_u8(uint8_t **pptr)
+struct hdhomerun_pkt_t *hdhomerun_pkt_create(void)
 {
-	uint8_t *ptr = *pptr;
-	uint8_t v = *ptr++;
-	*pptr = ptr;
+	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_read_u16(uint8_t **pptr)
+uint16_t hdhomerun_pkt_read_u16(struct hdhomerun_pkt_t *pkt)
 {
-	uint8_t *ptr = *pptr;
 	uint16_t v;
-	v =  (uint16_t)*ptr++ << 8;
-	v |= (uint16_t)*ptr++ << 0;
-	*pptr = ptr;
+	v =  (uint16_t)*pkt->pos++ << 8;
+	v |= (uint16_t)*pkt->pos++ << 0;
 	return v;
 }
 
-uint32_t hdhomerun_read_u32(uint8_t **pptr)
+uint32_t hdhomerun_pkt_read_u32(struct hdhomerun_pkt_t *pkt)
 {
-	uint8_t *ptr = *pptr;
 	uint32_t v;
-	v =  (uint32_t)*ptr++ << 24;
-	v |= (uint32_t)*ptr++ << 16;
-	v |= (uint32_t)*ptr++ << 8;
-	v |= (uint32_t)*ptr++ << 0;
-	*pptr = ptr;
+	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_read_var_length(uint8_t **pptr, uint8_t *end)
+size_t hdhomerun_pkt_read_var_length(struct hdhomerun_pkt_t *pkt)
 {
-	uint8_t *ptr = *pptr;
 	size_t length;
 	
-	if (ptr + 1 > end) {
-		return -1;
+	if (pkt->pos + 1 > pkt->end) {
+		return (size_t)-1;
 	}
 
-	length = (size_t)*ptr++;
+	length = (size_t)*pkt->pos++;
 	if (length & 0x0080) {
-		if (ptr + 1 > end) {
-			return -1;
+		if (pkt->pos + 1 > pkt->end) {
+			return (size_t)-1;
 		}
 
 		length &= 0x007F;
-		length |= (size_t)*ptr++ << 7;
+		length |= (size_t)*pkt->pos++ << 7;
 	}
 	
-	*pptr = ptr;
 	return length; 
 }
 
-int hdhomerun_read_tlv(uint8_t **pptr, uint8_t *end, uint8_t *ptag, size_t *plength, uint8_t **pvalue)
+uint8_t *hdhomerun_pkt_read_tlv(struct hdhomerun_pkt_t *pkt, uint8_t *ptag, size_t *plength)
 {
-	if (end - *pptr < 2) {
-		return -1;
+	if (pkt->pos + 2 > pkt->end) {
+		return NULL;
 	}
 	
-	*ptag = hdhomerun_read_u8(pptr);
-	*plength = hdhomerun_read_var_length(pptr, end);
-	*pvalue = *pptr;
-	
-	if ((size_t)(end - *pptr) < *plength) {
-		return -1;
+	*ptag = hdhomerun_pkt_read_u8(pkt);
+	*plength = hdhomerun_pkt_read_var_length(pkt);
+
+	if (pkt->pos + *plength > pkt->end) {
+		return NULL;
 	}
 	
-	*pptr += *plength;
-	return 0;
+	return pkt->pos + *plength;
 }
 
-void hdhomerun_write_u8(uint8_t **pptr, uint8_t v)
+void hdhomerun_pkt_write_u8(struct hdhomerun_pkt_t *pkt, uint8_t v)
 {
-	uint8_t *ptr = *pptr;
-	*ptr++ = v;
-	*pptr = ptr;
-}
+	*pkt->pos++ = v;
 
-void hdhomerun_write_u16(uint8_t **pptr, uint16_t v)
-{
-	uint8_t *ptr = *pptr;
-	*ptr++ = (uint8_t)(v >> 8);
-	*ptr++ = (uint8_t)(v >> 0);
-	*pptr = ptr;
+	if (pkt->pos > pkt->end) {
+		pkt->end = pkt->pos;
+	}
 }
 
-void hdhomerun_write_u32(uint8_t **pptr, uint32_t v)
+void hdhomerun_pkt_write_u16(struct hdhomerun_pkt_t *pkt, uint16_t v)
 {
-	uint8_t *ptr = *pptr;
-	*ptr++ = (uint8_t)(v >> 24);
-	*ptr++ = (uint8_t)(v >> 16);
-	*ptr++ = (uint8_t)(v >> 8);
-	*ptr++ = (uint8_t)(v >> 0);
-	*pptr = ptr;
-}
+	*pkt->pos++ = (uint8_t)(v >> 8);
+	*pkt->pos++ = (uint8_t)(v >> 0);
 
-void hdhomerun_write_var_length(uint8_t **pptr, size_t v)
-{
-	uint8_t *ptr = *pptr;
-	if (v <= 127) {
-		*ptr++ = (uint8_t)v;
-	} else {
-		*ptr++ = (uint8_t)(v | 0x80);
-		*ptr++ = (uint8_t)(v >> 7);
+	if (pkt->pos > pkt->end) {
+		pkt->end = pkt->pos;
 	}
-	*pptr = ptr;
 }
 
-static void hdhomerun_write_mem(uint8_t **pptr, void *mem, size_t length)
+void hdhomerun_pkt_write_u32(struct hdhomerun_pkt_t *pkt, uint32_t v)
 {
-	uint8_t *ptr = *pptr;
-	memcpy(ptr, mem, length);
-	ptr += length;
-	*pptr = ptr;
-}
+	*pkt->pos++ = (uint8_t)(v >> 24);
+	*pkt->pos++ = (uint8_t)(v >> 16);
+	*pkt->pos++ = (uint8_t)(v >> 8);
+	*pkt->pos++ = (uint8_t)(v >> 0);
 
-static uint32_t hdhomerun_calc_crc(uint8_t *start, uint8_t *end)
-{
-	uint8_t *ptr = start;
-	uint32_t crc = 0xFFFFFFFF;
-	while (ptr < end) {
-		uint8_t x = (uint8_t)(crc) ^ *ptr++;
-		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;
+	if (pkt->pos > pkt->end) {
+		pkt->end = pkt->pos;
 	}
-	return crc ^ 0xFFFFFFFF;
 }
 
-static int hdhomerun_check_crc(uint8_t *start, uint8_t *end)
+void hdhomerun_pkt_write_var_length(struct hdhomerun_pkt_t *pkt, size_t v)
 {
-	if (end - start < 8) {
-		return -1;
+	if (v <= 127) {
+		*pkt->pos++ = (uint8_t)v;
+	} else {
+		*pkt->pos++ = (uint8_t)(v | 0x80);
+		*pkt->pos++ = (uint8_t)(v >> 7);
 	}
-	uint8_t *ptr = end -= 4;
-	uint32_t actual_crc = hdhomerun_calc_crc(start, ptr);
-	uint32_t packet_crc;
-	packet_crc =  (uint32_t)*ptr++ << 0;
-	packet_crc |= (uint32_t)*ptr++ << 8;
-	packet_crc |= (uint32_t)*ptr++ << 16;
-	packet_crc |= (uint32_t)*ptr++ << 24;
-	if (actual_crc != packet_crc) {
-		return -1;
+
+	if (pkt->pos > pkt->end) {
+		pkt->end = pkt->pos;
 	}
-	return 0;
 }
 
-static void hdhomerun_write_header_length(uint8_t *ptr, size_t length)
+void hdhomerun_pkt_write_mem(struct hdhomerun_pkt_t *pkt, const void *mem, size_t length)
 {
-	hdhomerun_write_u16(&ptr, (uint16_t)length);
-}
+	memcpy(pkt->pos, mem, length);
+	pkt->pos += length;
 
-void hdhomerun_write_crc(uint8_t **pptr, uint8_t *start)
-{
-	uint8_t *ptr = *pptr;
-	uint32_t crc = hdhomerun_calc_crc(start, ptr);
-	*ptr++ = (uint8_t)(crc >> 0);
-	*ptr++ = (uint8_t)(crc >> 8);
-	*ptr++ = (uint8_t)(crc >> 16);
-	*ptr++ = (uint8_t)(crc >> 24);
-	*pptr = ptr;
+	if (pkt->pos > pkt->end) {
+		pkt->end = pkt->pos;
+	}
 }
 
-void hdhomerun_write_discover_request(uint8_t **pptr, uint32_t device_type, uint32_t device_id)
+int hdhomerun_pkt_open_frame(struct hdhomerun_pkt_t *pkt, uint16_t *ptype)
 {
-	uint8_t *start = *pptr;
-	hdhomerun_write_u16(pptr, HDHOMERUN_TYPE_DISCOVER_REQ);
-	hdhomerun_write_u16(pptr, 0);
+	pkt->pos = pkt->start;
 
-	hdhomerun_write_u8(pptr, HDHOMERUN_TAG_DEVICE_TYPE);
-	hdhomerun_write_var_length(pptr, 4);
-	hdhomerun_write_u32(pptr, device_type);
-	hdhomerun_write_u8(pptr, HDHOMERUN_TAG_DEVICE_ID);
-	hdhomerun_write_var_length(pptr, 4);
-	hdhomerun_write_u32(pptr, device_id);
+	if (pkt->pos + 4 > pkt->end) {
+		return 0;
+	}
 
-	hdhomerun_write_header_length(start + 2, (int)(*pptr - start - 4));
-	hdhomerun_write_crc(pptr, start);
-}
+	*ptype = hdhomerun_pkt_read_u16(pkt);
+	size_t length = hdhomerun_pkt_read_u16(pkt);
+	pkt->pos += length;
 
-void hdhomerun_write_get_set_request(uint8_t **pptr, const char *name, const char *value)
-{
-	uint8_t *start = *pptr;
-	hdhomerun_write_u16(pptr, HDHOMERUN_TYPE_GETSET_REQ);
-	hdhomerun_write_u16(pptr, 0);
+	if (pkt->pos + 4 > pkt->end) {
+		pkt->pos = pkt->start;
+		return 0;
+	}
 
-	int name_len = (int)strlen(name) + 1;
-	hdhomerun_write_u8(pptr, HDHOMERUN_TAG_GETSET_NAME);
-	hdhomerun_write_var_length(pptr, name_len);
-	hdhomerun_write_mem(pptr, (void *)name, name_len);
+	uint32_t calc_crc = hdhomerun_pkt_calc_crc(pkt->start, pkt->pos);
 
-	if (value) {
-		int value_len = (int)strlen(value) + 1;
-		hdhomerun_write_u8(pptr, HDHOMERUN_TAG_GETSET_VALUE);
-		hdhomerun_write_var_length(pptr, value_len);
-		hdhomerun_write_mem(pptr, (void *)value, value_len);
+	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;
 	}
 
-	hdhomerun_write_header_length(start + 2, (int)(*pptr - start - 4));
-	hdhomerun_write_crc(pptr, start);
+	pkt->start += 4;
+	pkt->end = pkt->start + length;
+	pkt->pos = pkt->start;
+	return 1;
 }
 
-void hdhomerun_write_upgrade_request(uint8_t **pptr, uint32_t sequence, void *data, size_t length)
+void hdhomerun_pkt_seal_frame(struct hdhomerun_pkt_t *pkt, uint16_t frame_type)
 {
-	uint8_t *start = *pptr;
-	hdhomerun_write_u16(pptr, HDHOMERUN_TYPE_UPGRADE_REQ);
-	hdhomerun_write_u16(pptr, 0);
+	size_t length = pkt->end - pkt->start;
 
-	hdhomerun_write_u32(pptr, sequence);
-	if (length > 0) {
-		hdhomerun_write_mem(pptr, data, length);
-	}
+	pkt->start -= 4;
+	pkt->pos = pkt->start;
+	hdhomerun_pkt_write_u16(pkt, frame_type);
+	hdhomerun_pkt_write_u16(pkt, (uint16_t)length);
 
-	hdhomerun_write_header_length(start + 2, *pptr - start - 4);
-	hdhomerun_write_crc(pptr, start);
-}
+	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);
 
-size_t hdhomerun_peek_packet_length(uint8_t *ptr)
-{
-	ptr += 2;
-	return (size_t)hdhomerun_read_u16(&ptr) + 8;
+	pkt->pos = pkt->start;
 }
-
-int hdhomerun_process_packet(uint8_t **pptr, uint8_t **pend)
-{
-	if (hdhomerun_check_crc(*pptr, *pend) < 0) {
-		return -1;
-	}
-	*pend -= 4;
-	
-	uint16_t type = hdhomerun_read_u16(pptr);
-	uint16_t length = hdhomerun_read_u16(pptr);
-	if ((*pend - *pptr) < length) {
-		return -1;
-	}
-	*pend = *pptr + length;
-	return (int)type;
-}
-
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_pkt.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_pkt.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_pkt.h	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_pkt.h	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_pkt.h
  *
- * Copyright © 2005-2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2005-2006 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,8 +14,20 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 #ifdef __cplusplus
 extern "C" {
@@ -110,6 +122,9 @@
 #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
@@ -121,6 +136,7 @@
 #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_DEVICE_TYPE_WILDCARD 0xFFFFFFFF
@@ -129,25 +145,33 @@
 
 #define HDHOMERUN_MIN_PEEK_LENGTH 4
 
-extern uint8_t hdhomerun_read_u8(uint8_t **pptr);
-extern uint16_t hdhomerun_read_u16(uint8_t **pptr);
-extern uint32_t hdhomerun_read_u32(uint8_t **pptr);
-extern size_t hdhomerun_read_var_length(uint8_t **pptr, uint8_t *end);
-extern void hdhomerun_write_u8(uint8_t **pptr, uint8_t v);
-extern void hdhomerun_write_u16(uint8_t **pptr, uint16_t v);
-extern void hdhomerun_write_u32(uint8_t **pptr, uint32_t v);
-extern void hdhomerun_write_var_length(uint8_t **pptr, size_t v);
-extern void hdhomerun_write_crc(uint8_t **pptr, uint8_t *start);
-
-extern size_t hdhomerun_peek_packet_length(uint8_t *ptr);
-extern int hdhomerun_process_packet(uint8_t **pptr, uint8_t **pend);
-extern int hdhomerun_read_tlv(uint8_t **pptr, uint8_t *end, uint8_t *ptag, size_t *plength, uint8_t **pvalue);
-
-extern void hdhomerun_write_discover_request(uint8_t **pptr, uint32_t device_type, uint32_t device_id);
-extern void hdhomerun_write_get_set_request(uint8_t **pptr, const char *name, const char *value);
-extern void hdhomerun_write_upgrade_request(uint8_t **pptr, uint32_t sequence, void *data, size_t length);
+struct hdhomerun_pkt_t {
+	uint8_t *pos;
+	uint8_t *start;
+	uint8_t *end;
+	uint8_t *limit;
+	uint8_t buffer[3074];
+};
+
+extern LIBTYPE struct hdhomerun_pkt_t *hdhomerun_pkt_create(void);
+extern LIBTYPE void hdhomerun_pkt_destroy(struct hdhomerun_pkt_t *pkt);
+extern LIBTYPE void hdhomerun_pkt_reset(struct hdhomerun_pkt_t *pkt);
+
+extern LIBTYPE uint8_t hdhomerun_pkt_read_u8(struct hdhomerun_pkt_t *pkt);
+extern LIBTYPE uint16_t hdhomerun_pkt_read_u16(struct hdhomerun_pkt_t *pkt);
+extern LIBTYPE uint32_t hdhomerun_pkt_read_u32(struct hdhomerun_pkt_t *pkt);
+extern LIBTYPE size_t hdhomerun_pkt_read_var_length(struct hdhomerun_pkt_t *pkt);
+extern LIBTYPE uint8_t *hdhomerun_pkt_read_tlv(struct hdhomerun_pkt_t *pkt, uint8_t *ptag, size_t *plength);
+
+extern LIBTYPE void hdhomerun_pkt_write_u8(struct hdhomerun_pkt_t *pkt, uint8_t v);
+extern LIBTYPE void hdhomerun_pkt_write_u16(struct hdhomerun_pkt_t *pkt, uint16_t v);
+extern LIBTYPE void hdhomerun_pkt_write_u32(struct hdhomerun_pkt_t *pkt, uint32_t v);
+extern LIBTYPE void hdhomerun_pkt_write_var_length(struct hdhomerun_pkt_t *pkt, size_t v);
+extern LIBTYPE void hdhomerun_pkt_write_mem(struct hdhomerun_pkt_t *pkt, const void *mem, size_t length);
+
+extern LIBTYPE bool_t hdhomerun_pkt_open_frame(struct hdhomerun_pkt_t *pkt, uint16_t *ptype);
+extern LIBTYPE void hdhomerun_pkt_seal_frame(struct hdhomerun_pkt_t *pkt, uint16_t frame_type);
 
 #ifdef __cplusplus
 }
 #endif
-
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_types.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_types.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_types.h	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_types.h	2009-01-16 18:31:54.000000000 -0600
@@ -0,0 +1,71 @@
+/*
+ * hdhomerun_types.h
+ *
+ * Copyright Â© 2008 Silicondust Engineering Ltd. <www.silicondust.com>.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
+ */
+
+struct hdhomerun_device_t;
+
+struct hdhomerun_tuner_status_t {
+	char channel[32];
+	char lock_str[32];
+	bool_t signal_present;
+	bool_t lock_supported;
+	bool_t 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_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];
+};
+
+struct hdhomerun_plotsample_t {
+	int16_t real;
+	int16_t imag;
+};
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_video.c release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_video.c
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_video.c	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_video.c	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_video.c
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,27 +14,45 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 
-#include "hdhomerun_os.h"
-#include "hdhomerun_pkt.h"
-#include "hdhomerun_video.h"
+#include "hdhomerun.h"
 
 struct hdhomerun_video_sock_t {
+	pthread_mutex_t lock;
 	uint8_t *buffer;
 	size_t buffer_size;
 	volatile size_t head;
 	volatile size_t tail;
 	size_t advance;
-	volatile bool_t running;
 	volatile bool_t terminate;
 	pthread_t thread;
 	int sock;
+	uint32_t rtp_sequence;
+	struct hdhomerun_debug_t *dbg;
+	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 uint8_t sequence[0x2000];
 };
 
-static void *hdhomerun_video_thread(void *arg);
+static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg);
 
 static bool_t hdhomerun_video_bind_sock_internal(struct hdhomerun_video_sock_t *vs, uint16_t listen_port)
 {
@@ -80,6 +98,11 @@
 		return NULL;
 	}
 
+	pthread_mutex_init(&vs->lock, NULL);
+
+	/* 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) {
@@ -113,16 +136,19 @@
 
 	/* Bind socket. */
 	if (!hdhomerun_video_bind_sock(vs, listen_port)) {
-		hdhomerun_video_destroy(vs);
+		close(vs->sock);
+		free(vs->buffer);
+		free(vs);
 		return NULL;
 	}
 
 	/* Start thread. */
-	if (pthread_create(&vs->thread, NULL, &hdhomerun_video_thread, vs) != 0) {
-		hdhomerun_video_destroy(vs);
+	if (pthread_create(&vs->thread, NULL, &hdhomerun_video_thread_execute, vs) != 0) {
+		close(vs->sock);
+		free(vs->buffer);
+		free(vs);
 		return NULL;
 	}
-	vs->running = 1;
 
 	/* Success. */
 	return vs;
@@ -130,39 +156,100 @@
 
 void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs)
 {
-	if (vs->running) {
-		vs->terminate = 1;
-		pthread_join(vs->thread, NULL);
-	}
+	vs->terminate = TRUE;
+	pthread_join(vs->thread, NULL);
+
 	close(vs->sock);
 	free(vs->buffer);
+
 	free(vs);
 }
 
+void hdhomerun_video_set_debug(struct hdhomerun_video_sock_t *vs, struct hdhomerun_debug_t *dbg)
+{
+	vs->dbg = dbg;
+}
+
 uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs)
 {
 	struct sockaddr_in sock_addr;
 	socklen_t sockaddr_size = sizeof(sock_addr);
 	if (getsockname(vs->sock, (struct sockaddr*)&sock_addr, &sockaddr_size) != 0) {
+		hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_get_local_port: getsockname failed (%d)\n", sock_getlasterror);
 		return 0;
 	}
 	return ntohs(sock_addr.sin_port);
 }
 
-int hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs)
+static void hdhomerun_video_stats_ts_pkt(struct hdhomerun_video_sock_t *vs, uint8_t *ptr)
 {
-	return vs->sock;
+	uint16_t packet_identifier = ((uint16_t)(ptr[1] & 0x1F) << 8) | (uint16_t)ptr[2];
+	if (packet_identifier == 0x1FFF) {
+		return;
+	}
+
+	bool_t transport_error = ptr[1] >> 7;
+	if (transport_error) {
+		vs->transport_error_count++;
+		vs->sequence[packet_identifier] = 0xFF;
+		return;
+	}
+
+	uint8_t continuity_counter = ptr[3] & 0x0F;
+	uint8_t previous_sequence = vs->sequence[packet_identifier];
+
+	if (continuity_counter == ((previous_sequence + 1) & 0x0F)) {
+		vs->sequence[packet_identifier] = continuity_counter;
+		return;
+	}
+	if (previous_sequence == 0xFF) {
+		vs->sequence[packet_identifier] = continuity_counter;
+		return;
+	}
+	if (continuity_counter == previous_sequence) {
+		return;
+	}
+
+	vs->sequence_error_count++;
+	vs->sequence[packet_identifier] = continuity_counter;
 }
 
-static void *hdhomerun_video_thread(void *arg)
+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;
+
+	if (rtp_sequence != ((vs->rtp_sequence + 1) & 0xFFFF)) {
+		if (vs->rtp_sequence != 0xFFFFFFFF) {
+			vs->network_error_count++;
+
+			/* restart pid sequence check */
+			memset((void *)vs->sequence, 0xFF, sizeof(vs->sequence));
+		}
+	}
+
+	vs->rtp_sequence = rtp_sequence;
+}
+
+static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg)
 {
 	struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)arg;
+	struct hdhomerun_pkt_t pkt_inst;
 
 	while (!vs->terminate) {
-		size_t head = vs->head;
+		struct hdhomerun_pkt_t *pkt = &pkt_inst;
+		hdhomerun_pkt_reset(pkt);
 
 		/* Receive. */
-		int length = recv(vs->sock, (char *)vs->buffer + head, VIDEO_DATA_PACKET_SIZE, 0);
+		int length = recv(vs->sock, (char *)pkt->end, VIDEO_RTP_DATA_PACKET_SIZE, 0);
+		pkt->end += length;
+
+		if (length == VIDEO_RTP_DATA_PACKET_SIZE) {
+			hdhomerun_video_parse_rtp(vs, pkt);
+			length = (int)(pkt->end - pkt->pos);
+		}
+
 		if (length != VIDEO_DATA_PACKET_SIZE) {
 			if (length > 0) {
 				/* Data received but not valid - ignore. */
@@ -172,10 +259,27 @@
 				/* Wait for more data. */
 				continue;
 			}
-			vs->terminate = 1;
+			vs->terminate = TRUE;
 			return NULL;
 		}
 
+		pthread_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) {
@@ -184,11 +288,15 @@
 
 		/* Check for buffer overflow. */
 		if (head == vs->tail) {
+			vs->overflow_error_count++;
+			pthread_mutex_unlock(&vs->lock);
 			continue;
 		}
 
 		/* Atomic update. */
 		vs->head = head;
+
+		pthread_mutex_unlock(&vs->lock);
 	}
 
 	return NULL;
@@ -196,6 +304,8 @@
 
 uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size, size_t *pactual_size)
 {
+	pthread_mutex_lock(&vs->lock);
+
 	size_t head = vs->head;
 	size_t tail = vs->tail;
 
@@ -212,6 +322,7 @@
 	if (head == tail) {
 		vs->advance = 0;
 		*pactual_size = 0;
+		pthread_mutex_unlock(&vs->lock);
 		return NULL;
 	}
 
@@ -219,6 +330,7 @@
 	if (size == 0) {
 		vs->advance = 0;
 		*pactual_size = 0;
+		pthread_mutex_unlock(&vs->lock);
 		return NULL;
 	}
 
@@ -233,13 +345,55 @@
 	}
 	vs->advance = size;
 	*pactual_size = size;
-	return vs->buffer + tail;
+	uint8_t *result = vs->buffer + tail;
+
+	pthread_mutex_unlock(&vs->lock);
+	return result;
 }
 
 void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs)
 {
-	/* Atomic update of tail. */
+	pthread_mutex_lock(&vs->lock);
+
 	vs->tail = vs->head;
 	vs->advance = 0;
+
+	memset((void *)vs->sequence, 0xFF, sizeof(vs->sequence));
+
+	vs->rtp_sequence = 0xFFFFFFFF;
+
+	vs->packet_count = 0;
+	vs->transport_error_count = 0;
+	vs->network_error_count = 0;
+	vs->sequence_error_count = 0;
+	vs->overflow_error_count = 0;
+
+	pthread_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=%ld net=%ld te=%ld miss=%ld drop=%ld\n",
+		stats.packet_count, stats.network_error_count,
+		stats.transport_error_count, stats.sequence_error_count,
+		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));
+
+	pthread_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;
+
+	pthread_mutex_unlock(&vs->lock);
+}
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/hdhomerun_video.h release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/hdhomerun_video.h
--- mythtv/libs/libmythtv/hdhomerun/hdhomerun_video.h	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/hdhomerun_video.h	2009-01-16 18:31:54.000000000 -0600
@@ -1,12 +1,12 @@
 /*
  * hdhomerun_video.h
  *
- * Copyright © 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
+ * Copyright Â© 2006 Silicondust Engineering Ltd. <www.silicondust.com>.
  *
- * This library is free software; you can redistribute it and/or
+ * 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.
+ * version 3 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
@@ -14,8 +14,20 @@
  * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * As a special exception to the GNU Lesser General Public License,
+ * you may link, statically or dynamically, an application with a
+ * publicly distributed version of the Library to produce an
+ * executable file containing portions of the Library, and
+ * distribute that executable file under terms of your choice,
+ * without any of the additional requirements listed in clause 4 of
+ * the GNU Lesser General Public License.
+ * 
+ * By "a publicly distributed version of the Library", we mean
+ * either the unmodified Library as distributed by Silicondust, or a
+ * modified version of the Library that is distributed under the
+ * conditions defined in the GNU Lesser General Public License.
  */
 #ifdef __cplusplus
 extern "C" {
@@ -23,10 +35,20 @@
 
 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.
  *
@@ -37,20 +59,15 @@
  *
  * When no longer needed, the socket should be destroyed by calling hdhomerun_control_destroy.
  */
-extern struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, size_t buffer_size);
-extern void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs);
+extern LIBTYPE struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, size_t buffer_size);
+extern LIBTYPE void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs);
 
 /*
  * Get the port the socket is listening on.
  *
  * Returns 16-bit port with native endianness, or 0 on error.
  */
-extern uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs);
-
-/*
- * Get the low-level socket handle.
- */
-extern int hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs);
+extern LIBTYPE uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs);
 
 /*
  * Read data from buffer.
@@ -69,12 +86,20 @@
  * 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 uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size, size_t *pactual_size);
+extern LIBTYPE uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size, size_t *pactual_size);
 
 /*
  * Flush the buffer.
  */
-extern void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs);
+extern LIBTYPE void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs);
+
+/*
+ * Debug print internal stats.
+ */
+extern LIBTYPE void hdhomerun_video_set_debug(struct hdhomerun_video_sock_t *vs, struct hdhomerun_debug_t *dbg);
+extern LIBTYPE void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs);
+
+extern LIBTYPE void hdhomerun_video_get_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_video_stats_t *stats);
 
 #ifdef __cplusplus
 }
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhomerun/lgpl.txt release.19703.0116b/mythtv/libs/libmythtv/hdhomerun/lgpl.txt
--- mythtv/libs/libmythtv/hdhomerun/lgpl.txt	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhomerun/lgpl.txt	2009-01-16 18:31:54.000000000 -0600
@@ -1,504 +1,165 @@
-		  GNU LESSER GENERAL PUBLIC LICENSE
-		       Version 2.1, February 1999
+		   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
 
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
  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.
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    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!
-
 
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions. 
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version. 
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  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 that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU 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 as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/hdhrchannel.cpp release.19703.0116b/mythtv/libs/libmythtv/hdhrchannel.cpp
--- mythtv/libs/libmythtv/hdhrchannel.cpp	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/hdhrchannel.cpp	2009-01-16 18:31:06.000000000 -0600
@@ -42,6 +42,7 @@
     if (valid && hdhomerun_discover_validate_device_id(_device_id))
 	return;
 
+    _device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
     /* Otherwise, is it a valid IP address? */
     struct in_addr address;
     if (inet_aton(device, &address)) 
@@ -99,7 +100,7 @@
 
     /* Discover. */
     struct hdhomerun_discover_device_t result;
-    int ret = hdhomerun_discover_find_device(_device_id, &result);
+    int ret = hdhomerun_discover_find_devices_custom(0, HDHOMERUN_DEVICE_TYPE_WILDCARD, _device_id, &result, 1);
     if (ret < 0)
     {
         VERBOSE(VB_IMPORTANT, LOC_ERR + "Unable to send discovery request" + ENO);
diff -r -u -N -X diff.exclude -x release.19703.0116a -x release.19703.0116b release.19703.0116a/mythtv/libs/libmythtv/libmythtv.pro release.19703.0116b/mythtv/libs/libmythtv/libmythtv.pro
--- mythtv/libs/libmythtv/libmythtv.pro	2009-01-16 18:17:24.000000000 -0600
+++ mythtv/libs/libmythtv/libmythtv.pro	2009-01-16 18:28:34.000000000 -0600
@@ -461,10 +461,18 @@
         # HDHomeRun library
         HEADERS += hdhomerun/hdhomerun_pkt.h   hdhomerun/hdhomerun_discover.h
         HEADERS += hdhomerun/hdhomerun_video.h hdhomerun/hdhomerun_control.h
-        HEADERS += hdhomerun/hdhomerun_os.h
+        HEADERS += hdhomerun/hdhomerun_os.h    hdhomerun/hdhomerun.h
+        HEADERS += hdhomerun/hdhomerun_dhcp.h  hdhomerun/hdhomerun_types.h
+        HEADERS += hdhomerun/hdhomerun_channels.h
+        HEADERS += hdhomerun/hdhomerun_channelscan.h
+        HEADERS += hdhomerun/hdhomerun_debug.h
 
         SOURCES += hdhomerun/hdhomerun_pkt.c   hdhomerun/hdhomerun_discover.c
         SOURCES += hdhomerun/hdhomerun_video.c hdhomerun/hdhomerun_control.c
+        SOURCES += hdhomerun/hdhomerun_debug.c hdhomerun/hdhomerun_channels.c
+        SOURCES += hdhomerun/hdhomerun_dhcp.c  hdhomerun/hdhomerun_channelscan.c
+        SOURCES += hdhomerun/hdhomerun_device.c
+
     }
 
     # Support for PVR-150/250/350/500, etc. on Linux
