<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>libhdhomerun/hdhomerun_channels.c</filename>
    </added>
    <added>
      <filename>libhdhomerun/hdhomerun_channels.h</filename>
    </added>
    <added>
      <filename>libhdhomerun/hdhomerun_dhcp.c</filename>
    </added>
    <added>
      <filename>libhdhomerun/hdhomerun_dhcp.h</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,28 +1,40 @@
-
 LIBSRCS += hdhomerun_pkt.c
 LIBSRCS += hdhomerun_debug.c
 LIBSRCS += hdhomerun_discover.c
-LIBSRCS += hdhomerun_control.c
+LIBSRCS += hdhomerun_channels.c
 LIBSRCS += hdhomerun_channelscan.c
+LIBSRCS += hdhomerun_control.c
 LIBSRCS += hdhomerun_video.c
 LIBSRCS += hdhomerun_device.c
 
 CFLAGS += -Wall -O2 -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wpointer-arith
-CPPFLAGS += -Wall -O2
+LDFLAGS += -lpthread
 
-hdhomerun_config : $(LIBSRCS) hdhomerun_config.c
-	gcc $(CFLAGS) $(LIBSRCS) hdhomerun_config.c -lpthread -o $@
-	strip $@
+ifeq ($(OS),Windows_NT)
+  BINEXT := .exe
+  LIBEXT := .dll
+  LDFLAGS += -liphlpapi
+else
+  LIBEXT := .so
+  ifneq ($(findstring solaris,$(OSTYPE)),)
+    LDFLAGS += -lns -lsocket
+  endif
+endif
 
-hdhomerun_config.exe : $(LIBSRCS) hdhomerun_config.c
-	gcc $(CFLAGS) $(LIBSRCS) hdhomerun_config.c -lpthread -liphlpapi -o $@
-	strip $@
 
-hdhomerun_mythconfig : $(LIBSRCS) hdhomerun_mythconfig.cpp
-	g++ $(CPPFLAGS) $(LIBSRCS) hdhomerun_mythconfig.cpp -lpthread -lmysqlclient -o $@
+hdhomerun_config$(BINEXT) : hdhomerun_config.c $(LIBSRCS)
+	$(CC) $(CFLAGS) $+ $(LDFLAGS) -o $@
 	strip $@
 
+libhdhomerun$(LIBEXT) : $(LIBSRCS)
+	$(CC) -shared -Wl,-soname,libhdhomerun$(LIBEXT) $+ $(LDFLAGS) -o $@
+
+list :
+	@echo hdhomerun_config$(BINEXT)
+	@echo libhdhomerun$(LIBEXT)
+
 clean :
-	rm -f hdhomerun_config
-	rm -f hdhomerun_config.exe
-	rm -f hdhomerun_mythconfig
+	-rm -f hdhomerun_config$(BINEXT)
+	-rm -f libhdhomerun$(LIBEXT)
+
+.PHONY: list clean</diff>
      <filename>libhdhomerun/Makefile</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2005-2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 Top level include file: hdhomerun.h</diff>
      <filename>libhdhomerun/README</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 #include &quot;hdhomerun_os.h&quot;
@@ -25,4 +24,5 @@
 #include &quot;hdhomerun_control.h&quot;
 #include &quot;hdhomerun_video.h&quot;
 #include &quot;hdhomerun_device.h&quot;
+#include &quot;hdhomerun_channels.h&quot;
 #include &quot;hdhomerun_channelscan.h&quot;</diff>
      <filename>libhdhomerun/hdhomerun.h</filename>
    </modified>
    <modified>
      <diff>@@ -1,12 +1,12 @@
 /*
  * hdhomerun_channelscan.c
  *
- * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
+ * Copyright &#169; 2007 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 #include &quot;hdhomerun_os.h&quot;
@@ -23,154 +22,15 @@
 #include &quot;hdhomerun_debug.h&quot;
 #include &quot;hdhomerun_control.h&quot;
 #include &quot;hdhomerun_device.h&quot;
+#include &quot;hdhomerun_channels.h&quot;
 #include &quot;hdhomerun_channelscan.h&quot;
 
-#define CHANNEL_MAP_US_BCAST 1
-#define CHANNEL_MAP_US_CABLE 2
-#define CHANNEL_MAP_US_HRC 3
-#define CHANNEL_MAP_US_IRC 4
-
-struct channelscan_entry_t {
-	struct channelscan_entry_t *next;
-	uint8_t channel_map;
-	uint8_t channel;
-	uint32_t frequency;
-};
-
-struct channelscan_map_range_t {
-	uint8_t channel_map;
-	uint8_t channel_range_start;
-	uint8_t channel_range_end;
-	uint32_t frequency;
-	uint32_t spacing;
-};
-
-static const struct channelscan_map_range_t channelscan_map_ranges[] = {
-	{CHANNEL_MAP_US_BCAST,   2,   4,  57000000, 6000000},
-	{CHANNEL_MAP_US_BCAST,   5,   6,  79000000, 6000000},
-	{CHANNEL_MAP_US_BCAST,   7,  13, 177000000, 6000000},
-	{CHANNEL_MAP_US_BCAST,  14,  69, 473000000, 6000000},
-	{CHANNEL_MAP_US_BCAST,  70,  83, 809000000, 6000000},
-
-	{CHANNEL_MAP_US_CABLE,   1,   1,  75000000, 6000000},
-	{CHANNEL_MAP_US_CABLE,   2,   4,  57000000, 6000000},
-	{CHANNEL_MAP_US_CABLE,   5,   6,  79000000, 6000000},
-	{CHANNEL_MAP_US_CABLE,   7,  13, 177000000, 6000000},
-	{CHANNEL_MAP_US_CABLE,  14,  22, 123000000, 6000000},
-	{CHANNEL_MAP_US_CABLE,  23,  94, 219000000, 6000000},
-	{CHANNEL_MAP_US_CABLE,  95,  99,  93000000, 6000000},
-	{CHANNEL_MAP_US_CABLE, 100, 159, 651000000, 6000000},
-
-	{CHANNEL_MAP_US_HRC,     1,   1,  73753600, 6000300},
-	{CHANNEL_MAP_US_HRC,     2,   4,  55752700, 6000300},
-	{CHANNEL_MAP_US_HRC,     5,   6,  79753900, 6000300},
-	{CHANNEL_MAP_US_HRC,     7,  13, 175758700, 6000300},
-	{CHANNEL_MAP_US_HRC,    14,  22, 121756000, 6000300},
-	{CHANNEL_MAP_US_HRC,    23,  94, 217760800, 6000300},
-	{CHANNEL_MAP_US_HRC,    95,  99,  91754500, 6000300},
-	{CHANNEL_MAP_US_HRC,   100, 159, 649782400, 6000300},
-
-	{CHANNEL_MAP_US_IRC,     1,   1,  75012500, 6000000},
-	{CHANNEL_MAP_US_IRC,     2,   4,  57012500, 6000000},
-	{CHANNEL_MAP_US_IRC,     5,   6,  81012500, 6000000},
-	{CHANNEL_MAP_US_IRC,     7,  13, 177012500, 6000000},
-	{CHANNEL_MAP_US_IRC,    14,  22, 123012500, 6000000},
-	{CHANNEL_MAP_US_IRC,    23,  41, 219012500, 6000000},
-	{CHANNEL_MAP_US_IRC,    42,  42, 333025000, 6000000},
-	{CHANNEL_MAP_US_IRC,    43,  94, 339012500, 6000000},
-	{CHANNEL_MAP_US_IRC,    95,  97,  93012500, 6000000},
-	{CHANNEL_MAP_US_IRC,    98,  99, 111025000, 6000000},
-	{CHANNEL_MAP_US_IRC,   100, 159, 651012500, 6000000},
-
-	{0,                      0,   0,         0,       0}
-};
-
-struct channelscan_entry_t *channelscan_list = NULL;
-
-static const char *channelscan_map_name(uint8_t channel_map)
-{
-	switch (channel_map) {
-	case CHANNEL_MAP_US_BCAST:
-		return &quot;us-bcast&quot;;
-	case CHANNEL_MAP_US_CABLE:
-		return &quot;us-cable&quot;;
-	case CHANNEL_MAP_US_HRC:
-		return &quot;us-hrc&quot;;
-	case CHANNEL_MAP_US_IRC:
-		return &quot;us-irc&quot;;
-	default:
-		return &quot;unknown&quot;;
-	}
-}
-
-static void channelscan_list_build_insert(struct channelscan_entry_t *entry)
-{
-	struct channelscan_entry_t **pprev = &amp;channelscan_list;
-	struct channelscan_entry_t *p = channelscan_list;
-
-	while (p) {
-		if (p-&gt;frequency &gt; entry-&gt;frequency) {
-			break;
-		}
-
-		pprev = &amp;p-&gt;next;
-		p = p-&gt;next;
-	}
-
-	entry-&gt;next = p;
-	*pprev = entry;
-}
-
-static void channelscan_list_build_range(const struct channelscan_map_range_t *range)
-{
-	uint8_t channel;
-	for (channel = range-&gt;channel_range_start; channel &lt;= range-&gt;channel_range_end; channel++) {
-		struct channelscan_entry_t *entry = (struct channelscan_entry_t *)calloc(1, sizeof(struct channelscan_entry_t));
-		if (!entry) {
-			return;
-		}
-
-		entry-&gt;channel_map = range-&gt;channel_map;
-		entry-&gt;channel = channel;
-		entry-&gt;frequency = range-&gt;frequency + ((uint32_t)(channel - range-&gt;channel_range_start) * range-&gt;spacing);
-		entry-&gt;frequency = (entry-&gt;frequency / 62500) * 62500;
-
-		channelscan_list_build_insert(entry);
-	}
-}
-
-static void channelscan_list_build(void)
-{
-	if (channelscan_list) {
-		return;
-	}
-
-	const struct channelscan_map_range_t *range = channelscan_map_ranges;
-	while (range-&gt;channel_map) {
-		channelscan_list_build_range(range);
-		range++;
-	}
-}
-
-static void channelscan_list_free(void)
-{
-	while (1) {
-		struct channelscan_entry_t *entry = channelscan_list;
-		if (!entry) {
-			break;
-		}
-
-		channelscan_list = entry-&gt;next;
-		free(entry);
-	}
-}
-
 static int channelscan_execute_find_lock_internal(struct hdhomerun_device_t *hd, uint32_t frequency, struct hdhomerun_tuner_status_t *status)
 {
 	char channel_str[64];
 
-	/* Set 8vsb channel. */
-	sprintf(channel_str, &quot;8vsb:%ld&quot;, (unsigned long)frequency);
+	/* Set auto channel. */
+	sprintf(channel_str, &quot;auto:%ld&quot;, (unsigned long)frequency);
 	int ret = hdhomerun_device_set_tuner_channel(hd, channel_str);
 	if (ret &lt;= 0) {
 		return ret;
@@ -185,22 +45,6 @@ static int channelscan_execute_find_lock_internal(struct hdhomerun_device_t *hd,
 		return 1;
 	}
 
-	/* Set qam channel. */
-	sprintf(channel_str, &quot;qam:%ld&quot;, (unsigned long)frequency);
-	ret = hdhomerun_device_set_tuner_channel(hd, channel_str);
-	if (ret &lt;= 0) {
-		return ret;
-	}
-
-	/* Wait for lock. */
-	ret = hdhomerun_device_wait_for_lock(hd, status);
-	if (ret &lt;= 0) {
-		return ret;
-	}
-	if (status-&gt;lock_supported || status-&gt;lock_unsupported) {
-		return 1;
-	}
-
 	return 1;
 }
 
@@ -269,7 +113,7 @@ static int channelscan_execute_find_programs(struct hdhomerun_device_t *hd, char
 		}
 
 		same++;
-		if (same &gt;= 4 - 1) {
+		if (same &gt;= 8) {
 			break;
 		}
 	}
@@ -292,10 +136,10 @@ static int channelscan_execute_callback(channelscan_callback_t callback, va_list
 	return ret;
 }
 
-static int channelscan_execute_internal(struct hdhomerun_device_t *hd, int mode, struct channelscan_entry_t **pentry, channelscan_callback_t callback, va_list callback_ap)
+static int channelscan_execute_internal(struct hdhomerun_device_t *hd, uint32_t channel_map, struct hdhomerun_channel_entry_t **pentry, channelscan_callback_t callback, va_list callback_ap)
 {
-	struct channelscan_entry_t *entry = *pentry;
-	uint32_t frequency = entry-&gt;frequency;
+	struct hdhomerun_channel_entry_t *entry = *pentry;
+	uint32_t frequency = hdhomerun_channel_entry_frequency(entry);
 	char buffer[256];
 	int ret;
 
@@ -304,14 +148,15 @@ static int channelscan_execute_internal(struct hdhomerun_device_t *hd, int mode,
 	sprintf(ptr, &quot;%ld (&quot;, (unsigned long)frequency);
 	ptr = strchr(ptr, 0);
 	while (1) {
-		sprintf(ptr, &quot;%s:%d&quot;, channelscan_map_name(entry-&gt;channel_map), entry-&gt;channel);
+		const char *name = hdhomerun_channel_entry_name(entry);
+		strcpy(ptr, name);
 		ptr = strchr(ptr, 0);
 
-		entry = entry-&gt;next;
+		entry = hdhomerun_channel_list_next(channel_map, entry);
 		if (!entry) {
 			break;
 		}
-		if (entry-&gt;frequency != frequency) {
+		if (hdhomerun_channel_entry_frequency(entry) != frequency) {
 			break;
 		}
 
@@ -326,13 +171,12 @@ static int channelscan_execute_internal(struct hdhomerun_device_t *hd, int mode,
 		return ret;
 	}
 
-	if (mode == HDHOMERUN_CHANNELSCAN_MODE_CHANNELLIST) {
-		return 1;
-	}
-
 	/* Find lock. */
 	struct hdhomerun_tuner_status_t status;
 	ret = channelscan_execute_find_lock(hd, frequency, &amp;status);
+	if (ret &lt;= 0) {
+		return ret;
+	}
 
 	ptr = buffer;
 	sprintf(ptr, &quot;%s (ss=%u snq=%u seq=%u)&quot;, status.lock_str, status.signal_strength, status.signal_to_noise_quality, status.symbol_error_quality);
@@ -377,33 +221,30 @@ static int channelscan_execute_internal(struct hdhomerun_device_t *hd, int mode,
 	return 1;
 }
 
-int channelscan_execute_single(struct hdhomerun_device_t *hd, int mode, struct channelscan_entry_t **pentry, channelscan_callback_t callback, ...)
+int channelscan_execute_single(struct hdhomerun_device_t *hd, uint32_t channel_map, struct hdhomerun_channel_entry_t **pentry, channelscan_callback_t callback, ...)
 {
-	channelscan_list_build();
 	if (!*pentry) {
-		*pentry = channelscan_list;
+		*pentry = hdhomerun_channel_list_first(channel_map);
 	}
 
 	va_list callback_ap;
 	va_start(callback_ap, callback);
 
-	int result = channelscan_execute_internal(hd, mode, pentry, callback, callback_ap);
+	int result = channelscan_execute_internal(hd, channel_map, pentry, callback, callback_ap);
 
 	va_end(callback_ap);
 	return result;
 }
 
-int channelscan_execute_all(struct hdhomerun_device_t *hd, int mode, channelscan_callback_t callback, ...)
+int channelscan_execute_all(struct hdhomerun_device_t *hd, uint32_t channel_map, channelscan_callback_t callback, ...)
 {
-	channelscan_list_build();
-
 	va_list callback_ap;
 	va_start(callback_ap, callback);
 
 	int result = 0;
-	struct channelscan_entry_t *entry = channelscan_list;
+	struct hdhomerun_channel_entry_t *entry = hdhomerun_channel_list_first(channel_map);
 	while (entry) {
-		result = channelscan_execute_internal(hd, mode, &amp;entry, callback, callback_ap);
+		result = channelscan_execute_internal(hd, channel_map, &amp;entry, callback, callback_ap);
 		if (result &lt;= 0) {
 			break;
 		}</diff>
      <filename>libhdhomerun/hdhomerun_channelscan.c</filename>
    </modified>
    <modified>
      <diff>@@ -1,12 +1,12 @@
 /*
  * hdhomerun_channelscan.h
  *
- * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
+ * Copyright &#169; 2007 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,16 +14,12 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 struct channelscan_entry_t;
 
-#define HDHOMERUN_CHANNELSCAN_MODE_SCAN 0
-#define HDHOMERUN_CHANNELSCAN_MODE_CHANNELLIST 1
-
 typedef int (*channelscan_callback_t)(va_list ap, const char *type, const char *str);
 
-extern int channelscan_execute_single(struct hdhomerun_device_t *hd, int mode, struct channelscan_entry_t **pentry, channelscan_callback_t callback, ...);
-extern int channelscan_execute_all(struct hdhomerun_device_t *hd, int mode, channelscan_callback_t callback, ...);
+extern int channelscan_execute_single(struct hdhomerun_device_t *hd, uint32_t channel_map, struct hdhomerun_channel_entry_t **pentry, channelscan_callback_t callback, ...);
+extern int channelscan_execute_all(struct hdhomerun_device_t *hd, uint32_t channel_map, channelscan_callback_t callback, ...);</diff>
      <filename>libhdhomerun/hdhomerun_channelscan.h</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 #include &quot;hdhomerun.h&quot;
@@ -148,12 +147,6 @@ static int cmd_set(const char *item, const char *value)
 	return 1;
 }
 
-static int cmd_streaminfo(const char *tuner_str)
-{
-	fprintf(stderr, &quot;streaminfo: use \&quot;get /tuner&lt;n&gt;/streaminfo\&quot;\n&quot;);
-	return -1;
-}
-
 static int cmd_scan_callback(va_list ap, const char *type, const char *str)
 {
 	FILE *fp = va_arg(ap, FILE *);
@@ -174,6 +167,12 @@ static int cmd_scan(const char *tuner_str, const char *filename)
 		return -1;
 	}
 
+	uint32_t channel_map = hdhomerun_device_model_channel_map_all(hd);
+	if (channel_map == 0) {
+		fprintf(stderr, &quot;failed to detect channel_map set\n&quot;);
+		return -1;
+	}
+
 	FILE *fp = NULL;
 	if (filename) {
 		fp = fopen(filename, &quot;w&quot;);
@@ -183,11 +182,17 @@ static int cmd_scan(const char *tuner_str, const char *filename)
 		}
 	}
 
-	int ret = channelscan_execute_all(hd, HDHOMERUN_CHANNELSCAN_MODE_SCAN, cmd_scan_callback, fp);
+	int ret = channelscan_execute_all(hd, channel_map, cmd_scan_callback, fp);
 
 	if (fp) {
 		fclose(fp);
 	}
+	if (ret &lt; 0) {
+		fprintf(stderr, &quot;communication error sending request to hdhomerun device\n&quot;);
+	}
+	if (ret == 0) {
+		fprintf(stderr, &quot;error running channel scan\n&quot;);
+	}
 	return ret;
 }
 
@@ -240,13 +245,36 @@ static int cmd_upgrade(const char *filename)
 		return -1;
 	}
 
+	printf(&quot;uploading firmware...\n&quot;);
 	if (hdhomerun_device_upgrade(hd, fp) &lt;= 0) {
 		fprintf(stderr, &quot;error sending upgrade file to hdhomerun device\n&quot;);
 		fclose(fp);
 		return -1;
 	}
+	sleep(2);
+
+	printf(&quot;upgrading firmware...\n&quot;);
+	sleep(8);
+
+	printf(&quot;rebooting...\n&quot;);
+	int count = 0;
+	char *version_str;
+	while (1) {
+		if (hdhomerun_device_get_version(hd, &amp;version_str, NULL) &gt;= 0) {
+			break;
+		}
 
-	printf(&quot;upgrade complete\n&quot;);
+		count++;
+		if (count &gt; 30) {
+			fprintf(stderr, &quot;error finding device after firmware upgrade\n&quot;);
+			fclose(fp);
+			return -1;
+		}
+
+		sleep(1);
+	}
+
+	printf(&quot;upgrade complete - now running firmware %s\n&quot;, version_str);
 	return 0;
 }
 
@@ -272,13 +300,6 @@ static int main_cmd(int argc, char *argv[])
 		return cmd_set(argv[0], argv[1]);
 	}
 
-	if (contains(cmd, &quot;streaminfo&quot;)) {
-		if (argc &lt; 1) {
-			return help();
-		}
-		return cmd_streaminfo(argv[0]);
-	}
-
 	if (contains(cmd, &quot;scan&quot;)) {
 		if (argc &lt; 1) {
 			return help();
@@ -310,7 +331,6 @@ static int main_cmd(int argc, char *argv[])
 static int main_internal(int argc, char *argv[])
 {
 #if defined(__WINDOWS__)
-	// Start WinSock
 	WORD wVersionRequested = MAKEWORD(2, 0);
 	WSADATA wsaData;
 	WSAStartup(wVersionRequested, &amp;wsaData);
@@ -343,19 +363,22 @@ static int main_internal(int argc, char *argv[])
 		return -1;
 	}
 
-	/* Connect to device and check firmware version. */
-	int ret = hdhomerun_device_firmware_version_check(hd, 0);
-	if (ret &lt; 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, &quot;invalid device id: %08lX\n&quot;, (unsigned long)device_id_requested);
+	}
+
+	/* Connect to device and check model. */
+	const char *model = hdhomerun_device_get_model_str(hd);
+	if (!model) {
 		fprintf(stderr, &quot;unable to connect to device\n&quot;);
 		hdhomerun_device_destroy(hd);
 		return -1;
 	}
-	if (ret == 0) {
-		fprintf(stderr, &quot;WARNING: firmware upgrade needed for all operations to function\n&quot;);
-	}
 
 	/* Command. */
-	ret = main_cmd(argc, argv);
+	int ret = main_cmd(argc, argv);
 
 	/* Cleanup. */
 	hdhomerun_device_destroy(hd);</diff>
      <filename>libhdhomerun/hdhomerun_config.c</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,56 +14,72 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 #include &quot;hdhomerun_os.h&quot;
 #include &quot;hdhomerun_pkt.h&quot;
+#include &quot;hdhomerun_debug.h&quot;
 #include &quot;hdhomerun_discover.h&quot;
 #include &quot;hdhomerun_control.h&quot;
 
+#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 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;
 };
 
-struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip)
+static void hdhomerun_control_close_sock(struct hdhomerun_control_sock_t *cs)
 {
-	struct hdhomerun_control_sock_t *cs = (struct hdhomerun_control_sock_t *)malloc(sizeof(struct hdhomerun_control_sock_t));
-	if (!cs) {
-		return NULL;
+	if (cs-&gt;sock == -1) {
+		return;
 	}
-	
+
+	close(cs-&gt;sock);
+	cs-&gt;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-&gt;desired_device_id = device_id;
 	cs-&gt;desired_device_ip = device_ip;
 	cs-&gt;actual_device_id = 0;
 	cs-&gt;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 *)calloc(1, sizeof(struct hdhomerun_control_sock_t));
+	if (!cs) {
+		return NULL;
+	}
+
 	cs-&gt;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-&gt;sock != -1) {
-		close(cs-&gt;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)
 {
-	if (cs-&gt;sock == -1) {
-		return;
-	}
-
-	close(cs-&gt;sock);
-	cs-&gt;sock = -1;
+	cs-&gt;dbg = dbg;
 }
 
 static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs)
@@ -72,9 +88,15 @@ static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs
 		return TRUE;
 	}
 
+	if ((cs-&gt;desired_device_id == 0) &amp;&amp; (cs-&gt;desired_device_ip == 0)) {
+		hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_connect_sock: no device specified\n&quot;);
+		return FALSE;
+	}
+
 	/* Find device. */
 	struct hdhomerun_discover_device_t result;
-	if (hdhomerun_discover_find_devices_custom(cs-&gt;desired_device_ip, HDHOMERUN_DEVICE_TYPE_TUNER, cs-&gt;desired_device_id, &amp;result, 1) &lt;= 0) {
+	if (hdhomerun_discover_find_devices_custom(cs-&gt;desired_device_ip, HDHOMERUN_DEVICE_TYPE_WILDCARD, cs-&gt;desired_device_id, &amp;result, 1) &lt;= 0) {
+		hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_connect_sock: device not found\n&quot;);
 		return FALSE;
 	}
 	cs-&gt;actual_device_ip = result.ip_addr;
@@ -83,12 +105,13 @@ static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs
 	/* Create socket. */
 	cs-&gt;sock = (int)socket(AF_INET, SOCK_STREAM, 0);
 	if (cs-&gt;sock == -1) {
+		hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_connect_sock: failed to create socket (%d)\n&quot;, sock_getlasterror);
 		return FALSE;
 	}
 
 	/* Set timeouts. */
-	setsocktimeout(cs-&gt;sock, SOL_SOCKET, SO_SNDTIMEO, 5000);
-	setsocktimeout(cs-&gt;sock, SOL_SOCKET, SO_RCVTIMEO, 5000);
+	setsocktimeout(cs-&gt;sock, SOL_SOCKET, SO_SNDTIMEO, HDHOMERUN_CONTROL_SEND_TIMEOUT);
+	setsocktimeout(cs-&gt;sock, SOL_SOCKET, SO_RCVTIMEO, HDHOMERUN_CONTROL_RECV_TIMEOUT);
 
 	/* Initiate connection. */
 	struct sockaddr_in sock_addr;
@@ -97,6 +120,7 @@ static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs
 	sock_addr.sin_addr.s_addr = htonl(cs-&gt;actual_device_ip);
 	sock_addr.sin_port = htons(HDHOMERUN_CONTROL_TCP_PORT);
 	if (connect(cs-&gt;sock, (struct sockaddr *)&amp;sock_addr, sizeof(sock_addr)) != 0) {
+		hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_connect_sock: failed to connect (%d)\n&quot;, sock_getlasterror);
 		hdhomerun_control_close_sock(cs);
 		return FALSE;
 	}
@@ -105,16 +129,10 @@ static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs
 	return TRUE;
 }
 
-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-&gt;desired_device_id = device_id;
-	cs-&gt;desired_device_ip = device_ip;
-}
-
 uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs)
 {
 	if (!hdhomerun_control_connect_sock(cs)) {
+		hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_get_device_id: connect failed\n&quot;);
 		return 0;
 	}
 
@@ -124,137 +142,183 @@ uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs)
 uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs)
 {
 	if (!hdhomerun_control_connect_sock(cs)) {
+		hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_get_device_ip: connect failed\n&quot;);
 		return 0;
 	}
 
 	return cs-&gt;actual_device_ip;
 }
 
+uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs)
+{
+	return cs-&gt;desired_device_id;
+}
+
+uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs)
+{
+	return cs-&gt;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-&gt;dbg, &quot;hdhomerun_control_get_local_addr: connect failed\n&quot;);
 		return 0;
 	}
 
 	struct sockaddr_in sock_addr;
 	socklen_t sockaddr_size = sizeof(sock_addr);
 	if (getsockname(cs-&gt;sock, (struct sockaddr*)&amp;sock_addr, &amp;sockaddr_size) != 0) {
+		hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_get_local_addr: getsockname failed (%d)\n&quot;, sock_getlasterror);
 		return 0;
 	}
 
 	return ntohl(sock_addr.sin_addr.s_addr);
 }
 
-int hdhomerun_control_get_sock(struct hdhomerun_control_sock_t *cs)
-{
-	return cs-&gt;sock;
-}
-
-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-&gt;sock, (char *)start, (int)length, 0) != length) {
+	int length = (int)(tx_pkt-&gt;end - tx_pkt-&gt;start);
+	if (send(cs-&gt;sock, (char *)tx_pkt-&gt;start, (int)length, 0) != length) {
+		hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_send_sock: send failed (%d)\n&quot;, 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;
+	uint64_t stop_time = getcurrenttime() + recv_timeout;
+	hdhomerun_pkt_reset(rx_pkt);
 
-	fd_set readfds;
-	FD_ZERO(&amp;readfds);
-	FD_SET(cs-&gt;sock, &amp;readfds);
-
-	if (select(cs-&gt;sock+1, &amp;readfds, NULL, NULL, &amp;t) &lt; 0) {
-		return -1;
-	}
+	while (getcurrenttime() &lt; stop_time) {
+		struct timeval t;
+		t.tv_sec = 0;
+		t.tv_usec = 250000;
+	
+		fd_set readfds;
+		FD_ZERO(&amp;readfds);
+		FD_SET(cs-&gt;sock, &amp;readfds);
+	
+		if (select(cs-&gt;sock+1, &amp;readfds, NULL, NULL, &amp;t) &lt; 0) {
+			hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_recv_sock: select failed (%d)\n&quot;, sock_getlasterror);
+			hdhomerun_control_close_sock(cs);
+			return -1;
+		}
+	
+		if (!FD_ISSET(cs-&gt;sock, &amp;readfds)) {
+			continue;
+		}
+	
+		int rx_length = recv(cs-&gt;sock, (char *)rx_pkt-&gt;end, (int)(rx_pkt-&gt;limit - rx_pkt-&gt;end), 0);
+		if (rx_length &lt;= 0) {
+			hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_recv_sock: recv failed (%d)\n&quot;, sock_getlasterror);
+			hdhomerun_control_close_sock(cs);
+			return -1;
+		}
+		rx_pkt-&gt;end += rx_length;
 
-	if (!FD_ISSET(cs-&gt;sock, &amp;readfds)) {
-		return 0;
-	}
+		int ret = hdhomerun_pkt_open_frame(rx_pkt, ptype);
+		if (ret &lt; 0) {
+			hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_recv_sock: frame error\n&quot;);
+			hdhomerun_control_close_sock(cs);
+			return -1;
+		}
+		if (ret == 0) {
+			continue;
+		}
 
-	int length = recv(cs-&gt;sock, (char *)buffer, (int)(limit - buffer), 0);
-	if (length &lt;= 0) {
-		return -1;
+		return 1;
 	}
 
-	return length;
+	hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_recv_sock: timeout\n&quot;);
+	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() + 2000;
-	uint8_t *ptr = buffer;
-
-	while (getcurrenttime() &lt; timeout) {
-		int length = hdhomerun_control_recv_sock(cs, ptr, limit);
-		if (length &lt; 0) {
-			return -1;
+	hdhomerun_pkt_seal_frame(tx_pkt, type);
+
+	int i;
+	for (i = 0; i &lt; 2; i++) {
+		if (cs-&gt;sock == -1) {
+			if (!hdhomerun_control_connect_sock(cs)) {
+				hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_send_recv: connect failed\n&quot;);
+				return -1;
+			}
 		}
-		if (length == 0) {
+
+		if (hdhomerun_control_send_sock(cs, tx_pkt) &lt; 0) {
 			continue;
 		}
-		ptr += length;
+		if (!rx_pkt) {
+			return 1;
+		}
 
-		if (buffer + HDHOMERUN_MIN_PEEK_LENGTH &gt; ptr) {
+		uint16_t rsp_type;
+		if (hdhomerun_control_recv_sock(cs, rx_pkt, &amp;rsp_type, recv_timeout) &lt; 0) {
 			continue;
 		}
-
-		length = (int)hdhomerun_peek_packet_length(buffer);
-		if (buffer + length &gt; ptr) {
+		if (rsp_type != type + 1) {
+			hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_send_recv: unexpected frame type\n&quot;);
+			hdhomerun_control_close_sock(cs);
 			continue;
 		}
 
-		return length;
+		return 1;
 	}
 
+	hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_send_recv: failed\n&quot;);
 	return -1;
 }
 
-static int hdhomerun_control_get_set_internal(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)
 {
-	/* Send request. */
-	uint8_t *ptr = cs-&gt;buffer;
-	hdhomerun_write_get_set_request(&amp;ptr, name, value);
-	if (hdhomerun_control_send(cs, cs-&gt;buffer, ptr) &lt; 0) {
-		return -1;
-	}
+	return hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, type, HDHOMERUN_CONTROL_RECV_TIMEOUT);
+}
 
-	/* Receive response. */
-	int length = hdhomerun_control_recv(cs, cs-&gt;buffer, cs-&gt;buffer + sizeof(cs-&gt;buffer));
-	if (length &lt;= 0) {
-		return -1;
+static int hdhomerun_control_get_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror)
+{
+	struct hdhomerun_pkt_t *tx_pkt = &amp;cs-&gt;tx_pkt;
+	struct hdhomerun_pkt_t *rx_pkt = &amp;cs-&gt;rx_pkt;
+
+	/* Request. */
+	hdhomerun_pkt_reset(tx_pkt);
+
+	int name_len = (int)strlen(name) + 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);
+
+	if (value) {
+		int value_len = (int)strlen(value) + 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-&gt;buffer;
-	uint8_t *end = ptr + length;
-	int type = hdhomerun_process_packet(&amp;ptr, &amp;end);
-	if (type &lt; 0) {
-		return -1;
-	}
-	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) &lt; 0) {
 		return -1;
 	}
 
-	while (ptr &lt; end) {
+	/* Response. */
+	while (1) {
 		uint8_t tag;
 		size_t len;
-		uint8_t *val;
-		if (hdhomerun_read_tlv(&amp;ptr, end, &amp;tag, &amp;len, &amp;val) &lt; 0) {
+		uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &amp;tag, &amp;len);
+		if (!next) {
 			break;
 		}
+
 		switch (tag) {
 		case HDHOMERUN_TAG_GETSET_VALUE:
 			if (pvalue) {
-				*pvalue = (char *)val;
-				val[len] = 0;
+				*pvalue = (char *)rx_pkt-&gt;pos;
+				rx_pkt-&gt;pos[len] = 0;
 			}
 			if (perror) {
 				*perror = NULL;
@@ -266,39 +330,17 @@ static int hdhomerun_control_get_set_internal(struct hdhomerun_control_sock_t *c
 				*pvalue = NULL;
 			}
 			if (perror) {
-				*perror = (char *)val;
-				val[len] = 0;
+				*perror = (char *)rx_pkt-&gt;pos;
+				rx_pkt-&gt;pos[len] = 0;
 			}
 			return 0;
 		}
-	}
-
-	return -1;
-}
-
-static int hdhomerun_control_get_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror)
-{
-	if (cs-&gt;sock == -1) {
-		if (!hdhomerun_control_connect_sock(cs)) {
-			return -1;
-		}
-
-		int ret = hdhomerun_control_get_set_internal(cs, name, value, pvalue, perror);
-		if (ret &lt; 0) {
-			hdhomerun_control_close_sock(cs);
-			return -1;
-		}
 
-		return ret;
+		rx_pkt-&gt;pos = next;
 	}
 
-	int ret = hdhomerun_control_get_set_internal(cs, name, value, pvalue, perror);
-	if (ret &lt; 0) {
-		hdhomerun_control_close_sock(cs);
-		return hdhomerun_control_get_set(cs, name, value, pvalue, perror);
-	}
-
-	return ret;
+	hdhomerun_debug_printf(cs-&gt;dbg, &quot;hdhomerun_control_get_set: missing response tags\n&quot;);
+	return -1;
 }
 
 int hdhomerun_control_get(struct hdhomerun_control_sock_t *cs, const char *name, char **pvalue, char **perror)
@@ -313,13 +355,11 @@ int hdhomerun_control_set(struct hdhomerun_control_sock_t *cs, const char *name,
 
 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 = &amp;cs-&gt;tx_pkt;
+	struct hdhomerun_pkt_t *rx_pkt = &amp;cs-&gt;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);
@@ -327,10 +367,11 @@ int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade
 			break;
 		}
 
-		ptr = cs-&gt;buffer;
-		hdhomerun_write_upgrade_request(&amp;ptr, sequence, data, length);
-		if (hdhomerun_control_send(cs, cs-&gt;buffer, ptr) &lt; 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) &lt; 0) {
 			return -1;
 		}
 
@@ -342,12 +383,33 @@ int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade
 		return 0;
 	}
 
-	ptr = cs-&gt;buffer;
-	hdhomerun_write_upgrade_request(&amp;ptr, 0xFFFFFFFF, NULL, 0);
-	if (hdhomerun_control_send(cs, cs-&gt;buffer, ptr) &lt; 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) &lt; 0) {
 		return -1;
 	}
 
+	/* Check response. */
+	while (1) {
+		uint8_t tag;
+		size_t len;
+		uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &amp;tag, &amp;len);
+		if (!next) {
+			break;
+		}
+
+		switch (tag) {
+		case HDHOMERUN_TAG_ERROR_MESSAGE:
+			return 0;
+
+		default:
+			break;
+		}
+
+		rx_pkt-&gt;pos = next;
+	}
+
 	return 1;
 }</diff>
      <filename>libhdhomerun/hdhomerun_control.c</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 #ifdef __cplusplus
 extern &quot;C&quot; {
@@ -46,6 +45,9 @@ extern void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs);
  */
 extern uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs);
 extern uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs);
+extern uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs);
+extern uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs);
+
 extern void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, uint32_t device_id, uint32_t device_ip);
 
 /*
@@ -58,9 +60,9 @@ extern void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, ui
 extern uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs);
 
 /*
- * Get the low-level socket handle.
+ * Low-level communication.
  */
-extern int hdhomerun_control_get_sock(struct hdhomerun_control_sock_t *cs);
+extern 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.
@@ -93,6 +95,11 @@ extern int hdhomerun_control_set(struct hdhomerun_control_sock_t *cs, const char
  */
 extern int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade_file);
 
+/*
+ * Debug logging.
+ */
+extern void hdhomerun_control_set_debug(struct hdhomerun_control_sock_t *cs, struct hdhomerun_debug_t *dbg);
+
 #ifdef __cplusplus
 }
 #endif</diff>
      <filename>libhdhomerun/hdhomerun_control.h</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 /*
@@ -29,6 +28,13 @@
 #include &quot;hdhomerun_os.h&quot;
 #include &quot;hdhomerun_debug.h&quot;
 
+#if !defined(HDHOMERUN_DEBUG_HOST)
+#define HDHOMERUN_DEBUG_HOST &quot;debug.silicondust.com&quot;
+#endif
+#if !defined(HDHOMERUN_DEBUG_PORT)
+#define HDHOMERUN_DEBUG_PORT &quot;8002&quot;
+#endif
+
 struct hdhomerun_debug_message_t
 {
 	struct hdhomerun_debug_message_t *next;
@@ -39,19 +45,23 @@ struct hdhomerun_debug_message_t
 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;
 
-	pthread_mutex_t file_lock;
-	FILE *log_file;
+	uint64_t connect_delay;
 
-	pthread_mutex_t server_lock;
-	bool_t server_enabled;
-	int server_sock;
-	uint64_t server_delay;
+	char *file_name;
+	FILE *file_fp;
+	int sock;
 };
 
 static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg);
@@ -63,13 +73,11 @@ struct hdhomerun_debug_t *hdhomerun_debug_create(void)
 		return NULL;
 	}
 
-	dbg-&gt;log_file = NULL;
-	dbg-&gt;server_sock = -1;
-	dbg-&gt;terminate = FALSE;
+	dbg-&gt;sock = -1;
 
+	pthread_mutex_init(&amp;dbg-&gt;print_lock, NULL);
 	pthread_mutex_init(&amp;dbg-&gt;queue_lock, NULL);
-	pthread_mutex_init(&amp;dbg-&gt;file_lock, NULL);
-	pthread_mutex_init(&amp;dbg-&gt;server_lock, NULL);
+	pthread_mutex_init(&amp;dbg-&gt;send_lock, NULL);
 
 	if (pthread_create(&amp;dbg-&gt;thread, NULL, &amp;hdhomerun_debug_thread_execute, dbg) != 0) {
 		free(dbg);
@@ -79,64 +87,106 @@ struct hdhomerun_debug_t *hdhomerun_debug_create(void)
 	return dbg;
 }
 
+/* Send lock held by caller */
+static void hdhomerun_debug_close_file(struct hdhomerun_debug_t *dbg)
+{
+	if (!dbg-&gt;file_fp) {
+		return;
+	}
+
+	fclose(dbg-&gt;file_fp);
+	dbg-&gt;file_fp = NULL;
+}
+
+/* Send lock held by caller */
+static void hdhomerun_debug_close_sock(struct hdhomerun_debug_t *dbg)
+{
+	if (dbg-&gt;sock == -1) {
+		return;
+	}
+
+	close(dbg-&gt;sock);
+	dbg-&gt;sock = -1;
+}
+
 void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg)
 {
 	dbg-&gt;terminate = TRUE;
 	pthread_join(dbg-&gt;thread, NULL);
 
-	if (dbg-&gt;server_sock != -1) {
-		close(dbg-&gt;server_sock);
-	}
-	if (dbg-&gt;log_file) {
-		fclose(dbg-&gt;log_file);
+	hdhomerun_debug_close_file(dbg);
+	hdhomerun_debug_close_sock(dbg);
+
+	if (dbg-&gt;prefix) {
+		free(dbg-&gt;prefix);
 	}
 
 	free(dbg);
 }
 
-void hdhomerun_debug_enable_log_file(struct hdhomerun_debug_t *dbg, char *filename)
+void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix)
 {
-	pthread_mutex_lock(&amp;dbg-&gt;file_lock);
+	pthread_mutex_lock(&amp;dbg-&gt;print_lock);
 
-	if (!dbg-&gt;log_file) {
-		dbg-&gt;log_file = fopen(filename, &quot;a&quot;);
+	if (dbg-&gt;prefix) {
+		free(dbg-&gt;prefix);
+		dbg-&gt;prefix = NULL;
 	}
 
-	pthread_mutex_unlock(&amp;dbg-&gt;file_lock);
+	if (prefix) {
+		dbg-&gt;prefix = strdup(prefix);
+	}
+
+	pthread_mutex_unlock(&amp;dbg-&gt;print_lock);
 }
 
-void hdhomerun_debug_disable_log_file(struct hdhomerun_debug_t *dbg)
+void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename)
 {
-	pthread_mutex_lock(&amp;dbg-&gt;file_lock);
+	pthread_mutex_lock(&amp;dbg-&gt;send_lock);
+
+	if (!filename &amp;&amp; !dbg-&gt;file_name) {
+		pthread_mutex_unlock(&amp;dbg-&gt;send_lock);
+		return;
+	}
+	if (filename &amp;&amp; dbg-&gt;file_name) {
+		if (strcmp(filename, dbg-&gt;file_name) == 0) {
+			pthread_mutex_unlock(&amp;dbg-&gt;send_lock);
+			return;
+		}
+	}
+
+	hdhomerun_debug_close_file(dbg);
+	hdhomerun_debug_close_sock(dbg);
 
-	if (dbg-&gt;log_file) {
-		fclose(dbg-&gt;log_file);
-		dbg-&gt;log_file = NULL;
+	if (dbg-&gt;file_name) {
+		free(dbg-&gt;file_name);
+		dbg-&gt;file_name = NULL;
+	}
+	if (filename) {
+		dbg-&gt;file_name = strdup(filename);
 	}
 
-	pthread_mutex_unlock(&amp;dbg-&gt;file_lock);
+	pthread_mutex_unlock(&amp;dbg-&gt;send_lock);
 }
 
-void hdhomerun_debug_enable_support_server(struct hdhomerun_debug_t *dbg)
+void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg)
 {
-	pthread_mutex_lock(&amp;dbg-&gt;server_lock);
+	pthread_mutex_lock(&amp;dbg-&gt;send_lock);
 
-	dbg-&gt;server_enabled = TRUE;
+	dbg-&gt;enabled = TRUE;
 
-	pthread_mutex_unlock(&amp;dbg-&gt;server_lock);
+	pthread_mutex_unlock(&amp;dbg-&gt;send_lock);
 }
 
-void hdhomerun_debug_disable_support_server(struct hdhomerun_debug_t *dbg)
+void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg)
 {
-	pthread_mutex_lock(&amp;dbg-&gt;server_lock);
+	pthread_mutex_lock(&amp;dbg-&gt;send_lock);
 
-	dbg-&gt;server_enabled = FALSE;
-	if (dbg-&gt;server_sock != -1) {
-		close(dbg-&gt;server_sock);
-		dbg-&gt;server_sock = -1;
-	}
+	dbg-&gt;enabled = FALSE;
+	hdhomerun_debug_close_file(dbg);
+	hdhomerun_debug_close_sock(dbg);
 
-	pthread_mutex_unlock(&amp;dbg-&gt;server_lock);
+	pthread_mutex_unlock(&amp;dbg-&gt;send_lock);
 }
 
 bool_t hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg)
@@ -145,14 +195,24 @@ bool_t hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg)
 		return FALSE;
 	}
 
-	if (dbg-&gt;log_file) {
-		return TRUE;
-	}
-	if (dbg-&gt;server_enabled) {
-		return TRUE;
-	}
+	return dbg-&gt;enabled;
+}
+
+void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout)
+{
+	timeout = getcurrenttime() + timeout;
 
-	return FALSE;
+	while (getcurrenttime() &lt; timeout) {
+		pthread_mutex_lock(&amp;dbg-&gt;queue_lock);
+		struct hdhomerun_debug_message_t *message = dbg-&gt;queue_tail;
+		pthread_mutex_unlock(&amp;dbg-&gt;queue_lock);
+
+		if (!message) {
+			return;
+		}
+
+		usleep(10*1000);
+	}
 }
 
 void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...)
@@ -168,6 +228,9 @@ void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_
 	if (!dbg) {
 		return;
 	}
+	if (!dbg-&gt;enabled) {
+		return;
+	}
 
 	struct hdhomerun_debug_message_t *message = (struct hdhomerun_debug_message_t *)malloc(sizeof(struct hdhomerun_debug_message_t));
 	if (!message) {
@@ -180,14 +243,22 @@ void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_
 
 	time_t t = time(NULL);
 	strftime(ptr, end - ptr, &quot;%Y%m%d-%H:%M:%S &quot;, localtime(&amp;t));
+	ptr = strchr(ptr, 0);
+
+	pthread_mutex_lock(&amp;dbg-&gt;print_lock);
+
+	if (dbg-&gt;prefix) {
+		snprintf(ptr, end - ptr, &quot;%s &quot;, dbg-&gt;prefix);
+		ptr = strchr(ptr, 0);
+	}
+
+	pthread_mutex_unlock(&amp;dbg-&gt;print_lock);
 
-	ptr = strchr(ptr, '\0');
 	vsnprintf(ptr, end - ptr, fmt, args);
 
-	ptr = strchr(ptr, '\0') - 1;
+	ptr = strchr(ptr, 0) - 1;
 	if (*ptr++ != '\n') {
-		*ptr++ = '\n';
-		*ptr++ = 0;
+		strcpy(ptr, &quot;\n&quot;);
 	}
 
 	pthread_mutex_lock(&amp;dbg-&gt;queue_lock);
@@ -200,40 +271,52 @@ void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_
 	} else {
 		dbg-&gt;queue_tail = message;
 	}
+	dbg-&gt;queue_depth++;
 
 	pthread_mutex_unlock(&amp;dbg-&gt;queue_lock);
 }
 
-static void hdhomerun_debug_output_message_log_file(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
+/* 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-&gt;log_file) {
-		return;
+	if (!dbg-&gt;file_fp) {
+		uint64_t current_time = getcurrenttime();
+		if (current_time &lt; dbg-&gt;connect_delay) {
+			return FALSE;
+		}
+		dbg-&gt;connect_delay = current_time + 60*1000;
+
+		dbg-&gt;file_fp = fopen(dbg-&gt;file_name, &quot;a&quot;);
+		if (!dbg-&gt;file_fp) {
+			return FALSE;
+		}
 	}
 
-	fwrite(message-&gt;buffer, 1, strlen(message-&gt;buffer), dbg-&gt;log_file);
+	fprintf(dbg-&gt;file_fp, &quot;%s&quot;, message-&gt;buffer);
+	fflush(dbg-&gt;file_fp);
+
+	return TRUE;
 }
 
+/* Send lock held by caller */
 #if defined(__CYGWIN__)
-static void hdhomerun_debug_output_message_support_server(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
+static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
 {
+	return TRUE;
 }
 #else
-static void hdhomerun_debug_output_message_support_server(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
+static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
 {
-	if (!dbg-&gt;server_enabled) {
-		return;
-	}
-
-	if (dbg-&gt;server_sock == -1) {
+	if (dbg-&gt;sock == -1) {
 		uint64_t current_time = getcurrenttime();
-		if (current_time &lt; dbg-&gt;server_delay) {
-			return;
+		if (current_time &lt; dbg-&gt;connect_delay) {
+			return FALSE;
 		}
-		dbg-&gt;server_delay = current_time + 60*1000;
+		dbg-&gt;connect_delay = current_time + 60*1000;
 
-		dbg-&gt;server_sock = (int)socket(AF_INET, SOCK_STREAM, 0);
-		if (dbg-&gt;server_sock == -1) {
-			return;
+		dbg-&gt;sock = (int)socket(AF_INET, SOCK_STREAM, 0);
+		if (dbg-&gt;sock == -1) {
+			return FALSE;
 		}
 
 		struct addrinfo hints;
@@ -243,59 +326,64 @@ static void hdhomerun_debug_output_message_support_server(struct hdhomerun_debug
 		hints.ai_protocol = IPPROTO_TCP;
 
 		struct addrinfo *sock_info;
-		if (getaddrinfo(&quot;debug.silicondust.com&quot;, &quot;8002&quot;, &amp;hints, &amp;sock_info) != 0) {
-			close(dbg-&gt;server_sock);
-			dbg-&gt;server_sock = -1;
-			return;
+		if (getaddrinfo(HDHOMERUN_DEBUG_HOST, HDHOMERUN_DEBUG_PORT, &amp;hints, &amp;sock_info) != 0) {
+			hdhomerun_debug_close_sock(dbg);
+			return FALSE;
 		}
-		if (connect(dbg-&gt;server_sock, sock_info-&gt;ai_addr, (int)sock_info-&gt;ai_addrlen) != 0) {
+		if (connect(dbg-&gt;sock, sock_info-&gt;ai_addr, (int)sock_info-&gt;ai_addrlen) != 0) {
 			freeaddrinfo(sock_info);
-			close(dbg-&gt;server_sock);
-			dbg-&gt;server_sock = -1;
-			return;
+			hdhomerun_debug_close_sock(dbg);
+			return FALSE;
 		}
 		freeaddrinfo(sock_info);
 	}
 
-	size_t length = strlen(message-&gt;buffer);	
-	if (send(dbg-&gt;server_sock, (char *)message-&gt;buffer, (int)length, 0) != length) {
-		close(dbg-&gt;server_sock);
-		dbg-&gt;server_sock = -1;
-		return;
+	size_t length = strlen(message-&gt;buffer);
+	if (send(dbg-&gt;sock, (char *)message-&gt;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)
+static bool_t hdhomerun_debug_output_message(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
 {
-	pthread_mutex_lock(&amp;dbg-&gt;queue_lock);
+	pthread_mutex_lock(&amp;dbg-&gt;send_lock);
 
-	struct hdhomerun_debug_message_t *message = dbg-&gt;queue_tail;
-	if (!message) {
-		pthread_mutex_unlock(&amp;dbg-&gt;queue_lock);
-		return FALSE;
+	if (!dbg-&gt;enabled) {
+		pthread_mutex_unlock(&amp;dbg-&gt;send_lock);
+		return TRUE;
+	}
+
+	bool_t ret;
+	if (dbg-&gt;file_name) {
+		ret = hdhomerun_debug_output_message_file(dbg, message);
+	} else {
+		ret = hdhomerun_debug_output_message_sock(dbg, message);
 	}
 
+	pthread_mutex_unlock(&amp;dbg-&gt;send_lock);
+	return ret;
+}
+
+static void hdhomerun_debug_pop_and_free_message(struct hdhomerun_debug_t *dbg)
+{
+	pthread_mutex_lock(&amp;dbg-&gt;queue_lock);
+
+	struct hdhomerun_debug_message_t *message = dbg-&gt;queue_tail;
 	dbg-&gt;queue_tail = message-&gt;prev;
 	if (message-&gt;prev) {
 		message-&gt;prev-&gt;next = NULL;
 	} else {
 		dbg-&gt;queue_head = NULL;
 	}
+	dbg-&gt;queue_depth--;
 
 	pthread_mutex_unlock(&amp;dbg-&gt;queue_lock);
 
-	pthread_mutex_lock(&amp;dbg-&gt;file_lock);
-	hdhomerun_debug_output_message_log_file(dbg, message);
-	pthread_mutex_unlock(&amp;dbg-&gt;file_lock);
-
-	pthread_mutex_lock(&amp;dbg-&gt;server_lock);
-	hdhomerun_debug_output_message_support_server(dbg, message);
-	pthread_mutex_unlock(&amp;dbg-&gt;server_lock);
-
 	free(message);
-
-	return TRUE;
 }
 
 static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg)
@@ -303,9 +391,28 @@ static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg)
 	struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)arg;
 
 	while (!dbg-&gt;terminate) {
-		if (!hdhomerun_debug_output_message(dbg)) {
+
+		pthread_mutex_lock(&amp;dbg-&gt;queue_lock);
+		struct hdhomerun_debug_message_t *message = dbg-&gt;queue_tail;
+		uint32_t queue_depth = dbg-&gt;queue_depth;
+		pthread_mutex_unlock(&amp;dbg-&gt;queue_lock);
+
+		if (!message) {
 			sleep(1);
+			continue;
 		}
+
+		if (queue_depth &gt; 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>
      <filename>libhdhomerun/hdhomerun_debug.c</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 /*
@@ -35,13 +34,14 @@ struct hdhomerun_debug_t;
 extern struct hdhomerun_debug_t *hdhomerun_debug_create(void);
 extern void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg);
 
-extern void hdhomerun_debug_enable_log_file(struct hdhomerun_debug_t *dbg, char *filename);
-extern void hdhomerun_debug_disable_log_file(struct hdhomerun_debug_t *dbg);
-extern void hdhomerun_debug_enable_support_server(struct hdhomerun_debug_t *dbg);
-extern void hdhomerun_debug_disable_support_server(struct hdhomerun_debug_t *dbg);
-
+extern void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix);
+extern void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename);
+extern void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg);
+extern void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg);
 extern bool_t hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg);
 
+extern void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout);
+
 extern void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...);
 extern void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_list args);
 </diff>
      <filename>libhdhomerun/hdhomerun_debug.h</filename>
    </modified>
    <modified>
      <diff>@@ -1,12 +1,12 @@
 /*
- * hdhomerun_record.c
+ * hdhomerun_device.c
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,13 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 #include &quot;hdhomerun_os.h&quot;
 #include &quot;hdhomerun_pkt.h&quot;
 #include &quot;hdhomerun_debug.h&quot;
+#include &quot;hdhomerun_channels.h&quot;
 #include &quot;hdhomerun_discover.h&quot;
 #include &quot;hdhomerun_control.h&quot;
 #include &quot;hdhomerun_video.h&quot;
@@ -29,16 +29,33 @@
 struct hdhomerun_device_t {
 	struct hdhomerun_control_sock_t *cs;
 	struct hdhomerun_video_sock_t *vs;
+	struct hdhomerun_debug_t *dbg;
 	unsigned int tuner;
 	char name[32];
-	char result_buffer[1024];
+	char model[32];
 };
 
-static void hdhomerun_device_update_name(struct hdhomerun_device_t *hd)
+static void hdhomerun_device_set_update(struct hdhomerun_device_t *hd)
 {
+	/* Clear cached information. */
+	*hd-&gt;model = 0;
+
+	/* New name. */
 	sprintf(hd-&gt;name, &quot;%08lX-%u&quot;, (unsigned long)hdhomerun_control_get_device_id(hd-&gt;cs), hd-&gt;tuner);
 }
 
+void hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip)
+{
+	hdhomerun_control_set_device(hd-&gt;cs, device_id, device_ip);
+	hdhomerun_device_set_update(hd);
+}
+
+void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner)
+{
+	hd-&gt;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));
@@ -46,63 +63,130 @@ struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t
 		return NULL;
 	}
 
-	hd-&gt;tuner = tuner;
-
-	hd-&gt;cs = hdhomerun_control_create(device_id, device_ip);
+	hd-&gt;cs = hdhomerun_control_create(0, 0);
 	if (!hd-&gt;cs) {
 		free(hd);
 		return NULL;
 	}
 
-	hdhomerun_device_update_name(hd);
+	hdhomerun_device_set_device(hd, device_id, device_ip);
+	hdhomerun_device_set_tuner(hd, tuner);
 
 	return hd;
 }
 
-struct hdhomerun_device_t *hdhomerun_device_create_from_str(const char *device_str)
+void hdhomerun_device_destroy(struct hdhomerun_device_t *hd)
 {
-	unsigned long a[4];
-	if (sscanf(device_str, &quot;%lu.%lu.%lu.%lu&quot;, &amp;a[0], &amp;a[1], &amp;a[2], &amp;a[3]) == 4) {
-		unsigned long device_ip = (a[0] &lt;&lt; 24) | (a[1] &lt;&lt; 16) | (a[2] &lt;&lt; 8) | (a[3] &lt;&lt; 0);
-		return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, (uint32_t)device_ip, 0);
+	if (hd-&gt;vs) {
+		hdhomerun_video_destroy(hd-&gt;vs);
 	}
 
-	unsigned long device_id;
-	unsigned int tuner;
-	if (sscanf(device_str, &quot;%lx:%u&quot;, &amp;device_id, &amp;tuner) == 2) {
-		return hdhomerun_device_create((uint32_t)device_id, 0, tuner);
+	hdhomerun_control_destroy(hd-&gt;cs);
+
+	free(hd);
+}
+
+static bool_t is_hex_char(char c)
+{
+	if ((c &gt;= '0') &amp;&amp; (c &lt;= '9')) {
+		return TRUE;
 	}
-	if (sscanf(device_str, &quot;%lx-%u&quot;, &amp;device_id, &amp;tuner) == 2) {
-		return hdhomerun_device_create((uint32_t)device_id, 0, tuner);
+	if ((c &gt;= 'A') &amp;&amp; (c &lt;= 'F')) {
+		return TRUE;
+	}
+	if ((c &gt;= 'a') &amp;&amp; (c &lt;= '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 &lt; 8; i++) {
+		if (!is_hex_char(*ptr++)) {
+			return NULL;
+		}
 	}
-	if (sscanf(device_str, &quot;%lx&quot;, &amp;device_id) == 1) {
+
+	if (*ptr == 0) {
+		unsigned long device_id;
+		if (sscanf(device_str, &quot;%lx&quot;, &amp;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, &quot;%lx-%u&quot;, &amp;device_id, &amp;tuner) != 2) {
+			return NULL;
+		}
+		return hdhomerun_device_create((uint32_t)device_id, 0, tuner);
+	}
+
 	return NULL;
 }
 
-void hdhomerun_device_destroy(struct hdhomerun_device_t *hd)
+static struct hdhomerun_device_t *hdhomerun_device_create_from_str_ip(const char *device_str)
 {
-	if (hd-&gt;vs) {
-		hdhomerun_video_destroy(hd-&gt;vs);
+	unsigned long a[4];
+	if (sscanf(device_str, &quot;%lu.%lu.%lu.%lu&quot;, &amp;a[0], &amp;a[1], &amp;a[2], &amp;a[3]) != 4) {
+		return NULL;
 	}
 
-	hdhomerun_control_destroy(hd-&gt;cs);
-
-	free(hd);
+	unsigned long device_ip = (a[0] &lt;&lt; 24) | (a[1] &lt;&lt; 16) | (a[2] &lt;&lt; 8) | (a[3] &lt;&lt; 0);
+	return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, (uint32_t)device_ip, 0);
 }
 
-void hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip)
+static struct hdhomerun_device_t *hdhomerun_device_create_from_str_dns(const char *device_str)
 {
-	hdhomerun_control_set_device(hd-&gt;cs, device_id, device_ip);
-	hdhomerun_device_update_name(hd);
+#if defined(__CYGWIN__)
+	return NULL;
+#else
+	struct addrinfo hints;
+	memset(&amp;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, &quot;65001&quot;, &amp;hints, &amp;sock_info) != 0) {
+		return NULL;
+	}
+
+	struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info-&gt;ai_addr;
+	uint32_t device_ip = ntohl(sock_addr-&gt;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
 }
 
-void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner)
+struct hdhomerun_device_t *hdhomerun_device_create_from_str(const char *device_str)
 {
-	hd-&gt;tuner = tuner;
-	hdhomerun_device_update_name(hd);
+	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)
@@ -120,6 +204,16 @@ int hdhomerun_device_set_tuner_from_str(struct hdhomerun_device_t *hd, const cha
 	return -1;
 }
 
+void hdhomerun_device_set_debug(struct hdhomerun_device_t *hd, struct hdhomerun_debug_t *dbg)
+{
+	hd-&gt;dbg = dbg;
+	hdhomerun_control_set_debug(hd-&gt;cs, dbg);
+
+	if (hd-&gt;vs) {
+		hdhomerun_video_set_debug(hd-&gt;vs, dbg);
+	}
+}
+
 uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd)
 {
 	return hdhomerun_control_get_device_id(hd-&gt;cs);
@@ -130,6 +224,16 @@ uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd)
 	return hdhomerun_control_get_device_ip(hd-&gt;cs);
 }
 
+uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd)
+{
+	return hdhomerun_control_get_device_id_requested(hd-&gt;cs);
+}
+
+uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd)
+{
+	return hdhomerun_control_get_device_ip_requested(hd-&gt;cs);
+}
+
 unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd)
 {
 	return hd-&gt;tuner;
@@ -142,9 +246,16 @@ struct hdhomerun_control_sock_t *hdhomerun_device_get_control_sock(struct hdhome
 
 struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdhomerun_device_t *hd)
 {
+	if (hd-&gt;vs) {
+		return hd-&gt;vs;
+	}
+
+	hd-&gt;vs = hdhomerun_video_create(0, VIDEO_DATA_BUFFER_SIZE_1S * 2);
 	if (!hd-&gt;vs) {
-		hd-&gt;vs = hdhomerun_video_create(0, VIDEO_DATA_BUFFER_SIZE_1S);
+		return NULL;
 	}
+
+	hdhomerun_video_set_debug(hd-&gt;vs, hd-&gt;dbg);
 	return hd-&gt;vs;
 }
 
@@ -166,6 +277,55 @@ static uint32_t hdhomerun_device_get_status_parse(const char *status_str, const
 	return (uint32_t)value;
 }
 
+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 (strstr(status-&gt;lock_str, &quot;8vsb&quot;)) {
+		ss_yellow_min = 50;	/* -30dBmV */
+		ss_green_min = 75;	/* -15dBmV */
+	} else if (strstr(status-&gt;lock_str, &quot;qam64&quot;)) {
+		ss_yellow_min = 80;	/* -12dBmV */
+		ss_green_min = 90;	/* -6dBmV */
+	} else if (strstr(status-&gt;lock_str, &quot;qam256&quot;)) {
+		ss_yellow_min = 80;	/* -12dBmV */
+		ss_green_min = 90;	/* -6dBmV */
+	} else {
+		return HDHOMERUN_STATUS_COLOR_NEUTRAL;
+	}
+
+	if (status-&gt;signal_strength &gt;= ss_green_min) {
+		return HDHOMERUN_STATUS_COLOR_GREEN;
+	}
+	if (status-&gt;signal_strength &gt;= 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-&gt;signal_to_noise_quality &gt;= 60) {
+		return HDHOMERUN_STATUS_COLOR_GREEN;
+	}
+	if (status-&gt;signal_to_noise_quality &gt;= 40) {
+		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-&gt;symbol_error_quality &gt;= 100) {
+		return HDHOMERUN_STATUS_COLOR_GREEN;
+	}
+
+	return HDHOMERUN_STATUS_COLOR_RED;
+}
+
 int hdhomerun_device_get_tuner_status(struct hdhomerun_device_t *hd, struct hdhomerun_tuner_status_t *status)
 {
 	memset(status, 0, sizeof(struct hdhomerun_tuner_status_t));
@@ -250,6 +410,55 @@ int hdhomerun_device_get_tuner_target(struct hdhomerun_device_t *hd, char **ptar
 	return hdhomerun_control_get(hd-&gt;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, &quot;/tuner%u/plotsample&quot;, hd-&gt;tuner);
+
+	char *result;
+	int ret = hdhomerun_control_get(hd-&gt;cs, name, &amp;result, NULL);
+	if (ret &lt;= 0) {
+		return ret;
+	}
+
+	struct hdhomerun_plotsample_t *samples = (struct hdhomerun_plotsample_t *)result;
+	*psamples = samples;
+	size_t count = 0;
+
+	while (1) {
+		char *ptr = strchr(result, ' ');
+		if (!ptr) {
+			break;
+		}
+		*ptr++ = 0;
+
+		unsigned long raw;
+		if (sscanf(result, &quot;%lx&quot;, &amp;raw) != 1) {
+			break;
+		}
+
+		uint16_t real = (raw &gt;&gt; 12) &amp; 0x0FFF;
+		if (real &amp; 0x0800) {
+			real |= 0xF000;
+		}
+
+		uint16_t imag = (raw &gt;&gt; 0) &amp; 0x0FFF;
+		if (imag &amp; 0x0800) {
+			imag |= 0xF000;
+		}
+
+		samples-&gt;real = (int16_t)real;
+		samples-&gt;imag = (int16_t)imag;
+		samples++;
+		count++;
+
+		result = ptr;
+	}
+
+	*pcount = count;
+	return 1;
+}
+
 int hdhomerun_device_get_ir_target(struct hdhomerun_device_t *hd, char **ptarget)
 {
 	return hdhomerun_control_get(hd-&gt;cs, &quot;/ir/target&quot;, ptarget, NULL);
@@ -430,9 +639,9 @@ int hdhomerun_device_set_var(struct hdhomerun_device_t *hd, const char *name, co
 
 int hdhomerun_device_wait_for_lock(struct hdhomerun_device_t *hd, struct hdhomerun_tuner_status_t *status)
 {
-	/* Wait for up to 1.5 seconds for lock. */
+	/* Wait for up to 2.5 seconds for lock. */
 	int i;
-	for (i = 0; i &lt; 6; i++) {
+	for (i = 0; i &lt; 10; i++) {
 		usleep(250000);
 
 		/* Get status to check for lock. Quality numbers will not be valid yet. */
@@ -500,6 +709,63 @@ int hdhomerun_device_firmware_version_check(struct hdhomerun_device_t *hd, uint3
 	return 1;
 }
 
+const char *hdhomerun_device_get_model_str(struct hdhomerun_device_t *hd)
+{
+	if (*hd-&gt;model) {
+		return hd-&gt;model;
+	}
+
+	char *model_str;
+	int ret = hdhomerun_control_get(hd-&gt;cs, &quot;/sys/model&quot;, &amp;model_str, NULL);
+	if (ret &lt; 0) {
+		return NULL;
+	}
+	if (ret == 0) {
+		strcpy(hd-&gt;model, &quot;hdhomerun_atsc&quot;);
+		return hd-&gt;model;
+	}
+
+	strcpy(hd-&gt;model, model_str);
+	return hd-&gt;model;
+}
+
+uint32_t hdhomerun_device_model_channel_map_all(struct hdhomerun_device_t *hd)
+{
+	return hdhomerun_device_model_channel_map_antenna(hd) | hdhomerun_device_model_channel_map_cable(hd);
+}
+
+uint32_t hdhomerun_device_model_channel_map_antenna(struct hdhomerun_device_t *hd)
+{
+	const char *model = hdhomerun_device_get_model_str(hd);
+	if (!model) {
+		return 0;
+	}
+
+	if (strstr(model, &quot;atsc&quot;)) {
+		return CHANNEL_MAP_US_BCAST;
+	}
+
+	if (strstr(model, &quot;dvbt&quot;)) {
+		return CHANNEL_MAP_UK_BCAST | CHANNEL_MAP_NZ_BCAST;
+	}
+
+	return 0;
+}
+
+uint32_t hdhomerun_device_model_channel_map_cable(struct hdhomerun_device_t *hd)
+{
+	const char *model = hdhomerun_device_get_model_str(hd);
+	if (!model) {
+		return 0;
+	}
+
+	if (strstr(model, &quot;atsc&quot;)) {
+		return CHANNEL_MAP_US_CABLE | CHANNEL_MAP_US_HRC | CHANNEL_MAP_US_IRC;
+	}
+
+	return 0;
+}
+
 int hdhomerun_device_upgrade(struct hdhomerun_device_t *hd, FILE *upgrade_file)
 {
 	hdhomerun_control_set(hd-&gt;cs, &quot;/tuner0/channel&quot;, &quot;none&quot;, NULL, NULL);
@@ -507,9 +773,9 @@ int hdhomerun_device_upgrade(struct hdhomerun_device_t *hd, FILE *upgrade_file)
 	return hdhomerun_control_upgrade(hd-&gt;cs, upgrade_file);
 }
 
-void hdhomerun_device_debug_print_video_stats(struct hdhomerun_device_t *hd, struct hdhomerun_debug_t *dbg)
+void hdhomerun_device_debug_print_video_stats(struct hdhomerun_device_t *hd)
 {
-	if (!hdhomerun_debug_enabled(dbg)) {
+	if (!hdhomerun_debug_enabled(hd-&gt;dbg)) {
 		return;
 	}
 
@@ -520,17 +786,17 @@ void hdhomerun_device_debug_print_video_stats(struct hdhomerun_device_t *hd, str
 	char *error_str;
 	int ret = hdhomerun_control_get(hd-&gt;cs, name, &amp;debug_str, &amp;error_str);
 	if (ret &lt; 0) {
-		hdhomerun_debug_printf(dbg, &quot;%s video dev: communication error getting debug stats\n&quot;, hd-&gt;name);
+		hdhomerun_debug_printf(hd-&gt;dbg, &quot;video dev: communication error getting debug stats\n&quot;);
 		return;
 	}
 
 	if (error_str) {
-		hdhomerun_debug_printf(dbg, &quot;%s video dev: %s\n&quot;, hd-&gt;name, error_str);
+		hdhomerun_debug_printf(hd-&gt;dbg, &quot;video dev: %s\n&quot;, error_str);
 	} else {
-		hdhomerun_debug_printf(dbg, &quot;%s video dev: %s\n&quot;, hd-&gt;name, debug_str);
+		hdhomerun_debug_printf(hd-&gt;dbg, &quot;video dev: %s\n&quot;, debug_str);
 	}
 
 	if (hd-&gt;vs) {
-		hdhomerun_video_debug_print_stats(hd-&gt;vs, dbg, hd-&gt;name);
+		hdhomerun_video_debug_print_stats(hd-&gt;vs);
 	}
 }</diff>
      <filename>libhdhomerun/hdhomerun_device.c</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 #ifdef __cplusplus
@@ -26,6 +25,11 @@ extern &quot;C&quot; {
 #define HDHOMERUN_DEVICE_MAX_LOCK_TO_DATA_TIME 2000
 #define HDHOMERUN_DEVICE_MAX_TUNE_TO_DATA_TIME (HDHOMERUN_DEVICE_MAX_TUNE_TO_LOCK_TIME + HDHOMERUN_DEVICE_MAX_LOCK_TO_DATA_TIME)
 
+#define HDHOMERUN_STATUS_COLOR_NEUTRAL	0xFFFFFFFF
+#define HDHOMERUN_STATUS_COLOR_RED		0xFFFF0000
+#define HDHOMERUN_STATUS_COLOR_YELLOW	0xFFFFFF00
+#define HDHOMERUN_STATUS_COLOR_GREEN	0xFF008000
+
 struct hdhomerun_device_t;
 
 struct hdhomerun_tuner_status_t {
@@ -41,6 +45,11 @@ struct hdhomerun_tuner_status_t {
 	uint32_t packets_per_second;
 };
 
+struct hdhomerun_plotsample_t {
+	int16_t real;
+	int16_t imag;
+};
+
 /*
  * Create a device object.
  *
@@ -65,7 +74,6 @@ struct hdhomerun_tuner_status_t {
  * 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:
  *     &lt;device id&gt;
- *     &lt;device id&gt;:&lt;tuner index&gt;
  *     &lt;device id&gt;-&lt;tuner index&gt;
  *     &lt;ip address&gt;
  * If the tuner index is not included in the device_str then it is set to zero.
@@ -85,7 +93,10 @@ extern void hdhomerun_device_destroy(struct hdhomerun_device_t *hd);
  */
 extern uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd);
 extern uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd);
+extern uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd);
+extern uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd);
 extern unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd);
+
 extern void hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip);
 extern void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner);
 extern int hdhomerun_device_set_tuner_from_str(struct hdhomerun_device_t *hd, const char *tuner_str);
@@ -117,10 +128,20 @@ extern int hdhomerun_device_get_tuner_channelmap(struct hdhomerun_device_t *hd,
 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, char **pprogram);
 extern int hdhomerun_device_get_tuner_target(struct hdhomerun_device_t *hd, char **ptarget);
+extern int hdhomerun_device_get_tuner_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount);
 extern int hdhomerun_device_get_ir_target(struct hdhomerun_device_t *hd, char **ptarget);
 extern int hdhomerun_device_get_lineup_location(struct hdhomerun_device_t *hd, char **plocation);
 extern int hdhomerun_device_get_version(struct hdhomerun_device_t *hd, char **pversion_str, uint32_t *pversion_num);
 
+extern uint32_t hdhomerun_device_get_tuner_status_ss_color(struct hdhomerun_tuner_status_t *status);
+extern uint32_t hdhomerun_device_get_tuner_status_snq_color(struct hdhomerun_tuner_status_t *status);
+extern uint32_t hdhomerun_device_get_tuner_status_seq_color(struct hdhomerun_tuner_status_t *status);
+
+extern const char *hdhomerun_device_get_model_str(struct hdhomerun_device_t *hd);
+extern uint32_t hdhomerun_device_model_channel_map_all(struct hdhomerun_device_t *hd);
+extern uint32_t hdhomerun_device_model_channel_map_antenna(struct hdhomerun_device_t *hd);
+extern uint32_t hdhomerun_device_model_channel_map_cable(struct hdhomerun_device_t *hd);
+
 /*
  * Set operations.
  *
@@ -224,7 +245,8 @@ extern struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdh
 /*
  * Debug print internal stats.
  */
-extern void hdhomerun_device_debug_print_video_stats(struct hdhomerun_device_t *hd, struct hdhomerun_debug_t *dbg);
+extern void hdhomerun_device_set_debug(struct hdhomerun_device_t *hd, struct hdhomerun_debug_t *dbg);
+extern void hdhomerun_device_debug_print_video_stats(struct hdhomerun_device_t *hd);
 
 #ifdef __cplusplus
 }</diff>
      <filename>libhdhomerun/hdhomerun_device.h</filename>
    </modified>
    <modified>
      <diff>@@ -1,12 +1,12 @@
 /*
  * hdhomerun_discover.c
  *
- * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
+ * Copyright &#169; 2006-2007 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 #include &quot;hdhomerun_os.h&quot;
@@ -24,85 +23,68 @@
 
 #if defined(__CYGWIN__) || defined(__WINDOWS__)
 #include &lt;windows.h&gt;
-#include &lt;iptypes.h&gt;
 #include &lt;iphlpapi.h&gt;
+#define USE_IPHLPAPI 1
 #endif
 
-#if defined(__APPLE__) || defined(__BSD__)
+#if defined(__linux__) || defined(__APPLE__) || defined(BSD)
 #include &lt;ifaddrs.h&gt;
+#define USE_IFADDRS 1
 #endif
 
+#define HDHOMERUN_DISOCVER_MAX_SOCK_COUNT 16
+
 struct hdhomerun_discover_sock_t {
 	int sock;
+	uint32_t broadcast_ip;
+};
+
+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 struct hdhomerun_discover_sock_t *hdhomerun_discover_create(void)
+static void hdhomerun_discover_sock_create(struct hdhomerun_discover_t *ds, uint32_t local_ip, uint32_t 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-&gt;sock_count &gt;= HDHOMERUN_DISOCVER_MAX_SOCK_COUNT) {
+		return;
 	}
-	
+
 	/* Create socket. */
-	ds-&gt;sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
-	if (ds-&gt;sock == -1) {
-		free(ds);
-		return NULL;
+	int sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock == -1) {
+		return;
 	}
 
 	/* Set timeouts. */
-	setsocktimeout(ds-&gt;sock, SOL_SOCKET, SO_SNDTIMEO, 1000);
-	setsocktimeout(ds-&gt;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-&gt;sock, SOL_SOCKET, SO_BROADCAST, (char *)&amp;sock_opt, sizeof(sock_opt));
+	setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&amp;sock_opt, sizeof(sock_opt));
 
 	/* Bind socket. */
 	struct sockaddr_in sock_addr;
 	memset(&amp;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-&gt;sock, (struct sockaddr *)&amp;sock_addr, sizeof(sock_addr)) != 0) {
-		close(ds-&gt;sock);
-		free(ds);
-		return NULL;
-	}
-
-	/* Success. */
-	return ds;
-}
-
-static void hdhomerun_discover_destroy(struct hdhomerun_discover_sock_t *ds)
-{
-	close(ds-&gt;sock);
-	free(ds);
-}
-
-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(&amp;ptr, device_type, device_id);
-
-	struct sockaddr_in sock_addr;
-	memset(&amp;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-&gt;sock, (char *)buffer, (int)length, 0, (struct sockaddr *)&amp;sock_addr, sizeof(sock_addr)) != length) {
-		return -1;
+	if (bind(sock, (struct sockaddr *)&amp;sock_addr, sizeof(sock_addr)) != 0) {
+		close(sock);
+		return;
 	}
 
-	return 0;
+	/* Write sock entry. */
+	struct hdhomerun_discover_sock_t *dss = &amp;ds-&gt;socks[ds-&gt;sock_count++];
+	dss-&gt;sock = sock;
+	dss-&gt;broadcast_ip = local_ip | ~mask;
 }
 
-#if 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);
@@ -111,37 +93,29 @@ static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds
 	if (Ret != NO_ERROR) {
 		free(pAdapterInfo);
 		if (Ret != ERROR_BUFFER_OVERFLOW) {
-			return -1;
+			return;
 		}
 		pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); 
 		Ret = GetAdaptersInfo(pAdapterInfo, &amp;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 = &amp;pAdapter-&gt;IpAddressList;
 		while (pIPAddr) {
-			uint32_t addr = ntohl(inet_addr(pIPAddr-&gt;IpAddress.String));
+			uint32_t local_ip = ntohl(inet_addr(pIPAddr-&gt;IpAddress.String));
 			uint32_t mask = ntohl(inet_addr(pIPAddr-&gt;IpMask.String));
-			
-			uint32_t broadcast = addr | ~mask;
-			if ((broadcast == 0x00000000) || (broadcast == 0xFFFFFFFF)) {
-				pIPAddr = pIPAddr-&gt;Next;
-				continue;
-			}
 
-			if (hdhomerun_discover_send_packet(ds, broadcast, device_type, device_id) &lt; 0) {
+			if (local_ip == 0) {
 				pIPAddr = pIPAddr-&gt;Next;
 				continue;
 			}
 
-			send_count++;
-
+			hdhomerun_discover_sock_create(ds, local_ip, mask);
 			pIPAddr = pIPAddr-&gt;Next;
 		}
 
@@ -149,72 +123,18 @@ static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds
 	}
 
 	free(pAdapterInfo);
-
-	if (send_count == 0) {
-		return -1;
-	}
-
-	return 0;
 }
+#endif
 
-#elif defined(__linux__)
-
-static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, uint32_t device_type, uint32_t device_id)
-{
-	FILE *fp = fopen(&quot;/proc/net/route&quot;, &quot;r&quot;);
-	if (!fp) {
-		return -1;
-	}
-
-	unsigned int send_count = 0;
-	while (1) {
-		char line[256];
-		if (!fgets(line, sizeof(line), fp)) {
-			break;
-		}
-		line[255] = 0;
-
-		uint32_t dest;
-		uint32_t mask;
-		if (sscanf(line, &quot;%*s %x %*x %*x %*d %*d %*d %x&quot;, &amp;dest, &amp;mask) != 2) {
-			continue;
-		}
-		dest = ntohl(dest);
-		mask = ntohl(mask);
-		
-		uint32_t broadcast = dest | ~mask;
-
-		if ((broadcast == 0x00000000) || (broadcast == 0xFFFFFFFF)) {
-			continue;
-		}
-
-		if (hdhomerun_discover_send_packet(ds, broadcast, device_type, device_id) &lt; 0) {
-			continue;
-		}
-
-		send_count++;
-	}
-
-	fclose(fp);
-
-	if (send_count == 0) {
-		return -1;
-	}
-
-	return 0;
-}
-
-#elif defined(__APPLE__) || defined(__BSD__)
-
-static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, uint32_t device_type, uint32_t device_id)
+#if defined(USE_IFADDRS)
+static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
 {
 	struct ifaddrs *ifap;
 	if (getifaddrs(&amp;ifap) &lt; 0) {
-		return -1;
+		return;
 	}
 
 	struct ifaddrs *p = ifap;
-	unsigned int send_count = 0;
 	while (p) {
 		struct sockaddr_in *addr_in = (struct sockaddr_in *)p-&gt;ifa_addr;
 		struct sockaddr_in *mask_in = (struct sockaddr_in *)p-&gt;ifa_netmask;
@@ -223,93 +143,125 @@ static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds
 			continue;
 		}
 
-		uint32_t addr = ntohl(addr_in-&gt;sin_addr.s_addr);
+		uint32_t local_ip = ntohl(addr_in-&gt;sin_addr.s_addr);
 		uint32_t mask = ntohl(mask_in-&gt;sin_addr.s_addr);
-
-		uint32_t broadcast = addr | ~mask;
-
-		if ((broadcast == 0x00000000) || (broadcast == 0xFFFFFFFF)) {
+		if (local_ip == 0) {
 			p = p-&gt;ifa_next;
 			continue;
 		}
 
-		if (hdhomerun_discover_send_packet(ds, broadcast, device_type, device_id) &lt; 0) {
-			p = p-&gt;ifa_next;
-			continue;
-		}
-
-		send_count++;
+		hdhomerun_discover_sock_create(ds, local_ip, mask);
 		p = p-&gt;ifa_next;
 	}
 
 	freeifaddrs(ifap);
-
-	if (send_count == 0) {
-		return -1;
-	}
-
-	return 0;
 }
+#endif
 
-#else
 
-static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, uint32_t device_type, uint32_t device_id)
+#if !defined(USE_IPHLPAPI) &amp;&amp; !defined(USE_IFADDRS)
+static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
 {
-	return -1;
+	/* do nothing */
 }
-
 #endif
 
-static int hdhomerun_discover_send(struct hdhomerun_discover_sock_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
+static struct hdhomerun_discover_t *hdhomerun_discover_create(void)
 {
-	if (target_ip != 0) {
-		return hdhomerun_discover_send_packet(ds, target_ip, device_type, device_id);
+	struct hdhomerun_discover_t *ds = (struct hdhomerun_discover_t *)calloc(1, sizeof(struct hdhomerun_discover_t));
+	if (!ds) {
+		return NULL;
 	}
 
-	if (hdhomerun_discover_send_internal(ds, device_type, device_id) &lt; 0) {
-		return hdhomerun_discover_send_packet(ds, 0xFFFFFFFF, device_type, device_id);
+	/* Detect &amp; create sockets. */
+	hdhomerun_discover_sock_detect(ds);
+	if (ds-&gt;sock_count == 0) {
+		hdhomerun_discover_sock_create(ds, 0, 0);
+		if (ds-&gt;sock_count == 0) {
+			free(ds);
+			return NULL;
+		}
 	}
 
-	return 0;
+	/* Success. */
+	return ds;
 }
 
-static int hdhomerun_discover_recv(struct hdhomerun_discover_sock_t *ds, struct hdhomerun_discover_device_t *result)
+static void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds)
 {
-	struct timeval t;
-	t.tv_sec = 0;
-	t.tv_usec = 250000;
+	unsigned int i;
+	for (i = 0; i &lt; ds-&gt;sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &amp;ds-&gt;socks[i];
+		close(dss-&gt;sock);
+	}
 
-	fd_set readfds;
-	FD_ZERO(&amp;readfds);
-	FD_SET(ds-&gt;sock, &amp;readfds);
+	free(ds);
+}
 
-	if (select(ds-&gt;sock+1, &amp;readfds, NULL, NULL, &amp;t) &lt; 0) {
-		return -1;
+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)
+{
+	if (target_ip == 0) {
+		target_ip = dss-&gt;broadcast_ip;
 	}
-	if (!FD_ISSET(ds-&gt;sock, &amp;readfds)) {
-		return 0;
+
+	struct hdhomerun_pkt_t *tx_pkt = &amp;ds-&gt;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(&amp;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-&gt;end - tx_pkt-&gt;start);
+	if (sendto(dss-&gt;sock, (char *)tx_pkt-&gt;start, length, 0, (struct sockaddr *)&amp;sock_addr, sizeof(sock_addr)) != length) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static bool_t hdhomerun_discover_send(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
+{
+	bool_t result = FALSE;
+
+	unsigned int i;
+	for (i = 0; i &lt; ds-&gt;sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &amp;ds-&gt;socks[i];
+		result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
 	}
 
-	uint8_t buffer[1024];
+	return result;
+}
+
+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 = &amp;ds-&gt;rx_pkt;
+	hdhomerun_pkt_reset(rx_pkt);
+
 	struct sockaddr_in sock_addr;
+	memset(&amp;sock_addr, 0, sizeof(sock_addr));
 	socklen_t sockaddr_size = sizeof(sock_addr);
-	int rx_length = recvfrom(ds-&gt;sock, (char *)buffer, sizeof(buffer), 0, (struct sockaddr *)&amp;sock_addr, &amp;sockaddr_size);
+
+	int rx_length = recvfrom(dss-&gt;sock, (char *)rx_pkt-&gt;end, (int)(rx_pkt-&gt;limit - rx_pkt-&gt;end), 0, (struct sockaddr *)&amp;sock_addr, &amp;sockaddr_size);
 	if (rx_length &lt;= 0) {
 		/* Don't return error - windows machine on VPN can sometimes cause a sock error here but otherwise works. */
 		return 0;
 	}
-	if (rx_length &lt; HDHOMERUN_MIN_PEEK_LENGTH) {
-		return 0;
-	}
+	rx_pkt-&gt;end += rx_length;
 
-	size_t length = hdhomerun_peek_packet_length(buffer);
-	if (length &gt; (size_t)rx_length) {
+	uint16_t type;
+	if (hdhomerun_pkt_open_frame(rx_pkt, &amp;type) &lt;= 0) {
 		return 0;
 	}
-
-	uint8_t *ptr = buffer;
-	uint8_t *end = buffer + length;
-	int type = hdhomerun_process_packet(&amp;ptr, &amp;end);
 	if (type != HDHOMERUN_TYPE_DISCOVER_RPY) {
 		return 0;
 	}
@@ -317,11 +269,12 @@ static int hdhomerun_discover_recv(struct hdhomerun_discover_sock_t *ds, struct
 	result-&gt;ip_addr = ntohl(sock_addr.sin_addr.s_addr);
 	result-&gt;device_type = 0;
 	result-&gt;device_id = 0;
+
 	while (1) {
 		uint8_t tag;
 		size_t len;
-		uint8_t *value;
-		if (hdhomerun_read_tlv(&amp;ptr, end, &amp;tag, &amp;len, &amp;value) &lt; 0) {
+		uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &amp;tag, &amp;len);
+		if (!next) {
 			break;
 		}
 
@@ -330,28 +283,71 @@ static int hdhomerun_discover_recv(struct hdhomerun_discover_sock_t *ds, struct
 			if (len != 4) {
 				break;
 			}
-			result-&gt;device_type = hdhomerun_read_u32(&amp;value);
+			result-&gt;device_type = hdhomerun_pkt_read_u32(rx_pkt);
 			break;
+
 		case HDHOMERUN_TAG_DEVICE_ID:
 			if (len != 4) {
 				break;
 			}
-			result-&gt;device_id = hdhomerun_read_u32(&amp;value);
+			result-&gt;device_id = hdhomerun_pkt_read_u32(rx_pkt);
 			break;
+
 		default:
 			break;
 		}
+
+		rx_pkt-&gt;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(&amp;readfds);
+	int max_sock = -1;
+
+	unsigned int i;
+	for (i = 0; i &lt; ds-&gt;sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &amp;ds-&gt;socks[i];
+		FD_SET(dss-&gt;sock, &amp;readfds);
+		if (dss-&gt;sock &gt; max_sock) {
+			max_sock = dss-&gt;sock;
+		}
+	}
+
+	if (select(max_sock+1, &amp;readfds, NULL, NULL, &amp;t) &lt; 0) {
+		return -1;
+	}
+
+	for (i = 0; i &lt; ds-&gt;sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &amp;ds-&gt;socks[i];
+		if (!FD_ISSET(dss-&gt;sock, &amp;readfds)) {
+			continue;
+		}
+
+		if (hdhomerun_discover_recv_internal(ds, dss, result) &lt;= 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 &lt; count; index++) {
 		struct hdhomerun_discover_device_t *result = &amp;result_list[index];
-		if (result-&gt;device_id == device_id) {
+		if (result-&gt;ip_addr == ip_addr) {
 			return result;
 		}
 	}
@@ -359,16 +355,12 @@ static struct hdhomerun_discover_device_t *hdhomerun_discover_find_in_list(struc
 	return NULL;
 }
 
-static int hdhomerun_discover_find_devices_internal(struct hdhomerun_discover_sock_t *ds, uint32_t target_ip, 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)
 {
-	if (!hdhomerun_discover_validate_device_id(device_id)) {
-		return 0;
-	}
-
 	int count = 0;
 	int attempt;
 	for (attempt = 0; attempt &lt; 4; attempt++) {
-		if (hdhomerun_discover_send(ds, target_ip, device_type, device_id) &lt; 0) {
+		if (!hdhomerun_discover_send(ds, target_ip, device_type, device_id)) {
 			return -1;
 		}
 
@@ -397,7 +389,7 @@ static int hdhomerun_discover_find_devices_internal(struct hdhomerun_discover_so
 			}
 
 			/* Ensure not already in list. */
-			if (hdhomerun_discover_find_in_list(result_list, count, result-&gt;device_id)) {
+			if (hdhomerun_discover_find_in_list(result_list, count, result-&gt;ip_addr)) {
 				continue;
 			}
 
@@ -412,35 +404,9 @@ static int hdhomerun_discover_find_devices_internal(struct hdhomerun_discover_so
 	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, 0, 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)
-{
-	struct hdhomerun_discover_sock_t *ds = hdhomerun_discover_create();
-	if (!ds) {
-		return -1;
-	}
-
-	int ret = hdhomerun_discover_find_devices_internal(ds, 0, device_type, HDHOMERUN_DEVICE_ID_WILDCARD, result_list, max_count);
-
-	hdhomerun_discover_destroy(ds);
-	return ret;
-}
-
 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;
 	}</diff>
      <filename>libhdhomerun/hdhomerun_discover.c</filename>
    </modified>
    <modified>
      <diff>@@ -1,12 +1,12 @@
 /*
  * hdhomerun_discover.h
  *
- * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
+ * Copyright &#169; 2006-2007 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 #ifdef __cplusplus
 extern &quot;C&quot; {
@@ -28,36 +27,13 @@ struct hdhomerun_discover_device_t {
 };
 
 /*
- * 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.
  *
- * 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);
-
-/*
- * Find custom.
- *
- * 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.</diff>
      <filename>libhdhomerun/hdhomerun_discover.h</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,11 +14,10 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
-#if defined(WIN32)
+#if defined(_WIN32) || defined(_WIN64)
 #define __WINDOWS__
 #endif
 
@@ -27,6 +26,7 @@
 #include &lt;windows.h&gt;
 #include &lt;winsock2.h&gt;
 #include &lt;ws2tcpip.h&gt;
+#include &lt;wspiapi.h&gt;
 #include &lt;stdlib.h&gt;
 #include &lt;stdio.h&gt;
 #include &lt;stdarg.h&gt;
@@ -64,6 +64,10 @@
 #if defined(__WINDOWS__)
 
 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;</diff>
      <filename>libhdhomerun/hdhomerun_os.h</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2005-2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,257 +14,220 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 #include &quot;hdhomerun_os.h&quot;
 #include &quot;hdhomerun_pkt.h&quot;
 
-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-&gt;limit = pkt-&gt;buffer + sizeof(pkt-&gt;buffer);
+	pkt-&gt;start = pkt-&gt;buffer + 1024;
+	pkt-&gt;end = pkt-&gt;start;
+	pkt-&gt;pos = pkt-&gt;start;
+}
+
+static uint32_t hdhomerun_pkt_calc_crc(uint8_t *start, uint8_t *end)
+{
+	uint8_t *pos = start;
+	uint32_t crc = 0xFFFFFFFF;
+	while (pos &lt; end) {
+		uint8_t x = (uint8_t)(crc) ^ *pos++;
+		crc &gt;&gt;= 8;
+		if (x &amp; 0x01) crc ^= 0x77073096;
+		if (x &amp; 0x02) crc ^= 0xEE0E612C;
+		if (x &amp; 0x04) crc ^= 0x076DC419;
+		if (x &amp; 0x08) crc ^= 0x0EDB8832;
+		if (x &amp; 0x10) crc ^= 0x1DB71064;
+		if (x &amp; 0x20) crc ^= 0x3B6E20C8;
+		if (x &amp; 0x40) crc ^= 0x76DC4190;
+		if (x &amp; 0x80) crc ^= 0xEDB88320;
+	}
+	return crc ^ 0xFFFFFFFF;
+}
+
+uint8_t hdhomerun_pkt_read_u8(struct hdhomerun_pkt_t *pkt)
+{
+	uint8_t v = *pkt-&gt;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++ &lt;&lt; 8;
-	v |= (uint16_t)*ptr++ &lt;&lt; 0;
-	*pptr = ptr;
+	v =  (uint16_t)*pkt-&gt;pos++ &lt;&lt; 8;
+	v |= (uint16_t)*pkt-&gt;pos++ &lt;&lt; 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++ &lt;&lt; 24;
-	v |= (uint32_t)*ptr++ &lt;&lt; 16;
-	v |= (uint32_t)*ptr++ &lt;&lt; 8;
-	v |= (uint32_t)*ptr++ &lt;&lt; 0;
-	*pptr = ptr;
+	v =  (uint32_t)*pkt-&gt;pos++ &lt;&lt; 24;
+	v |= (uint32_t)*pkt-&gt;pos++ &lt;&lt; 16;
+	v |= (uint32_t)*pkt-&gt;pos++ &lt;&lt; 8;
+	v |= (uint32_t)*pkt-&gt;pos++ &lt;&lt; 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 &gt; end) {
-		return -1;
+	if (pkt-&gt;pos + 1 &gt; pkt-&gt;end) {
+		return (size_t)-1;
 	}
 
-	length = (size_t)*ptr++;
+	length = (size_t)*pkt-&gt;pos++;
 	if (length &amp; 0x0080) {
-		if (ptr + 1 &gt; end) {
-			return -1;
+		if (pkt-&gt;pos + 1 &gt; pkt-&gt;end) {
+			return (size_t)-1;
 		}
 
 		length &amp;= 0x007F;
-		length |= (size_t)*ptr++ &lt;&lt; 7;
+		length |= (size_t)*pkt-&gt;pos++ &lt;&lt; 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 &lt; 2) {
-		return -1;
+	if (pkt-&gt;pos + 2 &gt; pkt-&gt;end) {
+		return NULL;
 	}
 	
-	*ptag = hdhomerun_read_u8(pptr);
-	*plength = hdhomerun_read_var_length(pptr, end);
-	*pvalue = *pptr;
-	
-	if ((size_t)(end - *pptr) &lt; *plength) {
-		return -1;
+	*ptag = hdhomerun_pkt_read_u8(pkt);
+	*plength = hdhomerun_pkt_read_var_length(pkt);
+
+	if (pkt-&gt;pos + *plength &gt; pkt-&gt;end) {
+		return NULL;
 	}
 	
-	*pptr += *plength;
-	return 0;
+	return pkt-&gt;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-&gt;pos++ = v;
 
-void hdhomerun_write_u16(uint8_t **pptr, uint16_t v)
-{
-	uint8_t *ptr = *pptr;
-	*ptr++ = (uint8_t)(v &gt;&gt; 8);
-	*ptr++ = (uint8_t)(v &gt;&gt; 0);
-	*pptr = ptr;
+	if (pkt-&gt;pos &gt; pkt-&gt;end) {
+		pkt-&gt;end = pkt-&gt;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 &gt;&gt; 24);
-	*ptr++ = (uint8_t)(v &gt;&gt; 16);
-	*ptr++ = (uint8_t)(v &gt;&gt; 8);
-	*ptr++ = (uint8_t)(v &gt;&gt; 0);
-	*pptr = ptr;
-}
+	*pkt-&gt;pos++ = (uint8_t)(v &gt;&gt; 8);
+	*pkt-&gt;pos++ = (uint8_t)(v &gt;&gt; 0);
 
-void hdhomerun_write_var_length(uint8_t **pptr, size_t v)
-{
-	uint8_t *ptr = *pptr;
-	if (v &lt;= 127) {
-		*ptr++ = (uint8_t)v;
-	} else {
-		*ptr++ = (uint8_t)(v | 0x80);
-		*ptr++ = (uint8_t)(v &gt;&gt; 7);
+	if (pkt-&gt;pos &gt; pkt-&gt;end) {
+		pkt-&gt;end = pkt-&gt;pos;
 	}
-	*pptr = ptr;
 }
 
-void hdhomerun_write_mem(uint8_t **pptr, const 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-&gt;pos++ = (uint8_t)(v &gt;&gt; 24);
+	*pkt-&gt;pos++ = (uint8_t)(v &gt;&gt; 16);
+	*pkt-&gt;pos++ = (uint8_t)(v &gt;&gt; 8);
+	*pkt-&gt;pos++ = (uint8_t)(v &gt;&gt; 0);
 
-static uint32_t hdhomerun_calc_crc(uint8_t *start, uint8_t *end)
-{
-	uint8_t *ptr = start;
-	uint32_t crc = 0xFFFFFFFF;
-	while (ptr &lt; end) {
-		uint8_t x = (uint8_t)(crc) ^ *ptr++;
-		crc &gt;&gt;= 8;
-		if (x &amp; 0x01) crc ^= 0x77073096;
-		if (x &amp; 0x02) crc ^= 0xEE0E612C;
-		if (x &amp; 0x04) crc ^= 0x076DC419;
-		if (x &amp; 0x08) crc ^= 0x0EDB8832;
-		if (x &amp; 0x10) crc ^= 0x1DB71064;
-		if (x &amp; 0x20) crc ^= 0x3B6E20C8;
-		if (x &amp; 0x40) crc ^= 0x76DC4190;
-		if (x &amp; 0x80) crc ^= 0xEDB88320;
+	if (pkt-&gt;pos &gt; pkt-&gt;end) {
+		pkt-&gt;end = pkt-&gt;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 &lt; 8) {
-		return -1;
+	if (v &lt;= 127) {
+		*pkt-&gt;pos++ = (uint8_t)v;
+	} else {
+		*pkt-&gt;pos++ = (uint8_t)(v | 0x80);
+		*pkt-&gt;pos++ = (uint8_t)(v &gt;&gt; 7);
 	}
-	uint8_t *ptr = end - 4;
-	uint32_t actual_crc = hdhomerun_calc_crc(start, ptr);
-	uint32_t packet_crc;
-	packet_crc =  (uint32_t)*ptr++ &lt;&lt; 0;
-	packet_crc |= (uint32_t)*ptr++ &lt;&lt; 8;
-	packet_crc |= (uint32_t)*ptr++ &lt;&lt; 16;
-	packet_crc |= (uint32_t)*ptr++ &lt;&lt; 24;
-	if (actual_crc != packet_crc) {
-		return -1;
+
+	if (pkt-&gt;pos &gt; pkt-&gt;end) {
+		pkt-&gt;end = pkt-&gt;pos;
 	}
-	return 0;
 }
 
-void hdhomerun_write_header_length(uint8_t *buffer, uint8_t *end)
+void hdhomerun_pkt_write_mem(struct hdhomerun_pkt_t *pkt, const void *mem, size_t length)
 {
-	uint8_t *ptr = buffer + 2;
-	size_t length = end - buffer - 4;
-	hdhomerun_write_u16(&amp;ptr, (uint16_t)length);
-}
+	memcpy(pkt-&gt;pos, mem, length);
+	pkt-&gt;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 &gt;&gt; 0);
-	*ptr++ = (uint8_t)(crc &gt;&gt; 8);
-	*ptr++ = (uint8_t)(crc &gt;&gt; 16);
-	*ptr++ = (uint8_t)(crc &gt;&gt; 24);
-	*pptr = ptr;
+	if (pkt-&gt;pos &gt; pkt-&gt;end) {
+		pkt-&gt;end = pkt-&gt;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);
-
-	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);
-
-	hdhomerun_write_header_length(start, *pptr);
-	hdhomerun_write_crc(pptr, start);
-}
+	pkt-&gt;pos = pkt-&gt;start;
 
-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);
-
-	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);
-
-	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);
+	if (pkt-&gt;pos + 4 &gt; pkt-&gt;end) {
+		return 0;
 	}
 
-	hdhomerun_write_header_length(start, *pptr);
-	hdhomerun_write_crc(pptr, start);
-}
+	*ptype = hdhomerun_pkt_read_u16(pkt);
+	size_t length = hdhomerun_pkt_read_u16(pkt);
+	pkt-&gt;pos += length;
 
-void hdhomerun_write_upgrade_request(uint8_t **pptr, uint32_t sequence, void *data, size_t length)
-{
-	uint8_t *start = *pptr;
-	hdhomerun_write_u16(pptr, HDHOMERUN_TYPE_UPGRADE_REQ);
-	hdhomerun_write_u16(pptr, 0);
+	if (pkt-&gt;pos + 4 &gt; pkt-&gt;end) {
+		pkt-&gt;pos = pkt-&gt;start;
+		return 0;
+	}
+
+	uint32_t calc_crc = hdhomerun_pkt_calc_crc(pkt-&gt;start, pkt-&gt;pos);
 
-	hdhomerun_write_u32(pptr, sequence);
-	if (length &gt; 0) {
-		hdhomerun_write_mem(pptr, data, length);
+	uint32_t packet_crc;
+	packet_crc =  (uint32_t)*pkt-&gt;pos++ &lt;&lt; 0;
+	packet_crc |= (uint32_t)*pkt-&gt;pos++ &lt;&lt; 8;
+	packet_crc |= (uint32_t)*pkt-&gt;pos++ &lt;&lt; 16;
+	packet_crc |= (uint32_t)*pkt-&gt;pos++ &lt;&lt; 24;
+	if (calc_crc != packet_crc) {
+		return -1;
 	}
 
-	hdhomerun_write_header_length(start, *pptr);
-	hdhomerun_write_crc(pptr, start);
+	pkt-&gt;start += 4;
+	pkt-&gt;end = pkt-&gt;start + length;
+	pkt-&gt;pos = pkt-&gt;start;
+	return 1;
 }
 
-size_t hdhomerun_peek_packet_length(uint8_t *ptr)
+void hdhomerun_pkt_seal_frame(struct hdhomerun_pkt_t *pkt, uint16_t frame_type)
 {
-	ptr += 2;
-	return (size_t)hdhomerun_read_u16(&amp;ptr) + 8;
-}
+	size_t length = pkt-&gt;end - pkt-&gt;start;
 
-int hdhomerun_process_packet(uint8_t **pptr, uint8_t **pend)
-{
-	if (hdhomerun_check_crc(*pptr, *pend) &lt; 0) {
-		return -1;
-	}
-	*pend -= 4;
-	
-	uint16_t type = hdhomerun_read_u16(pptr);
-	uint16_t length = hdhomerun_read_u16(pptr);
-	if ((*pend - *pptr) &lt; length) {
-		return -1;
-	}
-	*pend = *pptr + length;
-	return (int)type;
-}
+	pkt-&gt;start -= 4;
+	pkt-&gt;pos = pkt-&gt;start;
+	hdhomerun_pkt_write_u16(pkt, frame_type);
+	hdhomerun_pkt_write_u16(pkt, (uint16_t)length);
 
+	uint32_t crc = hdhomerun_pkt_calc_crc(pkt-&gt;start, pkt-&gt;end);
+	*pkt-&gt;end++ = (uint8_t)(crc &gt;&gt; 0);
+	*pkt-&gt;end++ = (uint8_t)(crc &gt;&gt; 8);
+	*pkt-&gt;end++ = (uint8_t)(crc &gt;&gt; 16);
+	*pkt-&gt;end++ = (uint8_t)(crc &gt;&gt; 24);
+
+	pkt-&gt;pos = pkt-&gt;start;
+}</diff>
      <filename>libhdhomerun/hdhomerun_pkt.c</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2005-2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 #ifdef __cplusplus
 extern &quot;C&quot; {
@@ -129,27 +128,33 @@ extern &quot;C&quot; {
 
 #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_mem(uint8_t **pptr, const void *mem, size_t length);
-extern void hdhomerun_write_var_length(uint8_t **pptr, size_t v);
-extern void hdhomerun_write_header_length(uint8_t *buffer, uint8_t *end);
-extern void hdhomerun_write_crc(uint8_t **pptr, uint8_t *start);
+struct hdhomerun_pkt_t {
+	uint8_t *pos;
+	uint8_t *start;
+	uint8_t *end;
+	uint8_t *limit;
+	uint8_t buffer[3074];
+};
 
-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 struct hdhomerun_pkt_t *hdhomerun_pkt_create(void);
+extern void hdhomerun_pkt_destroy(struct hdhomerun_pkt_t *pkt);
+extern void hdhomerun_pkt_reset(struct hdhomerun_pkt_t *pkt);
 
-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);
+extern uint8_t hdhomerun_pkt_read_u8(struct hdhomerun_pkt_t *pkt);
+extern uint16_t hdhomerun_pkt_read_u16(struct hdhomerun_pkt_t *pkt);
+extern uint32_t hdhomerun_pkt_read_u32(struct hdhomerun_pkt_t *pkt);
+extern size_t hdhomerun_pkt_read_var_length(struct hdhomerun_pkt_t *pkt);
+extern uint8_t *hdhomerun_pkt_read_tlv(struct hdhomerun_pkt_t *pkt, uint8_t *ptag, size_t *plength);
+
+extern void hdhomerun_pkt_write_u8(struct hdhomerun_pkt_t *pkt, uint8_t v);
+extern void hdhomerun_pkt_write_u16(struct hdhomerun_pkt_t *pkt, uint16_t v);
+extern void hdhomerun_pkt_write_u32(struct hdhomerun_pkt_t *pkt, uint32_t v);
+extern void hdhomerun_pkt_write_var_length(struct hdhomerun_pkt_t *pkt, size_t v);
+extern void hdhomerun_pkt_write_mem(struct hdhomerun_pkt_t *pkt, const void *mem, size_t length);
+
+extern bool_t hdhomerun_pkt_open_frame(struct hdhomerun_pkt_t *pkt, uint16_t *ptype);
+extern void hdhomerun_pkt_seal_frame(struct hdhomerun_pkt_t *pkt, uint16_t frame_type);
 
 #ifdef __cplusplus
 }
 #endif
-</diff>
      <filename>libhdhomerun/hdhomerun_pkt.h</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 
 #include &quot;hdhomerun_os.h&quot;
@@ -32,10 +31,12 @@ struct hdhomerun_video_sock_t {
 	volatile bool_t terminate;
 	pthread_t thread;
 	int sock;
-	uint32_t packet_count;
-	uint32_t transport_error_count;
-	uint32_t sequence_error_count;
-	uint8_t sequence[0x2000];
+	struct hdhomerun_debug_t *dbg;
+	volatile uint32_t packet_count;
+	volatile uint32_t transport_error_count;
+	volatile uint32_t sequence_error_count;
+	volatile uint32_t overflow_error_count;
+	volatile uint8_t sequence[0x2000];
 };
 
 static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg);
@@ -85,7 +86,7 @@ struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, size
 	}
 
 	/* Reset sequence tracking. */
-	memset(vs-&gt;sequence, 0xFF, sizeof(vs-&gt;sequence));
+	hdhomerun_video_flush(vs);
 
 	/* Buffer size. */
 	vs-&gt;buffer_size = (buffer_size / VIDEO_DATA_PACKET_SIZE) * VIDEO_DATA_PACKET_SIZE;
@@ -149,21 +150,22 @@ void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs)
 	free(vs);
 }
 
+void hdhomerun_video_set_debug(struct hdhomerun_video_sock_t *vs, struct hdhomerun_debug_t *dbg)
+{
+	vs-&gt;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-&gt;sock, (struct sockaddr*)&amp;sock_addr, &amp;sockaddr_size) != 0) {
+		hdhomerun_debug_printf(vs-&gt;dbg, &quot;hdhomerun_video_get_local_port: getsockname failed (%d)\n&quot;, sock_getlasterror);
 		return 0;
 	}
 	return ntohs(sock_addr.sin_port);
 }
 
-int hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs)
-{
-	return vs-&gt;sock;
-}
-
 static void hdhomerun_video_stats_ts_pkt(struct hdhomerun_video_sock_t *vs, uint8_t *ptr)
 {
 	uint16_t packet_identifier = ((uint16_t)(ptr[1] &amp; 0x1F) &lt;&lt; 8) | (uint16_t)ptr[2];
@@ -179,16 +181,17 @@ static void hdhomerun_video_stats_ts_pkt(struct hdhomerun_video_sock_t *vs, uint
 	}
 
 	uint8_t continuity_counter = ptr[3] &amp; 0x0F;
+	uint8_t previous_sequence = vs-&gt;sequence[packet_identifier];
 
-	if (continuity_counter == ((vs-&gt;sequence[packet_identifier] + 1) &amp; 0x0F)) {
+	if (continuity_counter == ((previous_sequence + 1) &amp; 0x0F)) {
 		vs-&gt;sequence[packet_identifier] = continuity_counter;
 		return;
 	}
-	if (vs-&gt;sequence[packet_identifier] == 0xFF) {
+	if (previous_sequence == 0xFF) {
 		vs-&gt;sequence[packet_identifier] = continuity_counter;
 		return;
 	}
-	if (continuity_counter == vs-&gt;sequence[packet_identifier]) {
+	if (continuity_counter == previous_sequence) {
 		return;
 	}
 
@@ -237,6 +240,7 @@ static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg)
 
 		/* Check for buffer overflow. */
 		if (head == vs-&gt;tail) {
+			vs-&gt;overflow_error_count++;
 			continue;
 		}
 
@@ -295,13 +299,18 @@ void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs)
 	vs-&gt;tail = vs-&gt;head;
 	vs-&gt;advance = 0;
 
-	memset(vs-&gt;sequence, 0xFF, sizeof(vs-&gt;sequence));
+	int i;
+	for (i = 0; i &lt; 8192; i++) {
+		vs-&gt;sequence[i] = 0xFF;
+	}
+
 	vs-&gt;packet_count = 0;
 	vs-&gt;transport_error_count = 0;
 	vs-&gt;sequence_error_count = 0;
+	vs-&gt;overflow_error_count = 0;
 }
 
-void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_debug_t *dbg, char *prefix)
+void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs)
 {
-	hdhomerun_debug_printf(dbg, &quot;%s video sock: pkt=%ld te=%ld miss=%ld\n&quot;, prefix, vs-&gt;packet_count, vs-&gt;transport_error_count, vs-&gt;sequence_error_count);
+	hdhomerun_debug_printf(vs-&gt;dbg, &quot;video sock: pkt=%ld te=%ld miss=%ld drop=%ld\n&quot;, vs-&gt;packet_count, vs-&gt;transport_error_count, vs-&gt;sequence_error_count, vs-&gt;overflow_error_count);
 }</diff>
      <filename>libhdhomerun/hdhomerun_video.c</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,10 @@
  *
  * Copyright &#169; 2006 Silicondust Engineering Ltd. &lt;www.silicondust.com&gt;.
  *
- * 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,7 @@
  * 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 &lt;http://www.gnu.org/licenses/&gt;.
  */
 #ifdef __cplusplus
 extern &quot;C&quot; {
@@ -48,11 +47,6 @@ extern void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs);
 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);
-
-/*
  * Read data from buffer.
  *
  * size_t max_size: The maximum amount of data to be returned.
@@ -79,7 +73,8 @@ extern void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs);
 /*
  * Debug print internal stats.
  */
-extern void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_debug_t *dbg, char *prefix);
+extern void hdhomerun_video_set_debug(struct hdhomerun_video_sock_t *vs, struct hdhomerun_debug_t *dbg);
+extern void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs);
 
 #ifdef __cplusplus
 }</diff>
      <filename>libhdhomerun/hdhomerun_video.h</filename>
    </modified>
    <modified>
      <diff>@@ -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. &lt;http://fsf.org/&gt;
  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 &quot;Lesser&quot; 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
-&quot;work based on the library&quot; and a &quot;work that uses the library&quot;.  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 &quot;this License&quot;).
-Each licensee is addressed as &quot;you&quot;.
-
-  A &quot;library&quot; 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 &quot;Library&quot;, below, refers to any such software library or work
-which has been distributed under these terms.  A &quot;work based on the
-Library&quot; 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 &quot;modification&quot;.)
-
-  &quot;Source code&quot; 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 &quot;work that uses the Library&quot;.  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 &quot;work that uses the Library&quot; with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a &quot;work that uses the
-library&quot;.  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a &quot;work that uses the Library&quot; 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 &quot;work that uses the Library&quot; 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 &quot;work that
-    uses the Library&quot;, 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 &quot;work that uses the
-Library&quot; 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
-&quot;any later version&quot;, 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 &quot;AS IS&quot; 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
-&quot;copyright&quot; line and a pointer to where the full notice is found.
-
-    &lt;one line to give the library's name and a brief idea of what it does.&gt;
-    Copyright (C) &lt;year&gt;  &lt;name of author&gt;
-
-    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 &quot;copyright disclaimer&quot; 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.
-
-  &lt;signature of Ty Coon&gt;, 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, &quot;this License&quot; refers to version 3 of the GNU Lesser
+General Public License, and the &quot;GNU GPL&quot; refers to version 3 of the GNU
+General Public License.
 
+  &quot;The Library&quot; refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An &quot;Application&quot; 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 &quot;Combined Work&quot; 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 &quot;Linked
+Version&quot;.
+
+  The &quot;Minimal Corresponding Source&quot; 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 &quot;Corresponding Application Code&quot; 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 &quot;or any later version&quot;
+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>
      <filename>libhdhomerun/lgpl.txt</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>e340b9f372bc3cd64cfefe782141932ac3349e88</id>
    </parent>
  </parents>
  <author>
    <name>Andrew Kimpton</name>
    <email>awk@awkward.org</email>
  </author>
  <url>http://github.com/awk/iontv/commit/5cb3595a350a2748273118b62ddfde20360b6ce5</url>
  <id>5cb3595a350a2748273118b62ddfde20360b6ce5</id>
  <committed-date>2008-06-26T14:11:44-07:00</committed-date>
  <authored-date>2008-06-26T14:11:44-07:00</authored-date>
  <message>Update to libhdhomerun_20080430</message>
  <tree>b74f7751da394175d3ae2664c883af7a65f083c8</tree>
  <committer>
    <name>Andrew Kimpton</name>
    <email>awk@awkward.org</email>
  </committer>
</commit>
