Attached is a patch which implements config dumping for MH remotes,
rebased on git.commit 57726be8c00398da63122fa9b355a5cde966fa18
Author: Scott Talbert <s...@techie.net>
Date: Sun Jun 9 17:40:05 2013 -0400
Implement config dumping for MH remotes.
Signed-off-by: Scott Talbert <s...@techie.net>
diff --git a/libconcord/configure.ac b/libconcord/configure.ac
index 47fc747..5a85e08 100644
--- a/libconcord/configure.ac
+++ b/libconcord/configure.ac
@@ -17,6 +17,12 @@ if test $a == 0
then
AC_MSG_ERROR([Error, libzzip is missing!])
fi
+AC_CHECK_HEADER(zip.h, [], [a=0])
+AC_CHECK_LIB(zip, zip_open, [], [a=0])
+if test $a == 0
+then
+ AC_MSG_ERROR([Error, libzip is missing!])
+fi
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([
Makefile
diff --git a/libconcord/libconcord.cpp b/libconcord/libconcord.cpp
index 1959ea3..be1406a 100644
--- a/libconcord/libconcord.cpp
+++ b/libconcord/libconcord.cpp
@@ -30,6 +30,7 @@
#include <stdlib.h>
#include <errno.h>
#include <zzip/lib.h>
+#include <zip.h>
#include <list>
#include <unistd.h>
#include <vector>
@@ -1036,6 +1037,82 @@ int _write_config_to_remote(lc_callback cb, void
*cb_arg, uint32_t cb_stage)
return 0;
}
+/*
+ * When MH configs are read from the remote, sometimes the remote returns extra
+ * data after the end of the config file proper. This function searches for
+ * the sequence of bytes that indicates the end of the config and returns the
+ * real length of the config that should be written out to disk.
+ */
+uint32_t _mh_get_config_len(uint8_t *in, uint32_t size)
+{
+ for (int i = 0; (i + 3) < size; i++) {
+ if (!memcmp(&in[i], MH_EOF_BYTES, 4)) {
+ return i + 4;
+ }
+ }
+ debug("Failed to find MH config EOF sequence");
+ return 0;
+}
+
+int _mh_write_config_to_file(uint8_t *in, uint32_t size, char *file_name)
+{
+ int zip_err;
+ struct zip *zip = zip_open(file_name, ZIP_CREATE | ZIP_EXCL, &zip_err);
+ if (!zip) {
+ if (zip_err == ZIP_ER_EXISTS) {
+ printf("Error: file %s already exists\n", file_name);
+ } else {
+ debug("Failed to create zip file %s", file_name);
+ }
+ return LC_ERROR_OS_FILE;
+ }
+ int index;
+
+ // Write XML
+ extern const char *mh_config_header;
+ int xml_buffer_len = strlen(mh_config_header) + 100;
+ char *xml_buffer = new char[xml_buffer_len];
+ uint16_t checksum = mh_get_checksum(in, size);
+ int xml_len = snprintf(xml_buffer, xml_buffer_len, mh_config_header,
+ size, size - 6, checksum, ri.skin);
+ if (xml_len >= xml_buffer_len) {
+ debug("Error, XML buffer length exceeded");
+ return LC_ERROR;
+ }
+ struct zip_source *xml = zip_source_buffer(zip, xml_buffer, xml_len, 0);
+ if (!xml) {
+ debug("Failed to create zip_source_buffer for XML file");
+ return LC_ERROR_OS_FILE;
+ }
+ index = zip_add(zip, "Description.xml", xml);
+ if (index == -1) {
+ debug("Error writing XML to zip file");
+ zip_source_free(xml);
+ return LC_ERROR_OS_FILE;
+ }
+
+ // Write EzHex file
+ struct zip_source *ezhex = zip_source_buffer(zip, in, size, 0);
+ if (!ezhex) {
+ debug("Failed to create zip_source_buffer for EzHex file");
+ return LC_ERROR_OS_FILE;
+ }
+ index = zip_add(zip, "Result.EzHex", ezhex);
+ if (index == -1) {
+ debug("Error writing EzHex to zip file");
+ zip_source_free(ezhex);
+ return LC_ERROR_OS_FILE;
+ }
+
+ if (zip_close(zip) != 0) {
+ debug("Error closing zip file");
+ return LC_ERROR_OS_FILE;
+ }
+
+ delete[] xml_buffer;
+ return 0;
+}
+
int write_config_to_remote(lc_callback cb, void *cb_arg)
{
return _write_config_to_remote(cb, cb_arg, LC_CB_STAGE_WRITE_CONFIG);
@@ -1044,6 +1121,17 @@ int write_config_to_remote(lc_callback cb, void *cb_arg)
int write_config_to_file(uint8_t *in, uint32_t size, char *file_name,
int binary)
{
+ // If this is an MH remote, need to find the real end of the binary
+ if (is_mh_remote()) {
+ size = _mh_get_config_len(in, size);
+ ri.config_bytes_used = size;
+ }
+
+ // If this is an MH remote, need to write out zip file with XML/binary
+ if (!binary && is_mh_remote()) {
+ return _mh_write_config_to_file(in, size, file_name);
+ }
+
binaryoutfile of;
if (of.open(file_name) != 0) {
diff --git a/libconcord/remote.h b/libconcord/remote.h
index 31bb5f1..6a4c399 100644
--- a/libconcord/remote.h
+++ b/libconcord/remote.h
@@ -35,6 +35,7 @@
which is 1 (num params) + 3 (3 parameter size bytes) + 1 (param 1)
+ 1024 (param 2) + 4 (param 3) = 1033. */
#define USBNET_MAX_PACKET_SIZE 1033
+const uint8_t MH_EOF_BYTES[] = { 0x50, 0x54, 0x59, 0x59 };
/*
* limits for IR signal learning, stop when any is reached:
@@ -151,6 +152,7 @@ void setup_ri_pointers(TRemoteInfo &ri);
void make_serial(uint8_t *ser, TRemoteInfo &ri);
int LearnIRInnerLoop(uint32_t *freq, uint32_t **ir_signal,
uint32_t *ir_signal_length, uint8_t seq);
+uint16_t mh_get_checksum(uint8_t* rd, const uint32_t len);
class CRemoteBase // Base class for all remotes
{
diff --git a/libconcord/remote_mh.cpp b/libconcord/remote_mh.cpp
index ce55ee9..5038945 100644
--- a/libconcord/remote_mh.cpp
+++ b/libconcord/remote_mh.cpp
@@ -252,6 +252,18 @@ int CRemoteMH::GetIdentity(TRemoteInfo &ri, THIDINFO &hid,
lc_callback cb,
}
debug("%s", identity.c_str());
+ if ((err = HID_WriteReport(msg_six))) {
+ debug("Failed to write to remote");
+ return LC_ERROR_WRITE;
+ }
+
+ if ((err = HID_ReadReport(rsp))) {
+ debug("Failed to read from remote");
+ return LC_ERROR_READ;
+ }
+ debug("msg_six");
+ debug_print_packet(rsp);
+
ri.fw_ver_major = strtol(find_value(identity, "fw_ver").c_str(), NULL, 10);
ri.fw_ver_minor = strtol(find_value(identity, "fw_ver").c_str()+2, NULL,
10);
@@ -271,8 +283,29 @@ int CRemoteMH::GetIdentity(TRemoteInfo &ri, THIDINFO &hid,
lc_callback cb,
cb(cb_stage, cb_count++, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
}
- ri.config_bytes_used = 0;
- ri.max_config_size = 1;
+ // Send the read config message to find the config bytes used.
+ const uint8_t msg_read_config[MH_MAX_PACKET_SIZE] =
+ { 0xFF, 0x01, 0x00, 0x03, 0x80, '/', 'c', 'f', 'g', '/',
+ 'u', 's', 'e', 'r', 'c', 'f', 'g', 0x00, 0x80, 'R', 0x00 };
+ if ((err = HID_WriteReport(msg_read_config))) {
+ debug("Failed to write to remote");
+ return LC_ERROR_WRITE;
+ }
+
+ if ((err = HID_ReadReport(rsp))) {
+ debug("Failed to read from remote");
+ return LC_ERROR_READ;
+ }
+ debug("msg_read_config");
+ debug_print_packet(rsp);
+
+ // There are four extra bytes at the end of every MH config file -
+ // we add an extra four here so there is space in ReadFlash() to add
+ // those bytes.
+ ri.config_bytes_used = (rsp[7] << 24) + (rsp[8] << 16) + (rsp[9] << 8)
+ + rsp[10] + 4;
+ debug("ri.config_bytes_used = %d", ri.config_bytes_used);
+ ri.max_config_size = (ri.flash->size << 10);
ri.valid_config = 1;
if (cb) {
@@ -295,7 +328,9 @@ int CRemoteMH::GetIdentity(TRemoteInfo &ri, THIDINFO &hid,
lc_callback cb,
}
/* reset the sequence number to 0 */
- if ((err = HID_WriteReport(msg_six))) {
+ const uint8_t msg_reset_seq[MH_MAX_PACKET_SIZE] =
+ { 0xFF, 0x07, 0x01, 0x01, 0x01, 0x06 };
+ if ((err = HID_WriteReport(msg_reset_seq))) {
debug("Failed to write to remote");
return LC_ERROR_WRITE;
}
@@ -304,17 +339,132 @@ int CRemoteMH::GetIdentity(TRemoteInfo &ri, THIDINFO
&hid, lc_callback cb,
debug("Failed to read from remote");
return LC_ERROR_READ;
}
- debug("msg_six");
+ debug("msg_reset_seq");
debug_print_packet(rsp);
return 0;
}
+// Calculates the XOR checksum for a config read from the remote.
+uint16_t mh_get_checksum(uint8_t* rd, const uint32_t len)
+{
+ // This is the "SEED" that all the configs from the website use.
+ uint16_t cksum = 0x4321;
+ // The part of the config that gets checksummed is consistently 6 bytes
+ // less than the length of the config. Since we are checksumming two
+ // bytes at a type, we stop when i == len - 7, which is the same as
+ // i + 1 == len - 6. In the case of odd lengths, we skip the last byte.
+ for (int i = 0; i < (len - 7); i += 2) {
+ uint16_t j = (rd[i+1] << 8) + rd[i];
+ cksum ^= j;
+ }
+ debug("CHECKSUM=0x%04x", cksum);
+ return cksum;
+}
+
int CRemoteMH::ReadFlash(uint32_t addr, const uint32_t len, uint8_t *rd,
unsigned int protocol, bool verify, lc_callback cb,
void *cb_arg, uint32_t cb_stage)
{
- return LC_ERROR_UNSUPP;
+ int err = 0;
+ uint32_t cb_count = 0;
+
+ const uint8_t msg_one[MH_MAX_PACKET_SIZE] =
+ { 0xFF, 0x01, 0x00, 0x03, 0x80, '/', 'c', 'f', 'g', '/',
+ 'u', 's', 'e', 'r', 'c', 'f', 'g', 0x00, 0x80, 'R', 0x00 };
+ uint8_t msg_ack[MH_MAX_PACKET_SIZE] =
+ { 0xFF, 0x04, 0x01, 0x02, 0x01, 0x00, 0x01, 0x33 };
+ const uint8_t msg_eof = { 0xFE };
+ uint8_t rsp[MH_MAX_PACKET_SIZE];
+
+ if ((err = HID_WriteReport(msg_one))) {
+ debug("Failed to write to remote");
+ return LC_ERROR_WRITE;
+ }
+
+ if ((err = HID_ReadReport(rsp))) {
+ debug("Failed to read from remote");
+ return LC_ERROR_READ;
+ }
+ debug("msg_one");
+ debug_print_packet(rsp);
+
+ if ((err = HID_WriteReport(msg_ack))) {
+ debug("Failed to write to remote");
+ return LC_ERROR_WRITE;
+ }
+
+ if ((err = HID_ReadReport(rsp))) {
+ debug("Failed to read from remote");
+ return LC_ERROR_READ;
+ }
+ debug("msg_ack");
+ debug_print_packet(rsp);
+
+ int packet_count = 0;
+ int data_read = 0;
+ uint8_t seq = 0x01;
+ uint8_t *rd_ptr = rd;
+ while(!(err = HID_ReadReport(rsp))) {
+ debug_print_packet(rsp);
+ if (rsp[0] == msg_eof) {
+ break;
+ }
+ // Ignore 1st two bits on 2nd byte for length.
+ int len = rsp[1] & 0x3F;
+ // Skip 1st two bytes, read up to packet length. "len"
+ // represents the payload length (not including the two size
+ // bytes), so we read a full "len" bytes from 2 to len+2.
+ if (rd) {
+ memcpy(rd_ptr, &rsp[2], len);
+ rd_ptr += len;
+ }
+ data_read += len;
+ packet_count++;
+ if (packet_count == 50) {
+ msg_ack[2] = get_seq(seq);
+ debug_print_packet(msg_ack);
+ if ((err = HID_WriteReport(msg_ack))) {
+ debug("Failed to write to remote");
+ return LC_ERROR_WRITE;
+ }
+
+ if ((err = HID_ReadReport(rsp))) {
+ debug("Failed to read from remote");
+ return LC_ERROR_READ;
+ }
+ debug("ack");
+ debug_print_packet(rsp);
+ packet_count = 0;
+ }
+ }
+ debug("data_read=%d", data_read);
+
+ /*
+ * There are four extra bytes at the end of every config file, but they
+ * are not present when read from the remote. Add them here.
+ */
+ if (rd) {
+ memcpy(rd_ptr, MH_EOF_BYTES, 4);
+ rd_ptr += 4;
+ }
+ data_read += 4;
+
+ /* send reset sequence message */
+ const uint8_t msg_reset_seq[MH_MAX_PACKET_SIZE] =
+ { 0xFF, 0x07, get_seq(seq), 0x01, 0x01, 0x06 };
+ if ((err = HID_WriteReport(msg_reset_seq))) {
+ debug("Failed to write to remote");
+ return LC_ERROR_WRITE;
+ }
+ if ((err = HID_ReadReport(rsp))) {
+ debug("Failed to read from remote");
+ return LC_ERROR_READ;
+ }
+ debug("msg_reset_seq");
+ debug_print_packet(rsp);
+
+ return 0;
}
int CRemoteMH::InvalidateFlash(lc_callback cb, void *cb_arg, uint32_t lc_stage)
diff --git a/libconcord/xml_headers.h b/libconcord/xml_headers.h
index aa2d663..6f79253 100644
--- a/libconcord/xml_headers.h
+++ b/libconcord/xml_headers.h
@@ -129,6 +129,7 @@ const char *config_header="\
</INFORMATION>\r\n";
+const char *mh_config_header = "<DATA><FILES><FILE NAME=\"Result.EzHex\"
SIZE=\"%i\" PATH=\"/cfg/usercfg\" VERSION=\"1\" FW_VERSION=\"9.5\"
OPERATIONTYPE=\"userconfiguration\"><CHECKSUM SEED=\"0x4321\" OFFSET=\"0x0\"
LENGTH=\"0x%04x\" EXPECTEDVALUE=\"0x%04x\"
TYPE=\"XOR\"/></FILE></FILES><INTENDED><SKIN>%i</SKIN></INTENDED><ORDER><ORDER_ELEMENT
NAME=\"Result.EzHex\" RESET=\"true\"/></ORDER></DATA>";
//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\
------------------------------------------------------------------------------
How ServiceNow helps IT people transform IT departments:
1. A cloud service to automate IT design, transition and operations
2. Dashboards that offer high-level views of enterprise services
3. A single system of record for all IT processes
http://p.sf.net/sfu/servicenow-d2d-j
_______________________________________________
concordance-devel mailing list
concordance-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/concordance-devel