On 08/26/2010 10:39 PM, Phil Dibowitz wrote:
> I realize I'm the one with commit access, but this is pretty sizable chunk of
> code, and I'd like to get another set of eyes on this.
> 
> Stephen, and whoever else is interested, I'd appreciate a review of this
> patch.
> 
> I'd like to keep discussion of the API to the other thread, and I can update
> this patch accordingly, but for everything else, just put it inline, here.

Ugh. UpdateConfig was sorta blindly modeled after much of the code in
remote.cpp, including inheriting much of it's repetitive code. The attached
patch cleans up my original 89x patch by abstracting out the constant
repetitions of if-write, if-read, if-got-expected-ack into a single function.

More abstraction could be done, but this handles the most egregious offender
and drops the length of that function by quite a bit.

-- 
Phil Dibowitz                             p...@ipom.com
Open Source software and tech docs        Insanity Palace of Metallica
http://www.phildev.net/                   http://www.ipom.com/

"Be who you are and say what you feel, because those who mind don't matter
 and those who matter don't mind."
 - Dr. Seuss

? concordance/.deps
? concordance/.libs
? concordance/Makefile
? concordance/Makefile.in
? concordance/aclocal.m4
? concordance/autom4te.cache
? concordance/concordance
? concordance/config.guess
? concordance/config.h
? concordance/config.h.in
? concordance/config.log
? concordance/config.status
? concordance/config.sub
? concordance/configure
? concordance/depcomp
? concordance/install-sh
? concordance/libtool
? concordance/ltmain.sh
? concordance/m4
? concordance/missing
? concordance/stamp-h1
? consnoop/consnoop
? libconcord/.deps
? libconcord/.libs
? libconcord/Makefile
? libconcord/Makefile.in
? libconcord/aclocal.m4
? libconcord/add
? libconcord/autom4te.cache
? libconcord/binaryfile.lo
? libconcord/config.h
? libconcord/config.h.in
? libconcord/config.log
? libconcord/config.status
? libconcord/configure
? libconcord/depcomp
? libconcord/install-sh
? libconcord/libconcord.fdi
? libconcord/libconcord.la
? libconcord/libconcord.lo
? libconcord/libconcord.rules
? libconcord/libtool
? libconcord/libusbhid.lo
? libconcord/ltmain.sh
? libconcord/m4
? libconcord/protocol_z.h.phil
? libconcord/remote.lo
? libconcord/remote_z.cpp.phil
? libconcord/remote_z.lo
? libconcord/stamp-h1
? libconcord/usblan.lo
? libconcord/web.lo
? libconcord/bindings/perl/Makefile
? libconcord/bindings/perl/blib
? libconcord/bindings/perl/concord.bs
? libconcord/bindings/perl/concord.pm
? libconcord/bindings/perl/concord_wrap.c
? libconcord/bindings/perl/pm_to_blib
? libconcord/bindings/python/build
Index: concordance/concordance.c
===================================================================
RCS file: /cvsroot/concordance/concordance/concordance/concordance.c,v
retrieving revision 1.41
retrieving revision 1.41.2.10
diff -u -r1.41 -r1.41.2.10
--- concordance/concordance.c	1 Aug 2010 15:28:53 -0000	1.41
+++ concordance/concordance.c	26 Aug 2010 20:30:56 -0000	1.41.2.10
@@ -112,7 +112,7 @@
 #define DEFAULT_FW_FILENAME_BIN "firmware.bin"
 #define DEFAULT_SAFE_FILENAME "safe.bin"
 
-const char * const VERSION = "0.22";
+const char * const VERSION = "0.22+CVS";
 
 struct options_t {
 	int binary;
@@ -443,11 +443,24 @@
 }
 
 
+int upload_config_zwave(uint8_t *data, uint32_t size, struct options_t *options,
+	lc_callback cb, void *cb_arg)
+{
+	int err;
+
+	printf("Writing Config:      ");
+	if ((err = write_config_to_remote(data, size, cb, (void *)1))) {
+		return err;
+	}
+	printf("       done\n");
+
+	return 0;
+}
 
 /*
  * Read the config from a file and write it to the remote
  */
-int upload_config(uint8_t *data, uint32_t size, struct options_t *options,
+int upload_config_hid(uint8_t *data, uint32_t size, struct options_t *options,
 	lc_callback cb, void *cb_arg)
 {
 	int err, i;
@@ -462,9 +475,6 @@
 		if ((err = find_config_binary(data, size,
 					       &binary_data, &binary_size)))
 			return LC_ERROR;
-
-		if (!(*options).noweb)
-			post_preconfig(data, size);
 	}
 
 	printf("Preparing Update:    ");
@@ -536,16 +546,61 @@
 	}
 	printf("       done\n");
 
+	return 0;
+}
+
+int upload_config(uint8_t *data, uint32_t data_size, uint8_t *xml,
+	uint32_t xml_size, struct options_t *options, lc_callback cb,
+	void *cb_arg)
+{
+	int err;
+
+	/*
+	 * Tell the website we're going to start. This, it seems creates a
+	 * session object on their side, because if you miss the pre-config
+	 * communication, you get a no-session error.
+	 */
+	if (!(*options).binary && !(*options).noweb) {
+		printf("Contacting website:  ");
+		if (is_z_remote()) {
+			if ((err = post_preconfig(xml, xml_size)))
+				return err;
+		} else {
+			if ((err = post_preconfig(data, data_size)))
+				return err;
+		}
+		printf("                     done\n");
+	}
+
+	/* Do the actual update */
+	if (is_z_remote()) {
+		if ((err = upload_config_zwave(data, data_size, options, cb,
+			cb_arg)))
+			return err;
+	} else {
+		if ((err = upload_config_hid(data, data_size, options, cb,
+			cb_arg)))
+			return err;
+	}
+
+	/* Set the time */
 	printf("Setting Time:        ");
 	if ((err = set_time())) {
 		return err;
 	}
 	printf("                     done\n");
 
+	/* Tell the website we're done */
 	if (!(*options).binary && !(*options).noweb) {
 		printf("Contacting website:  ");
-		if ((err = post_postconfig(data, size))) {
-			return err;
+		if (is_z_remote()) {
+			if ((err = post_postconfig(xml, xml_size))) {
+				return err;
+			}
+		} else {
+			if ((err = post_postconfig(data, data_size))) {
+				return err;
+			}
 		}
 		printf("                     done\n");
 	}
@@ -553,6 +608,7 @@
 	return 0;
 }
 
+
 int dump_safemode(char *file_name, lc_callback cb, void *cb_arg)
 {
 	uint8_t * safe = 0;
@@ -887,12 +943,12 @@
 
 }
 
-int detect_mode(uint8_t *data, uint32_t size, int *mode)
+int detect_mode(uint8_t *data, uint32_t size, int *mode, int xml_only)
 {
 	int err, type;
 
 	*mode = MODE_UNSET;
-	err = identify_file(data, size, &type);
+	err = identify_file(data, size, &type, xml_only);
 	if (err) {
 		return err;
 	}
@@ -1136,9 +1192,9 @@
 {
 	struct options_t options;
 	char *file_name;
-	int mode, file_mode, err;
-	uint8_t *data;
-	uint32_t size;
+	int mode, file_mode, err, is_zipfile;
+	uint8_t *data, *xml;
+	uint32_t data_size, xml_size;
 
 #ifdef WIN32
 	con=GetStdHandle(STD_OUTPUT_HANDLE);
@@ -1173,10 +1229,26 @@
 	}
 
 	/*
+	 * We used to delay remote initialization until after all the figuring
+	 * out the mode and read the file stuff... but with zwave support we
+	 * need to know what type of remote we're dealing with early on.
+	 */
+
+	err = init_concord();
+	if (err != 0) {
+		fprintf(stderr, "ERROR: Couldn't initializing libconcord: %s\n",
+			lc_strerror(err));
+		exit(1);
+	}
+
+	/*
 	 * OK, if we have a filename go ahead and read the file...
 	 */
-	data = 0;
-	size = 0;
+	data = NULL;
+	data_size = 0;
+	xml = NULL;
+	xml_size = 0;
+	is_zipfile = 0;
 
 	/*
  	 * Alright, at this point, if there's going to be a filename,
@@ -1184,18 +1256,35 @@
  	 */
 	if (file_name && (mode != MODE_DUMP_CONFIG && mode != MODE_DUMP_FIRMWARE
 			  && mode != MODE_DUMP_SAFEMODE)) {
-		if (read_file(file_name, &data, &size)) {
-			fprintf(stderr, "ERROR: Cannot read input file: %s\n",
-				file_name);
-			exit(1);
+		if (is_z_remote() && !options.binary) {
+			if (read_zip_file(file_name, &data, &data_size, &xml,
+				&xml_size)) {
+				is_zipfile = 0;
+			} else {
+				is_zipfile = 1;
+			}
 		}
+
+		if (!is_zipfile) {
+			if (read_file(file_name, &data, &data_size)) {
+				fprintf(stderr,
+					"ERROR: Cannot read input file: %s\n",
+					file_name);
+				exit(1);
+			}
+		}
+
 		/*
 		 * Now that the file is read, see if we can recognize it:
 		 */
-		if (detect_mode(data, size, &file_mode)) {
-			fprintf(stderr, "WARNING: Cannot determine mode of");
-			fprintf(stderr, " operation from file.\n");
+		if (detect_mode(is_zipfile ? xml : data,
+				is_zipfile ? xml_size : data_size,
+			&file_mode, 1)) {
+			fprintf(stderr, "WARNING: Cannot determine ");
+			fprintf(stderr, "mode of operation from file.");
+			fprintf(stderr, "\n");
 		}
+
 		/*
 		 * If we don't have a mode, lets detect that mode based on
 		 * the file.
@@ -1231,12 +1320,6 @@
 		populate_default_filename(mode, options.binary, &file_name);
 	}
 
-	err = init_concord();
-	if (err != 0) {
-		fprintf(stderr, "ERROR: Couldn't initializing libconcord: %s\n",
-			lc_strerror(err));
-		exit(1);
-	}
 
 	/*
 	 * If we're in reset mode, not only do we not need to read and print
@@ -1279,10 +1362,12 @@
 			break;
 
 		case MODE_CONNECTIVITY:
-			if (!options.noweb)
+			if (!options.noweb) {
 				printf("Contacting website:  ");
-				err = post_connect_test_success(data, size);
+				err = post_connect_test_success(data,
+					data_size);
 				printf("                     done\n");
+			}
 			break;
 
 		case MODE_DUMP_CONFIG:
@@ -1298,8 +1383,8 @@
 			break;
 
 		case MODE_WRITE_CONFIG:
-			err = upload_config(data, size, &options,
-				cb_print_percent_status, NULL);
+			err = upload_config(data, data_size, xml, xml_size,
+				&options, cb_print_percent_status, NULL);
 			if (err != 0) {
 				printf("Failed to upload config: %s\n",
 					lc_strerror(err));
@@ -1319,7 +1404,7 @@
 			break;
 
 		case MODE_WRITE_FIRMWARE:
-			err = upload_firmware(data, size, &options,
+			err = upload_firmware(data, data_size, &options,
 				cb_print_percent_status, NULL);
 			if (err != 0) {
 				printf("Failed to upload firmware: %s\n",
@@ -1340,7 +1425,7 @@
 			break;
 
 		case MODE_LEARN_IR:
-			err = learn_ir_commands(data, size, &options);
+			err = learn_ir_commands(data, data_size, &options);
 			break;
 
 		case MODE_GET_TIME:
Index: consnoop/consnoop.cpp
===================================================================
RCS file: /cvsroot/concordance/concordance/consnoop/consnoop.cpp,v
retrieving revision 1.18
retrieving revision 1.17.2.3
diff -u -r1.18 -r1.17.2.3
--- consnoop/consnoop.cpp	2 Aug 2010 22:04:26 -0000	1.18
+++ consnoop/consnoop.cpp	21 Aug 2010 15:58:50 -0000	1.17.2.3
@@ -349,11 +349,11 @@
 		case COMMAND_WRITE_UPDATE_HEADER:
 			if (!type) {
 				printf("Write Update Header:");
-				print_z_params(param_ptr, length);
-				printf("\n");
-				break;
+			} else {
+				printf("Write Update Header Response:");
 			}
-			printf("Write Update Header Response\n");
+			print_z_params(param_ptr, length);
+			printf("\n");
 			break;
 		case COMMAND_WRITE_UPDATE_DATA:
 			if (!type) {
@@ -386,11 +386,11 @@
 		case COMMAND_FINISH_UPDATE:
 			if (!type) {
 				printf("Write Finish Update:");
-				print_z_params(param_ptr, length);
-				printf("\n");
-				break;
+			} else {
+				printf("Write Finish Update Response:");
 			}
-			printf("Finish Update Response\n");
+			print_z_params(param_ptr, length);
+			printf("\n");
 			break;
 		case COMMAND_Z_RESET:
 			if (!type) {
Index: libconcord/Makefile.am
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/Makefile.am,v
retrieving revision 1.12
retrieving revision 1.11.2.1
diff -u -r1.12 -r1.11.2.1
--- libconcord/Makefile.am	14 Aug 2010 21:17:04 -0000	1.12
+++ libconcord/Makefile.am	23 Aug 2010 21:52:27 -0000	1.11.2.1
@@ -5,8 +5,7 @@
 	web.cpp libusb/libusbhid.cpp usblan.cpp binaryfile.h hid.h protocol_z.h \
 	 remote_info.h web.h protocol.h remote.h usblan.h xml_headers.h
 include_HEADERS = libconcord.h
-libconcord_la_LDFLAGS = -version-info 2:0:0 -lusb
-libconcord_la_LADD = usb
+libconcord_la_LDFLAGS = -version-info 2:0:1 -lusb -lzzip
 
 # udev and friends support
 udev:
Index: libconcord/libconcord.cpp
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/libconcord.cpp,v
retrieving revision 1.42
retrieving revision 1.42.2.8
diff -u -r1.42 -r1.42.2.8
--- libconcord/libconcord.cpp	27 Jul 2010 19:33:52 -0000	1.42
+++ libconcord/libconcord.cpp	26 Aug 2010 18:26:37 -0000	1.42.2.8
@@ -29,6 +29,9 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <errno.h>
+#include <zzip/lib.h>
+#include <list>
 #include "libconcord.h"
 #include "lc_internal.h"
 #include "remote.h"
@@ -38,8 +41,6 @@
 #include "web.h"
 #include "protocol.h"
 #include "time.h"
-#include <errno.h>
-#include <list>
 
 #define ZWAVE_HID_PID_MIN 0xC112
 #define ZWAVE_HID_PID_MAX 0xC115
@@ -196,6 +197,12 @@
 	return ri.max_config_size;
 }
 
+int is_z_remote()
+{
+	/* should this be in the remoteinfo struct? */
+	return rmt->IsZRemote() ? 1 : 0;
+}
+
 int get_time_second()
 {
 	return rtime.second;
@@ -330,24 +337,29 @@
 	delete[] ptr;
 }
 
-int identify_file(uint8_t *in, uint32_t size, int *type)
+int identify_file(uint8_t *in, uint32_t size, int *type, int xml_only)
 {
 	int err;
+	uint8_t *start_info_ptr, *end_info_ptr;
 
 	/*
 	 * Validate this is a remotely sane XML file
 	 */
-	uint8_t *start_info_ptr;
-	err = GetTag("INFORMATION", in, size, start_info_ptr);
-	if (err == -1) {
-		return LC_ERROR;
-	}
+	if (xml_only) {
+		start_info_ptr = in;
+		end_info_ptr = start_info_ptr + size;
+	} else {
+		err = GetTag("INFORMATION", in, size, start_info_ptr);
+		if (err == -1) {
+			return LC_ERROR;
+		}
 
-	uint8_t *end_info_ptr;
-	err = GetTag("/INFORMATION", in, size, end_info_ptr);
-	if (err == -1) {
-		return LC_ERROR;
+		err = GetTag("/INFORMATION", in, size, end_info_ptr);
+		if (err == -1) {
+			return LC_ERROR;
+		}
 	}
+	debug("start/end pointers populated");
 
 	/*
 	 * Determine size of binary data following /INFORMATION
@@ -357,7 +369,7 @@
 	 * Account for CRLF after /INFORMATION>
 	 * But, don't screw up if it's missing
 	 */
-	if (data_len >= 2) {
+	if (data_len >= 2 && !xml_only) {
 		data_len -= 2;
 	}
 
@@ -372,6 +384,7 @@
 		string tag_s;
 		err = GetTag("KEY", tmp_data, tmp_size, tag_ptr, &tag_s);
 		if (err == -1) {
+			debug("not a connectivity test file");
 			break;
 		}
 		if (!stricmp(tag_s.c_str(), "GETZAPSONLY")) {
@@ -393,6 +406,7 @@
 		string tag_s;
 		err = GetTag("TYPE", tmp_data, tmp_size, tag_ptr, &tag_s);
 		if (err == -1) {
+			debug("not a firmware file");
 			break;
 		}
 		if (!stricmp(tag_s.c_str(), "Firmware_Main")) {
@@ -418,8 +432,8 @@
 		*type = LC_FILE_TYPE_CONNECTIVITY;
 		return 0;
 	}
-	if (!found_get_zaps_only && (data_len >= 16) && !found_firmware &&
-		!found_learn_ir) {
+	if (!found_get_zaps_only && (data_len >= 16 || xml_only) &&
+		!found_firmware && !found_learn_ir) {
 		*type = LC_FILE_TYPE_CONFIGURATION;
 		return 0;
 	}
@@ -440,6 +454,36 @@
 	return LC_ERROR;
 }
 
+int read_zip_file(char *file_name, uint8_t **data, uint32_t *data_size,
+	uint8_t **xml, uint32_t *xml_size)
+{
+	/* TODO: error checking */
+	zzip_error_t zip_err;
+	ZZIP_DIR *dir = zzip_dir_open(file_name, &zip_err);
+	if (dir) {
+		ZZIP_DIRENT dirent;
+		while (zzip_dir_read(dir, &dirent)) {
+			ZZIP_FILE *fh = zzip_file_open(dir, dirent.d_name, 0);
+			if (strcmp(dirent.d_name, "Data.xml") == 0) {
+				*xml_size = dirent.st_size;
+				*xml = new uint8_t[*xml_size];
+				zzip_size_t len = zzip_file_read(fh, *xml,
+					*xml_size);
+			} else {
+				*data_size = dirent.st_size;
+				*data = new uint8_t[*data_size];
+				zzip_size_t len = zzip_file_read(fh, *data,
+					*data_size);
+			}
+			zzip_file_close(fh);
+		}
+	} else {
+		return LC_ERROR;
+	}
+	zzip_dir_close(dir);
+	return 0;
+}
+
 /*
  * Common routine to read contents of file named *file_name into
  * byte buffer **out. Get size from file and return out[size] 
@@ -719,7 +763,8 @@
 
 int post_postconfig(uint8_t *data, uint32_t size)
 {
-	return Post(data, size, "COMPLETEPOSTOPTIONS", ri, true);
+	return Post(data, size, "COMPLETEPOSTOPTIONS", ri, true,
+		false, is_z_remote() ? true: false, NULL, NULL);
 }
 
 int post_connect_test_success(uint8_t *data, uint32_t size)
@@ -812,9 +857,13 @@
 		cb_arg = (void *)true;
 	}
 
-	if ((err = rmt->WriteFlash(ri.arch->config_base, size, in,
-			ri.protocol, cb, cb_arg))) {
-		return LC_ERROR_WRITE;
+	if (is_z_remote()) {
+		if ((err = rmt->UpdateConfig(size, in, cb, cb_arg)))
+			return LC_ERROR_WRITE;
+	} else {
+		if ((err = rmt->WriteFlash(ri.arch->config_base, size, in,
+			ri.protocol, cb, cb_arg)))
+			return LC_ERROR_WRITE;
 	}
 
 	return 0;
@@ -1397,7 +1446,7 @@
 	learn_key = key_name;
 	learn_seq = encoded_signal;
 
-	return Post(data, size, "POSTOPTIONS", ri, true, false,
+	return Post(data, size, "POSTOPTIONS", ri, true, false, false,
 			&learn_seq, &learn_key);
 }
 
Index: libconcord/libconcord.h
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/libconcord.h,v
retrieving revision 1.22
retrieving revision 1.22.2.6
diff -u -r1.22 -r1.22.2.6
--- libconcord/libconcord.h	27 Jul 2010 19:33:52 -0000	1.22
+++ libconcord/libconcord.h	26 Aug 2010 18:26:37 -0000	1.22.2.6
@@ -118,6 +118,7 @@
 char *get_serial(int p);
 int get_config_bytes_used();
 int get_config_bytes_total();
+int is_z_remote();
 
 /*
  * TIME ACCESSORS
@@ -160,7 +161,7 @@
  * the return value. If the file can be identified, the type of the file
  * will be written to *mode.
  */
-int identify_file(uint8_t *in, uint32_t size, int *type);
+int identify_file(uint8_t *in, uint32_t size, int *type, int xml_only);
 
 /*
  * GENERAL REMOTE INTERACTIONS
@@ -260,6 +261,8 @@
  * allocate a char array and point your pointer at it. Use delete_blob to
  * reclaim this memory.
  */
+int read_zip_file(char *file_name, uint8_t **data, uint32_t *data_size,
+	uint8_t **xml, uint32_t *xml_size);
 int read_file(char *file_name, uint8_t **out, uint32_t *size);
 /*
  * Given a binary-only config blob *in, write the config to a file. Unless
Index: libconcord/protocol_z.h
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/protocol_z.h,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -u -r1.5 -r1.5.2.1
--- libconcord/protocol_z.h	20 Jul 2010 21:18:12 -0000	1.5
+++ libconcord/protocol_z.h	1 Aug 2010 17:50:51 -0000	1.5.2.1
@@ -23,6 +23,10 @@
 #ifndef PROTOCOL_Z_H
 #define PROTOCOL_Z_H
 
+#define TYPE_TCP_ACK 0x40
+#define TYPE_TCP_FIN 0x20
+#define TYPE_TCP_SYN 0x80
+
 // 1000 only
 #define SERVICE_FAMILY_CLIENT 2
 // 1000 only
Index: libconcord/remote.h
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/remote.h,v
retrieving revision 1.20
retrieving revision 1.20.2.3
diff -u -r1.20 -r1.20.2.3
--- libconcord/remote.h	27 Jul 2010 19:33:53 -0000	1.20
+++ libconcord/remote.h	28 Aug 2010 01:35:24 -0000	1.20.2.3
@@ -155,12 +155,15 @@
 	virtual int FinishFirmware(const TRemoteInfo &ri) = 0;
 	virtual int PrepConfig(const TRemoteInfo &ri)=0;
 	virtual int FinishConfig(const TRemoteInfo &ri)=0;
+	virtual int UpdateConfig(const uint32_t len,
+		const uint8_t *wr, lc_callback cb, void *arg)=0;
 
 	virtual int GetTime(const TRemoteInfo &ri, THarmonyTime &ht)=0;
 	virtual int SetTime(const TRemoteInfo &ri, const THarmonyTime &ht)=0;
 
 	virtual int LearnIR(uint32_t *freq, uint32_t **ir_signal,
 		uint32_t *ir_signal_length, lc_callback cb, void *cb_arg)=0;
+	virtual int IsZRemote()=0;
 };
 
 class CRemote : public CRemoteBase	// All non-Z-Wave remotes
@@ -197,12 +200,15 @@
 	int FinishFirmware(const TRemoteInfo &ri);
 	int PrepConfig(const TRemoteInfo &ri);
 	int FinishConfig(const TRemoteInfo &ri);
+	virtual int UpdateConfig(const uint32_t len,
+		const uint8_t *wr, lc_callback cb, void *arg) {};
 
 	int GetTime(const TRemoteInfo &ri, THarmonyTime &ht);
 	int SetTime(const TRemoteInfo &ri, const THarmonyTime &ht);
 
 	int LearnIR(uint32_t *freq, uint32_t **ir_signal, 
 		uint32_t *ir_signal_length, lc_callback cb, void *cb_arg);
+	int IsZRemote() {return false;}
 };
 
 // Base class for all Z-Wave remotes
@@ -248,6 +254,7 @@
 
 	int LearnIR(uint32_t *freq, uint32_t **ir_signal,
 		uint32_t *ir_signal_length, lc_callback cb, void *cb_arg);
+	int IsZRemote() {return true;}
 };
 
 // 890, 890Pro, AVL-300, RF Extender
@@ -264,6 +271,8 @@
 	int TCP_Read(uint8_t &status, uint32_t &len, uint8_t *data);
 
 protected:
+	int TCPSendAndCheck(uint8_t cmd, uint32_t len=0, uint8_t *data=NULL,
+		bool ackonly=false);
 	virtual int Write(uint8_t typ, uint8_t cmd, uint32_t len=0,
 		uint8_t *data=NULL);
 	virtual int Read(uint8_t &status, uint32_t &len, uint8_t *data);
@@ -274,6 +283,8 @@
 public:
 	CRemoteZ_HID() {};
 	virtual ~CRemoteZ_HID() {};
+	int UpdateConfig(const uint32_t len,
+		const uint8_t *wr, lc_callback cb, void *arg);
 };
 
 // 1000, 1000i
Index: libconcord/remote_z.cpp
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/remote_z.cpp,v
retrieving revision 1.25
retrieving revision 1.25.2.8
diff -u -r1.25 -r1.25.2.8
--- libconcord/remote_z.cpp	27 Jul 2010 19:33:53 -0000	1.25
+++ libconcord/remote_z.cpp	28 Aug 2010 01:35:24 -0000	1.25.2.8
@@ -22,6 +22,8 @@
  */
 
 #include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
 #include "libconcord.h"
 #include "lc_internal.h"
 #include "hid.h"
@@ -29,6 +31,48 @@
 #include "usblan.h"
 #include "protocol_z.h"
 
+/* Have we acked the syn packet yet? */
+static bool SYN_ACKED = false;
+static unsigned int last_seq;
+static unsigned int last_ack;
+static unsigned int last_payload_bytes;
+
+int TCP_Ack(bool increment_ack=false, bool fin=false) {
+	uint8_t pkt[68];
+
+	/*
+	 * Note: It's the caller's responsibility to ensure we've already
+	 * seen the SYN packet.
+	 */
+
+	uint8_t seq;
+	uint8_t ack;
+	uint8_t flags;
+
+	seq = last_ack;
+	ack = last_seq + last_payload_bytes;
+	if (increment_ack)
+		ack++;
+	flags = TYPE_TCP_ACK;
+	if (fin)
+		flags |= TYPE_TCP_FIN;
+	pkt[0] = 3;
+	pkt[1] = flags;
+	pkt[2] = seq;
+	pkt[3] = ack;
+
+	debug("Writing packet:");
+#ifdef _DEBUG
+	for (int i = 0; i <= pkt[0]; i++) {
+		fprintf(stderr, "%02X ", pkt[i]);
+	}
+	fprintf(stderr, "\n");
+#endif
+
+	return HID_WriteReport(pkt);
+}
+
+
 /*
  * The HID-based zwave remotes have two modes: so called "UDP" and "TCP". Do
  * not confuse these with the network protocols of similar names.
@@ -51,8 +95,13 @@
 	if (data && len)
 		memcpy(pkt + 4, data, len);
 
-	debug("Writing packet. First 5 bytes: %02X %02X %02X %02X %02X\n",
-		pkt[0], pkt[1], pkt[2], pkt[3], pkt[4]);
+	debug("Writing packet:");
+#ifdef _DEBUG
+	for (int i = 0; i <= pkt[0]; i++) {
+		fprintf(stderr, "%02X ", pkt[i]);
+	}
+	fprintf(stderr, "\n");
+#endif
 
 	return HID_WriteReport(pkt);
 }
@@ -64,8 +113,15 @@
 	if ((err = HID_ReadReport(pkt))) {
 		return LC_ERROR_READ;
 	}
-	debug("Reading packet. First 5 bytes: %02X %02X %02X %02X %02X\n",
-		pkt[0], pkt[1], pkt[2], pkt[3], pkt[4]);
+
+	debug("Reading packet:");
+#ifdef _DEBUG
+	for (int i = 0; i <= pkt[0]; i++) {
+		fprintf(stderr, "%02X ", pkt[i]);
+	}
+	fprintf(stderr, "\n");
+#endif
+
 	if (pkt[0] < 4) {
 		return LC_ERROR;
 	}
@@ -82,10 +138,83 @@
 int CRemoteZ_HID::TCP_Write(uint8_t typ, uint8_t cmd, uint32_t len,
 	uint8_t *data)
 {
-	return 0;
+	uint8_t pkt[68];
+
+	/*
+	 * Note: It's the caller's responsibility to ensure we've already
+	 * seen the SYN packet.
+	 */
+
+	uint8_t seq;
+	uint8_t ack;
+	uint8_t flags;
+
+	if (!SYN_ACKED) {
+		seq = 0x28;
+		ack = last_seq + 1;
+		flags = TYPE_TCP_ACK | TYPE_TCP_SYN;
+		SYN_ACKED = true;
+	} else {
+		seq = last_ack;
+		ack = last_seq + last_payload_bytes;
+		flags = TYPE_TCP_ACK;
+	}
+
+	if (len > 60)
+		return LC_ERROR;
+	pkt[0] = 5+len;
+	pkt[1] = flags;
+	pkt[2] = seq;
+	pkt[3] = ack;
+	pkt[4] = typ;
+	pkt[5] = cmd;
+	if (data && len)
+		memcpy(pkt + 6, data, len);
+
+	
+	debug("Writing packet:");
+#ifdef _DEBUG
+	for (int i = 0; i <= pkt[0]; i++) {
+		fprintf(stderr, "%02X ", pkt[i]);
+	}
+	fprintf(stderr, "\n");
+#endif
+		
+	return HID_WriteReport(pkt);
 }
+
+
 int CRemoteZ_HID::TCP_Read(uint8_t &status, uint32_t &len, uint8_t *data)
 {
+	uint8_t pkt[68];
+	int err;
+	/*
+	 * Many TCP operations can take a while, like computing checksums,
+	 * and it will be a while before we get a response. So we set the
+	 * timeout to 30 seconds.
+	 */
+	if ((err = HID_ReadReport(pkt, 30000))) {
+		return LC_ERROR_READ;
+	}
+
+	debug("Reading packet:");
+#ifdef _DEBUG
+	for (int i = 0; i <= pkt[0]; i++) {
+		fprintf(stderr, "%02X ", pkt[i]);
+	}
+	fprintf(stderr, "\n");
+#endif
+
+	if (pkt[0] < 3) {
+		return LC_ERROR;
+	}
+	len = pkt[0] - 4;
+	last_seq = pkt[2];
+	last_ack = pkt[3];
+	last_payload_bytes = len + 1; // tcp payload size
+	//if(!len) return 0;
+	//memcpy(data, pkt + 6, len);
+	memcpy(data, pkt + 1, len+3);
 	return 0;
 }
 
@@ -270,6 +399,10 @@
 		return LC_ERROR_READ;
 	}
 
+	if (cb) {
+		cb(0, 1, 2, cb_arg);
+	}
+
 	CRemoteZ_Base::TParamList pl;
 	ParseParams(len, rsp, pl);
 
@@ -312,12 +445,17 @@
 		return LC_ERROR_READ;
 	}
 
+	if (cb) {
+		cb(1, 2, 2, cb_arg);
+	}
+
 	ParseParams(len, rsp, pl);
 
 	make_serial(pl.p[0], ri);
 
 	ri.config_bytes_used = 0;
 	ri.max_config_size = 1;
+	ri.valid_config = 1;
 
 #if 0	// Get region info - 1000 only!
 	uint8_t rr[] = { 1, 1, 1 }; // AddByteParam(1);
@@ -491,6 +629,246 @@
 	return 0;
 }
 
+int CRemoteZ_HID::TCPSendAndCheck(uint8_t cmd, uint32_t len, uint8_t *data,
+	bool ackonly)
+{
+	int err = 0;
+	uint8_t status;
+	unsigned int rlen;
+	uint8_t rsp[60];
+
+	if ((err = TCP_Write(TYPE_REQUEST, cmd, len, data))) {
+		debug("Failed to send request %02X", cmd);
+		return LC_ERROR_WRITE;
+	}
+
+	if ((err = TCP_Read(status, rlen, rsp))) {
+		debug("Failed to read from remote");
+		return LC_ERROR_READ;
+	}
+
+	/* make sure it was the response we expected */
+	if (rsp[0] != TYPE_TCP_ACK || rsp[3] != TYPE_RESPONSE ||
+		(ackonly == false && rsp[4] != cmd)) {
+		debug("Did not get the expected response packet");
+		return LC_ERROR;
+	}
+
+	return 0;
+}
+
+int CRemoteZ_HID::UpdateConfig(const uint32_t len, const uint8_t *wr,
+	lc_callback cb, void *arg)
+{
+	int err = 0;
+	int cb_count = 0;
+
+	/* Start a TCP transfer */
+	if ((err = Write(TYPE_REQUEST, COMMAND_INITIATE_UPDATE_TCP_CHANNEL))) {
+		debug("Failed to write to remote");
+		return LC_ERROR_WRITE;
+	}
+
+	uint8_t rsp[60];
+	unsigned int rlen;
+	uint8_t status;
+
+	/* Make sure the remote is ready to start the TCP transfer */
+	if ((err = Read(status, rlen, rsp))) {
+		debug("Failed to read from remote");
+		return LC_ERROR_READ;
+	}
+
+	if (rsp[1] != TYPE_RESPONSE || rsp[2] !=
+		COMMAND_INITIATE_UPDATE_TCP_CHANNEL) {
+		return LC_ERROR;
+	}
+
+	/* Look for a SYN packet */
+	debug("Looking for syn");
+	if ((err = TCP_Read(status, rlen, rsp))) {
+		debug("Failed to read syn from remote");
+		return LC_ERROR_READ;
+	}
+
+	if (rsp[0] != TYPE_TCP_SYN) {
+		debug("Not a SYN packet!");
+		return LC_ERROR;
+	}
+
+	/* ACK it with a command to start an update */
+	debug("START_UPDATE");
+	uint8_t cmd[60] = { 0x00, 0x04 };
+	if ((err = TCP_Write(TYPE_REQUEST, COMMAND_START_UPDATE, 2, cmd))) {
+		debug("Failed to write start-update to remote");
+		return LC_ERROR_WRITE;
+	}
+
+	if ((err = TCP_Read(status, rlen, rsp))) {
+		debug("Failed to read from remote");
+		return LC_ERROR_READ;
+	}
+
+	/* make sure ot says 'start update response' */
+	if (rsp[0] != TYPE_TCP_ACK || rsp[3] != TYPE_RESPONSE ||
+		rsp[4] != COMMAND_START_UPDATE) {
+		debug("Not expected ack");
+		return LC_ERROR;
+	}
+
+	/* write update-header */
+	debug("UPDATE_HEADER");
+	uint32_t nlen = len;
+	unsigned char *size_ptr = (unsigned char *)&nlen;
+	for (int i = 0; i < 4; i++) {
+		cmd[i] = size_ptr[i];
+	}
+	cmd[4] = 0x04;
+	if ((err = TCPSendAndCheck(COMMAND_WRITE_UPDATE_HEADER, 5, cmd))) {
+		return err;
+	}
+
+	/* write data - TCP_Write should split this up for us */
+	debug("UPDATE_DATA");
+	int pkt_len;
+	int tlen = len;
+	int count = 0;
+	int bytes_written = 0;
+	uint8_t *wr_ptr = const_cast<uint8_t*>(wr);
+	while (tlen) {
+		pkt_len = 58;
+		if (tlen < pkt_len) {
+			pkt_len = tlen;
+		}
+		tlen -= pkt_len;
+
+		debug("DATA %d, sending %d bytes, %d bytes left", count,
+			pkt_len, tlen);
+
+		if ((err = TCPSendAndCheck(COMMAND_WRITE_UPDATE_DATA, pkt_len,
+			wr_ptr, true))) {
+			return err;
+		}
+		wr_ptr += pkt_len;
+
+		if (cb) {
+			cb(count++, (int)(wr_ptr - wr), len, arg);
+		}
+	}
+
+	/* write update-done */
+	debug("UPDATE_DATA_DONE");
+	if ((err = TCPSendAndCheck(COMMAND_WRITE_UPDATE_DATA_DONE))) {
+		return err;
+	}
+
+	/* Funky ACK exchange - part 1 */
+	debug("FUNKY-ACK");
+	if ((err = TCP_Ack(false, false))) {
+		debug("Failed to send funky-ack");
+		return LC_ERROR_WRITE;
+	}
+
+	if ((err = TCP_Read(status, rlen, rsp))) {
+		debug("Failed to read from remote");
+		return LC_ERROR_READ;
+	}
+
+	/* Funky ACK exchange - part 2 */
+	debug("FUNKY-ACK");
+	if ((err = TCP_Ack(false, false))) {
+		debug("Failed to send funky-ack");
+		return LC_ERROR_WRITE;
+	}
+
+	if ((err = TCP_Read(status, rlen, rsp))) {
+		debug("Failed to read from remote");
+		return LC_ERROR_READ;
+	}
+
+	/* send get-cheksum */
+	debug("GET_CHECKSUM");
+	cmd[0] = 0xFF;
+	cmd[1] = 0xFF;
+	cmd[2] = 0x04;
+	if ((err = TCPSendAndCheck(COMMAND_GET_UPDATE_CHECKSUM, 3, cmd))) {
+		return err;
+	}
+
+	/* send finish-update */
+	debug("FINISH_UPDATE");
+	cmd[0] = 0x01;
+	cmd[1] = 0x04;
+	/*
+	 * OK, this is a bit weird. We will eventually get a response to the
+	 * FINISH_UPDATE, but first we get an "empty" ack, do another funky-ack
+	 * exchange, and then we'll get the real response to this FINISH_UPDATE.
+	 */
+	if ((err = TCPSendAndCheck(COMMAND_FINISH_UPDATE, 2, cmd, true))) {
+		return err;
+	}
+
+	/* Funky ACK exchange */
+	debug("FUNKY-ACK");
+	if ((err = TCP_Ack(false, false))) {
+		debug("Failed to send funky-ack");
+		return LC_ERROR_WRITE;
+	}
+
+	/* Read their empty ack */
+	if ((err = TCP_Read(status, rlen, rsp))) {
+		debug("Failed to read from remote");
+		return LC_ERROR_READ;
+	}
+	if (rsp[0] != TYPE_TCP_ACK) {
+		debug("Failed to read ack");
+		return LC_ERROR;
+	}
+
+	/* And then we should have the response we want. */
+	if ((err = TCP_Read(status, rlen, rsp))) {
+		debug("Failed to read from remote");
+		return LC_ERROR_READ;
+	}
+
+	/* make sure we got an ack */
+	if (rsp[0] != TYPE_TCP_ACK || rsp[3] != TYPE_RESPONSE ||
+		rsp[4] != COMMAND_FINISH_UPDATE) {
+		debug("Failed to read finish-update ack");
+		return LC_ERROR;
+	}
+
+	/* FIN-ACK */
+	debug("FIN-ACK");
+	if ((err = TCP_Ack(false, true))) {
+		debug("Failed to send fin-ack");
+		return LC_ERROR_WRITE;
+	}
+
+	if ((err = TCP_Read(status, rlen, rsp))) {
+		debug("Failed to read from remote");
+		return LC_ERROR_READ;
+	}
+
+	/* Make sure we got an ack */
+	if (rsp[0] != (TYPE_TCP_ACK | TYPE_TCP_FIN)) {
+		debug("Failed to read finish-update ack");
+		return LC_ERROR;
+	}
+
+	if ((err = TCP_Ack(true, false))) {
+		debug("Failed to ack the ack of our fin-ack");
+		return LC_ERROR_WRITE;
+	}
+
+	/*
+	 * Official traces seem to show a final ack to the above ack, but for us
+	 * it never comes... so we don't bother trying to read it.
+	 */
+
+	return 0;
+}
+
 int CRemoteZ_Base::LearnIR(uint32_t *freq, uint32_t **ir_signal,
 	uint32_t *ir_signal_length, lc_callback cb, void *cb_arg)
 {
Index: libconcord/web.cpp
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/web.cpp,v
retrieving revision 1.27
retrieving revision 1.27.2.1
diff -u -r1.27 -r1.27.2.1
--- libconcord/web.cpp	12 Oct 2008 22:35:26 -0000	1.27
+++ libconcord/web.cpp	25 Aug 2010 22:27:22 -0000	1.27.2.1
@@ -69,7 +69,13 @@
 	const size_t len = strlen(in);
 	for(size_t i = 0; i < len; ++i) {
 		const char c = in[i];
-		if(urlencodemap[c>>3] & (1 << (c & 7))) {
+		if (c == ' ') {
+			out += '+';
+		} else if (c == '(') {
+			out += "%28";
+		} else if (c == ')') {
+			out += "%29";
+		} else if(urlencodemap[c>>3] & (1 << (c & 7))) {
 			char hex[4];
 			sprintf(hex, "%%%02X", c);
 			out += hex;
@@ -252,7 +258,7 @@
 
 
 int Post(uint8_t *xml, uint32_t xml_size, const char *root, TRemoteInfo &ri,
-	bool has_userid, bool add_cookiekeyval = false,
+	bool has_userid, bool add_cookiekeyval = false, bool z_post=false,
 	string *learn_seq = NULL, string *learn_key = NULL)
 {
 
@@ -297,11 +303,18 @@
 		char serial[144];
 		sprintf(serial, "%s%s%s", ri.serial1, ri.serial2, ri.serial3);
 		char post_data[2000];
-		sprintf(post_data, post_xml,
-			ri.fw_ver_major, ri.fw_ver_minor, ri.fw_type,
-			serial, ri.hw_ver_major, ri.hw_ver_minor,
-			ri.flash_mfg, ri.flash_id, ri.protocol,
-			ri.architecture, ri.skin);
+		if (z_post) {
+			sprintf(post_data, z_post_xml,
+				ri.hw_ver_major, ri.hw_ver_minor,
+				ri.flash_mfg, ri.flash_id,
+				ri.fw_ver_major, ri.fw_ver_minor);
+		} else {
+			sprintf(post_data, post_xml,
+				ri.fw_ver_major, ri.fw_ver_minor, ri.fw_type,
+				serial, ri.hw_ver_major, ri.hw_ver_minor,
+				ri.flash_mfg, ri.flash_id, ri.protocol,
+				ri.architecture, ri.skin);
+		}
 
 		debug("post data: %s",post_data);
 
Index: libconcord/web.h
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/web.h,v
retrieving revision 1.11
retrieving revision 1.11.2.1
diff -u -r1.11 -r1.11.2.1
--- libconcord/web.h	12 Oct 2008 22:35:26 -0000	1.11
+++ libconcord/web.h	25 Aug 2010 22:27:22 -0000	1.11.2.1
@@ -31,7 +31,7 @@
 	string *learn_seq);
 
 int Post(uint8_t *xml, uint32_t xml_size, const char *root, TRemoteInfo &ri,
-	bool has_userid, bool add_cookiekeyval = false,
+	bool has_userid, bool add_cookiekeyval = false, bool z_post = false,
 	string *learn_seq=NULL, string *learn_key=NULL);
 
 #endif
Index: libconcord/xml_headers.h
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/xml_headers.h,v
retrieving revision 1.7
retrieving revision 1.7.2.1
diff -u -r1.7 -r1.7.2.1
--- libconcord/xml_headers.h	11 Apr 2008 05:05:26 -0000	1.7
+++ libconcord/xml_headers.h	25 Aug 2010 22:27:22 -0000	1.7.2.1
@@ -131,6 +131,9 @@
 </INFORMATION>\r\n";
 
 
+
+//User-Agent: HarmonyBrowser/7.7.0 (Build 0; UpdatedFrom 7.3.0.15; Skin logitech; Windows Vista 6.1; x86; en; rv: 1.8.0.2) Gecko/20060125\r\n\
+
 const char *post_header="\
 POST /%s HTTP/1.1\r\n\
 User-Agent: HarmonyBrowser/7.3.0 (Build 15; UpdatedFrom 7.3.0.15; Skin logitech; Windows XP 5.1; x86; en; rv: 1.8.0.2) Gecko/20060125\r\n\
@@ -160,4 +163,41 @@
 </VERSIONINFORMATION>\
 </EASYZAPPERDATA>\r\n";
 
+const char *z_post_xml="\
+<DATA>\
+<STATUS>Success</STATUS>\
+<INFORMATION>Initializing services</INFORMATION>\
+<INFORMATION>Getting remote control information</INFORMATION>\
+<INFORMATION>Logitech Harmony Remote Software version: 7.7.0</INFORMATION>\
+<INFORMATION>Hardware version: Board %i.%i.0 (0x%02X:0x%02X)</INFORMATION>\
+<INFORMATION>Firmware version: %i.%i</INFORMATION>\
+<INFORMATION>Getting remote control information - Successful</INFORMATION>\
+<INFORMATION>Checking version information</INFORMATION>\
+<INFORMATION>Checking version information - Successful</INFORMATION>\
+<INFORMATION>Waking the remote control</INFORMATION>\
+<INFORMATION>Waking the remote control - Successful</INFORMATION>\
+<INFORMATION>Getting remote control states</INFORMATION>\
+<INFORMATION>Getting remote control states - Successful</INFORMATION>\
+<INFORMATION>Uploading remote control information to web</INFORMATION>\
+<INFORMATION>Initializing the remote control.  Please wait...</INFORMATION>\
+<INFORMATION>Uploading remote control information to web - Successful</INFORMATION>\
+<INFORMATION>Uploading...</INFORMATION>\
+<INFORMATION>Starting communication</INFORMATION>\
+<INFORMATION>Starting communication - Successful</INFORMATION>\
+<INFORMATION>Estimating time to update remote control</INFORMATION>\
+<INFORMATION>Estimated time to update remote control : 2 minutes</INFORMATION>\
+<INFORMATION>Estimating time to update remote control - Successful</INFORMATION>\
+<INFORMATION>Updating region</INFORMATION>\
+<INFORMATION>Verifying the data written to the remote control</INFORMATION>\
+<INFORMATION>Verifying the data written to the remote control - Successful</INFORMATION>\
+<INFORMATION>Updating region - Successful</INFORMATION>\
+<INFORMATION>Verifying the data written to the remote control</INFORMATION>\
+<INFORMATION>Verifying the data written to the remote control - Successful</INFORMATION>\
+<INFORMATION>Terminating communication</INFORMATION>\
+<INFORMATION>Terminating communication - Successful</INFORMATION>\
+<INFORMATION>Updated state variables.</INFORMATION>\
+<INFORMATION>Updated state variables. - Successful</INFORMATION>\
+<INFORMATION>Updating remote control time</INFORMATION>\
+<INFORMATION>Updating remote control time - Successful</INFORMATION></DATA>";
+
 #endif

Attachment: signature.asc
Description: OpenPGP digital signature

------------------------------------------------------------------------------
Sell apps to millions through the Intel(R) Atom(Tm) Developer Program
Be part of this innovative community and reach millions of netbook users 
worldwide. Take advantage of special opportunities to increase revenue and 
speed time-to-market. Join now, and jumpstart your future.
http://p.sf.net/sfu/intel-atom-d2d
_______________________________________________
concordance-devel mailing list
concordance-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/concordance-devel

Reply via email to